Merge branch 'master' into refactor-spawning-from-vents

This commit is contained in:
dearmochi
2020-08-19 10:26:31 +02:00
committed by GitHub
243 changed files with 3735 additions and 8015 deletions
+28 -25
View File
@@ -681,7 +681,7 @@
req_access_txt = "2"
},
/obj/structure/cable{
icon_state = "1-10"
icon_state = "1-2"
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
@@ -858,18 +858,11 @@
/turf/simulated/floor/plasteel,
/area/mine/laborcamp/security)
"ce" = (
/obj/structure/cable{
icon_state = "2-4"
/obj/machinery/power/smes{
charge = 5e+006
},
/obj/structure/cable{
icon_state = "5-6"
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"cf" = (
/obj/machinery/power/terminal,
/obj/structure/cable{
icon_state = "0-8"
icon_state = "0-4"
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
@@ -880,6 +873,9 @@
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"ch" = (
/obj/structure/cable{
icon_state = "1-8"
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"ci" = (
@@ -1033,21 +1029,22 @@
/turf/simulated/floor/plasteel,
/area/mine/laborcamp)
"cz" = (
/obj/machinery/power/port_gen/pacman{
anchored = 1
},
/obj/structure/cable,
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"cA" = (
/obj/machinery/power/smes{
charge = 5e+006
/obj/machinery/power/terminal{
dir = 1
},
/obj/structure/cable{
icon_state = "0-4"
},
/obj/structure/reagent_dispensers/fueltank,
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"cA" = (
/obj/structure/cable{
icon_state = "0-9"
d1 = 4;
d2 = 8;
icon_state = "4-8";
pixel_x = 0;
tag = ""
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
@@ -1056,7 +1053,13 @@
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"cC" = (
/obj/structure/reagent_dispensers/fueltank,
/obj/machinery/power/port_gen/pacman{
anchored = 1
},
/obj/structure/cable{
d2 = 8;
icon_state = "0-8"
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"cD" = (
@@ -11202,7 +11205,7 @@ bm
aq
bB
bM
cf
ch
cA
aq
aj
@@ -11460,7 +11463,7 @@ aq
aq
aq
cg
cB
cA
aq
aj
cQ
@@ -11716,7 +11719,7 @@ aD
aD
aD
aq
ch
cB
cC
aq
aj
File diff suppressed because it is too large Load Diff
-56
View File
@@ -1,56 +0,0 @@
/datum/stack
var/list/stack = list()
var/max_elements = 0
/datum/stack/New(list/elements,max)
..()
if(elements)
stack = elements.Copy()
if(max)
max_elements = max
/datum/stack/proc/Pop()
if(is_empty())
return null
. = stack[stack.len]
stack.Cut(stack.len,0)
/datum/stack/proc/Push(element)
if(max_elements && (stack.len+1 > max_elements))
return null
stack += element
/datum/stack/proc/Top()
if(is_empty())
return null
. = stack[stack.len]
/datum/stack/proc/is_empty()
. = stack.len ? 0 : 1
//Rotate entire stack left with the leftmost looping around to the right
/datum/stack/proc/RotateLeft()
if(is_empty())
return 0
. = stack[1]
stack.Cut(1,2)
Push(.)
//Rotate entire stack to the right with the rightmost looping around to the left
/datum/stack/proc/RotateRight()
if(is_empty())
return 0
. = stack[stack.len]
stack.Cut(stack.len,0)
stack.Insert(1,.)
/datum/stack/proc/Copy()
var/datum/stack/S=new()
S.stack = stack.Copy()
S.max_elements = max_elements
return S
/datum/stack/proc/Clear()
stack.Cut()
+1
View File
@@ -7,6 +7,7 @@
#define PLANE_SPACE_PARALLAX -90
#define FLOOR_PLANE -2
#define FLOOR_OVERLAY_PLANE -1.5
#define GAME_PLANE -1
#define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals
+5
View File
@@ -4,3 +4,8 @@
#define SAY_LOG "Say"
#define EMOTE_LOG "Emote"
#define MISC_LOG "Misc"
#define DEADCHAT_LOG "Deadchat"
#define OOC_LOG "OOC"
#define LOOC_LOG "LOOC"
#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, DEADCHAT_LOG, OOC_LOG, LOOC_LOG, MISC_LOG)
+2 -60
View File
@@ -425,66 +425,8 @@
if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE)
. = TRUE
proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE)
var/roletext = be_special_type ? get_roletext(be_special_type) : null
var/list/mob/dead/observer/candidates = list()
var/time_passed = world.time
if(!Question)
Question = "Would you like to be a special role?"
for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list))
if(!G.key || !G.client)
continue
if(be_special_type)
if(!(be_special_type in G.client.prefs.be_special))
continue
if(antag_age_check)
if(!player_old_enough_antag(G.client, be_special_type))
continue
if(roletext)
if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate"))
continue
if(config.use_exp_restrictions && min_hours)
if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60)
continue
if(check_antaghud && cannotPossess(G))
continue
spawn(0)
G << 'sound/misc/notice2.ogg'//Alerting them to their consideration
if(flashwindow)
window_flash(G.client)
var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round")
if(!G?.client)
return
switch(ans)
if("Yes")
to_chat(G, "<span class='notice'>Choice registered: Yes.</span>")
if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed.
to_chat(G, "<span class='danger'>Sorry, you were too late for the consideration!</span>")
G << 'sound/machines/buzz-sigh.ogg'
return
candidates += G
if("No")
to_chat(G, "<span class='danger'>Choice registered: No.</span>")
return
if("Not This Round")
to_chat(G, "<span class='danger'>Choice registered: No.</span>")
to_chat(G, "<span class='notice'>You will no longer receive notifications for the role '[roletext]' for the rest of the round.</span>")
G.client.prefs.be_special -= be_special_type
return
else
return
sleep(poll_time)
//Check all our candidates, to make sure they didn't log off during the 30 second wait period.
for(var/mob/dead/observer/G in candidates)
if(!G.key || !G.client)
candidates.Remove(G)
return candidates
/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE)
var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud)
/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = FALSE, flashwindow = TRUE, check_antaghud = TRUE, source)
var/list/willing_ghosts = SSghost_spawns.poll_candidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud, source)
var/list/selected_ghosts = list()
if(!willing_ghosts.len)
return selected_ghosts
@@ -1,28 +1,28 @@
//////////////////////
//Heap object
//datum/heap object
//////////////////////
/Heap
/datum/heap
var/list/L
var/cmp
/Heap/New(compare)
/datum/heap/New(compare)
L = new()
cmp = compare
/Heap/proc/IsEmpty()
/datum/heap/proc/IsEmpty()
return !L.len
//Insert and place at its position a new node in the heap
/Heap/proc/Insert(atom/A)
/datum/heap/proc/Insert(atom/A)
L.Add(A)
Swim(L.len)
//removes and returns the first element of the heap
//(i.e the max or the min dependant on the comparison function)
/Heap/proc/Pop()
/datum/heap/proc/Pop()
if(!L.len)
return 0
. = L[1]
@@ -33,7 +33,7 @@
Sink(1)
//Get a node up to its right position in the heap
/Heap/proc/Swim(var/index)
/datum/heap/proc/Swim(var/index)
var/parent = round(index * 0.5)
while(parent > 0 && (call(cmp)(L[index],L[parent]) > 0))
@@ -42,7 +42,7 @@
parent = round(index * 0.5)
//Get a node down to its right position in the heap
/Heap/proc/Sink(var/index)
/datum/heap/proc/Sink(var/index)
var/g_child = GetGreaterChild(index)
while(g_child > 0 && (call(cmp)(L[index],L[g_child]) < 0))
@@ -52,7 +52,7 @@
//Returns the greater (relative to the comparison proc) of a node children
//or 0 if there's no child
/Heap/proc/GetGreaterChild(var/index)
/datum/heap/proc/GetGreaterChild(var/index)
if(index * 2 > L.len)
return 0
@@ -65,11 +65,11 @@
return index * 2
//Replaces a given node so it verify the heap condition
/Heap/proc/ReSort(atom/A)
/datum/heap/proc/ReSort(atom/A)
var/index = L.Find(A)
Swim(index)
Sink(index)
/Heap/proc/List()
/datum/heap/proc/List()
. = L.Copy()
+3
View File
@@ -693,6 +693,9 @@ proc/dd_sortedObjectList(list/incoming)
// Lazying Episode 3
#define LAZYSET(L, K, V) LAZYINITLIST(L); L[K] = V;
/// Returns whether a numerical index is within a given list's bounds. Faster than isnull(LAZYACCESS(L, I)).
#define ISINDEXSAFE(L, I) (I >= 1 && I <= length(L))
//same, but returns nothing and acts on list in place
/proc/shuffle_inplace(list/L)
if(!L)
+5
View File
@@ -2017,5 +2017,10 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
set waitfor = FALSE
return call(source, proctype)(arglist(arguments))
/proc/IsFrozen(atom/A)
if(A in GLOB.frozen_atom_list)
return TRUE
return FALSE
/// Waits at a line of code until X is true
#define UNTIL(X) while(!(X)) stoplag()
+2
View File
@@ -34,4 +34,6 @@ GLOBAL_PROTECT(IClog)
GLOBAL_LIST_EMPTY(OOClog)
GLOBAL_PROTECT(OOClog)
GLOBAL_DATUM_INIT(logging, /datum/logging, new /datum/logging())
GLOBAL_LIST_INIT(investigate_log_subjects, list("notes", "watchlist", "hrefs"))
+3 -1
View File
@@ -73,7 +73,9 @@
var/dragged = modifiers["drag"]
if(dragged && !modifiers[dragged])
return
if(IsFrozen(A) && !is_admin(usr))
to_chat(usr, "<span class='boldannounce'>Interacting with admin-frozen players is not permitted.</span>")
return
if(modifiers["middle"] && modifiers["shift"] && modifiers["ctrl"])
MiddleShiftControlClickOn(A)
return
+83 -20
View File
@@ -3,7 +3,7 @@
//PUBLIC - call these wherever you want
/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE)
/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE, timeout_override, no_anim)
/*
Proc to create or update an alert. Returns the alert if the alert is new or updated, 0 if it was thrown already
@@ -59,14 +59,16 @@
LAZYSET(alerts, category, alert) // This also creates the list if it doesn't exist
if(client && hud_used)
hud_used.reorganize_alerts()
alert.transform = matrix(32, 6, MATRIX_TRANSLATE)
animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
if(alert.timeout)
spawn(alert.timeout)
if(alert.timeout && alerts[category] == alert && world.time >= alert.timeout)
clear_alert(category)
alert.timeout = world.time + alert.timeout - world.tick_lag
if(!no_anim)
alert.transform = matrix(32, 6, MATRIX_TRANSLATE)
animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
var/timeout = timeout_override || alert.timeout
if(timeout)
addtimer(CALLBACK(alert, /obj/screen/alert/.proc/do_timeout, src, category), timeout)
alert.timeout = world.time + timeout - world.tick_lag
return alert
// Proc to clear an existing alert.
@@ -94,7 +96,6 @@
var/alerttooltipstyle = ""
var/override_alerts = FALSE //If it is overriding other alerts of the same type
/obj/screen/alert/MouseEntered(location,control,params)
openToolTip(usr, src, params, title = name, content = desc, theme = alerttooltipstyle)
@@ -102,6 +103,12 @@
/obj/screen/alert/MouseExited()
closeToolTip(usr)
/obj/screen/alert/proc/do_timeout(mob/M, category)
if(!M || !M.alerts)
return
if(timeout && M.alerts[category] == src && world.time >= timeout)
M.clear_alert(category)
//Gas alerts
/obj/screen/alert/not_enough_oxy
@@ -507,6 +514,34 @@ so as to remain in compliance with the most up-to-date laws."
timeout = 300
var/atom/target = null
var/action = NOTIFY_JUMP
var/show_time_left = FALSE // If true you need to call START_PROCESSING manually
var/image/time_left_overlay // The last image showing the time left
var/datum/candidate_poll/poll // If set, on Click() it'll register the player as a candidate
/obj/screen/alert/notify_action/process()
if(show_time_left)
var/timeleft = timeout - world.time
if(timeleft <= 0)
return PROCESS_KILL
if(time_left_overlay)
overlays -= time_left_overlay
var/obj/O = new
O.maptext = "<span style='font-family: \"Small Fonts\"; font-weight: bold; font-size: 32px; color: [(timeleft <= 10 SECONDS) ? "red" : "white"];'>[CEILING(timeleft / 10, 1)]</span>"
O.maptext_width = O.maptext_height = 128
var/matrix/M = new
M.Translate(4, 16)
O.transform = M
var/image/I = image(O)
I.layer = FLOAT_LAYER
I.plane = FLOAT_PLANE + 1
overlays += I
time_left_overlay = I
qdel(O)
..()
/obj/screen/alert/notify_action/Destroy()
target = null
@@ -515,20 +550,48 @@ so as to remain in compliance with the most up-to-date laws."
/obj/screen/alert/notify_action/Click()
if(!usr || !usr.client)
return
if(!target)
return
var/mob/dead/observer/G = usr
if(!istype(G))
return
switch(action)
if(NOTIFY_ATTACK)
target.attack_ghost(G)
if(NOTIFY_JUMP)
var/turf/T = get_turf(target)
if(T && isturf(T))
G.loc = T
if(NOTIFY_FOLLOW)
G.ManualFollow(target)
if(poll)
if(poll.sign_up(G))
// Add a small overlay to indicate we've signed up
display_signed_up()
else if(target)
switch(action)
if(NOTIFY_ATTACK)
target.attack_ghost(G)
if(NOTIFY_JUMP)
var/turf/T = get_turf(target)
if(T && isturf(T))
G.loc = T
if(NOTIFY_FOLLOW)
G.ManualFollow(target)
/obj/screen/alert/notify_action/proc/display_signed_up()
var/image/I = image('icons/mob/screen_gen.dmi', icon_state = "selector")
I.layer = FLOAT_LAYER
I.plane = FLOAT_PLANE + 2
overlays += I
/obj/screen/alert/notify_action/proc/display_stacks(stacks = 1)
if(stacks <= 1)
return
var/obj/O = new
O.maptext = "<span style='font-family: \"Small Fonts\"; font-size: 32px; color: yellow;'>[stacks]x</span>"
O.maptext_width = O.maptext_height = 128
var/matrix/M = new
M.Translate(4, 2)
O.transform = M
var/image/I = image(O)
I.layer = FLOAT_LAYER
I.plane = FLOAT_PLANE + 1
overlays += I
qdel(O)
/obj/screen/alert/notify_soulstone
name = "Soul Stone"
+1 -1
View File
@@ -81,7 +81,7 @@
user.do_attack_animation(M)
. = M.attacked_by(src, user, def_zone)
add_attack_logs(user, M, "Attacked with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL)
add_attack_logs(user, M, "Attacked with [name] ([uppertext(user.a_intent)]) ([uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL)
add_fingerprint(user)
-84
View File
@@ -1,84 +0,0 @@
#define MIDDLE_CLICK 0
#define ALT_CLICK 1
#define CTRL_CLICK 2
#define MAX_HARDSUIT_CLICK_MODE 2
/client
var/hardsuit_click_mode = MIDDLE_CLICK
/client/verb/toggle_hardsuit_mode()
set name = "Toggle Hardsuit Activation Mode"
set desc = "Switch between hardsuit activation modes."
set category = "OOC"
hardsuit_click_mode++
if(hardsuit_click_mode > MAX_HARDSUIT_CLICK_MODE)
hardsuit_click_mode = 0
switch(hardsuit_click_mode)
if(MIDDLE_CLICK)
to_chat(src, "Hardsuit activation mode set to middle-click.")
if(ALT_CLICK)
to_chat(src, "Hardsuit activation mode set to alt-click.")
if(CTRL_CLICK)
to_chat(src, "Hardsuit activation mode set to control-click.")
else
// should never get here, but just in case:
log_runtime(EXCEPTION("Bad hardsuit click mode: [hardsuit_click_mode] - expected 0 to [MAX_HARDSUIT_CLICK_MODE]"), src)
to_chat(src, "Somehow you bugged the system. Setting your hardsuit mode to middle-click.")
hardsuit_click_mode = MIDDLE_CLICK
/mob/living/MiddleClickOn(atom/A)
if(client && client.hardsuit_click_mode == MIDDLE_CLICK)
if(HardsuitClickOn(A))
return
..()
/mob/living/AltClickOn(atom/A)
if(client && client.hardsuit_click_mode == ALT_CLICK)
if(HardsuitClickOn(A))
return
..()
/mob/living/CtrlClickOn(atom/A)
if(client && client.hardsuit_click_mode == CTRL_CLICK)
if(HardsuitClickOn(A))
return
..()
/mob/living/proc/can_use_rig()
return 0
/mob/living/carbon/human/can_use_rig()
return 1
/mob/living/carbon/brain/can_use_rig()
return istype(loc, /obj/item/mmi)
/mob/living/silicon/ai/can_use_rig()
return istype(loc, /obj/item/aicard)
/mob/living/silicon/pai/can_use_rig()
return loc == card
/mob/living/proc/HardsuitClickOn(var/atom/A, var/alert_ai = 0)
if(!can_use_rig() || (next_move > world.time))
return 0
var/obj/item/rig/rig = get_rig()
if(istype(rig) && !rig.offline && rig.selected_module)
if(src != rig.wearer)
if(rig.ai_can_move_suit(src, check_user_module = 1))
message_admins("[key_name_admin(src)] is trying to force \the [key_name_admin(rig.wearer)] to use a hardsuit module.")
else
return 0
rig.selected_module.engage(A, alert_ai)
if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns
changeNext_move(CLICK_CD_MELEE)
return 1
return 0
#undef MIDDLE_CLICK
#undef ALT_CLICK
#undef CTRL_CLICK
#undef MAX_HARDSUIT_CLICK_MODE
+268
View File
@@ -0,0 +1,268 @@
SUBSYSTEM_DEF(ghost_spawns)
name = "Ghost Spawns"
init_order = INIT_ORDER_EVENTS
flags = SS_BACKGROUND
wait = 1 SECONDS
runlevels = RUNLEVEL_GAME
offline_implications = "Ghosts will no longer be able to respawn as event mobs (Blob, etc..). Shuttle call recommended."
/// List of polls currently ongoing, to be checked on next fire()
var/list/datum/candidate_poll/currently_polling
/// Whether there are active polls or not
var/polls_active = FALSE
/// Number of polls performed since the start
var/total_polls = 0
/// The poll that's closest to finishing
var/datum/candidate_poll/next_poll_to_finish
/datum/controller/subsystem/ghost_spawns/fire()
if(!polls_active)
return
if(!currently_polling) // if polls_active is TRUE then this shouldn't happen, but still..
currently_polling = list()
for(var/poll in currently_polling)
var/datum/candidate_poll/P = poll
if(P.time_left() <= 0)
polling_finished(P)
/**
* Polls for candidates with a question and a preview of the role
*
* This proc replaces /proc/pollCandidates.
* Should NEVER be used in a proc that has waitfor set to FALSE/0 (due to #define UNTIL)
* Arguments:
* * question - The question to ask to potential candidates
* * role - The role to poll for. Should be a ROLE_x enum. If set, potential candidates who aren't eligible will be ignored
* * antag_age_check - Whether to filter out potential candidates who don't have an old enough account
* * poll_time - How long to poll for in deciseconds
* * ignore_respawnability - Whether to ignore the player's respawnability
* * min_hours - The amount of hours needed for a potential candidate to be eligible
* * flash_window - Whether the poll should flash a potential candidate's game window
* * check_antaghud - Whether to filter out potential candidates who enabled AntagHUD
* * source - The atom, atom prototype, icon or mutable appearance to display as an icon in the alert
*/
/datum/controller/subsystem/ghost_spawns/proc/poll_candidates(question = "Would you like to play a special role?", role, antag_age_check = FALSE, poll_time = 30 SECONDS, ignore_respawnability = FALSE, min_hours = 0, flash_window = TRUE, check_antaghud = TRUE, source)
log_debug("Polling candidates [role ? "for [get_roletext(role)]" : "\"[question]\""] for [poll_time / 10] seconds")
// Start firing
polls_active = TRUE
total_polls++
var/datum/candidate_poll/P = new(role, question, poll_time)
LAZYADD(currently_polling, P)
// We're the poll closest to completion
if(!next_poll_to_finish || poll_time < next_poll_to_finish.time_left())
next_poll_to_finish = P
var/category = "[P.hash]_notify_action"
for(var/mob/dead/observer/M in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list))
if(!is_eligible(M))
continue
SEND_SOUND(M, 'sound/misc/notice2.ogg')
if(flash_window)
window_flash(M.client)
// If we somehow send two polls for the same mob type, but with a duration on the second one shorter than the time left on the first one,
// we need to keep the first one's timeout rather than use the shorter one
var/obj/screen/alert/notify_action/current_alert = LAZYACCESS(M.alerts, category)
var/alert_time = poll_time
var/alert_poll = P
if(current_alert && current_alert.timeout > (world.time + poll_time - world.tick_lag))
alert_time = current_alert.timeout - world.time + world.tick_lag
alert_poll = current_alert.poll
// Send them an on-screen alert
var/obj/screen/alert/notify_action/A = M.throw_alert(category, /obj/screen/alert/notify_action, timeout_override = alert_time, no_anim = TRUE)
if(!A)
continue
A.icon = ui_style2icon(M.client?.prefs.UI_style)
A.name = "Looking for candidates"
A.desc = "[question]\n\n(expires in [poll_time / 10] seconds)"
A.show_time_left = TRUE
A.poll = alert_poll
// Sign up inheritance and stacking
var/inherited_sign_up = FALSE
var/num_stack = 1
for(var/existing_poll in currently_polling)
var/datum/candidate_poll/P2 = existing_poll
if(P != P2 && P.hash == P2.hash)
// If there's already a poll for an identical mob type ongoing and the client is signed up for it, sign them up for this one
if(!inherited_sign_up && (M in P2.signed_up) && P.sign_up(M, TRUE))
A.display_signed_up()
inherited_sign_up = TRUE
// This number is used to display the number of polls the alert regroups
num_stack++
if(num_stack > 1)
A.display_stacks(num_stack)
// Image to display
var/image/I
if(source)
if(!ispath(source))
var/atom/S = source
var/old_layer = S.layer
var/old_plane = S.plane
S.layer = FLOAT_LAYER
S.plane = FLOAT_PLANE
A.overlays += S
S.layer = old_layer
S.plane = old_plane
else
I = image(source, layer = FLOAT_LAYER, dir = SOUTH)
else
// Just use a generic image
I = image('icons/effects/effects.dmi', icon_state = "static", layer = FLOAT_LAYER, dir = SOUTH)
if(I)
I.layer = FLOAT_LAYER
I.plane = FLOAT_PLANE
A.overlays += I
// Start processing it so it updates visually the timer
START_PROCESSING(SSprocessing, A)
A.process()
// Sleep until the time is up
UNTIL(P.finished)
return P.signed_up
/**
* Returns whether an observer is eligible to be an event mob
*
* Arguments:
* * M - The mob to check eligibility
* * role - The role to check eligibility for. Checks 1. the client has enabled the role 2. the account's age for this role if antag_age_check is TRUE
* * antag_age_check - Whether to check the account's age or not for the given role.
* * role_text - The role's clean text. Used for checking job bans to determine eligibility
* * min_hours - The amount of minimum hours the client needs before being eligible
* * check_antaghud - Whether to consider a client who enabled AntagHUD ineligible or not
*/
/datum/controller/subsystem/ghost_spawns/proc/is_eligible(mob/M, role, antag_age_check, role_text, min_hours, check_antaghud)
. = FALSE
if(!M.key || !M.client)
return
if(role)
if(!(role in M.client.prefs.be_special))
return
if(antag_age_check)
if(!player_old_enough_antag(M.client, role))
return
if(role_text)
if(jobban_isbanned(M, role_text) || jobban_isbanned(M, "Syndicate"))
return
if(config.use_exp_restrictions && min_hours)
if(M.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60)
return
if(check_antaghud && cannotPossess(M))
return
return TRUE
/**
* Called by the subsystem when a poll's timer runs out
*
* Can be called manually to finish a poll prematurely
* Arguments:
* * P - The poll to finish
*/
/datum/controller/subsystem/ghost_spawns/proc/polling_finished(datum/candidate_poll/P)
// Trim players who aren't eligible anymore
var/len_pre_trim = length(P.signed_up)
P.trim_candidates()
log_debug("Candidate poll [P.role ? "for [get_roletext(P.role)]" : "\"[P.question]\""] finished. [len_pre_trim] players signed up, [length(P.signed_up)] after trimming")
P.finished = TRUE
currently_polling -= P
// Determine which is the next poll closest the completion or "disable" firing if there's none
if(!length(currently_polling))
polls_active = FALSE
next_poll_to_finish = null
else if(P == next_poll_to_finish)
next_poll_to_finish = null
for(var/poll in currently_polling)
var/datum/candidate_poll/P2 = poll
if(!next_poll_to_finish || P2.time_left() < next_poll_to_finish.time_left())
next_poll_to_finish = P2
/datum/controller/subsystem/ghost_spawns/stat_entry(msg)
msg += "Active: [length(currently_polling)] | Total: [total_polls]"
if(next_poll_to_finish)
msg += " | Next: [DisplayTimeText(next_poll_to_finish.time_left())] ([length(next_poll_to_finish.signed_up)] candidates)"
..(msg)
// The datum that describes one instance of candidate polling
/datum/candidate_poll
var/role // The role the poll is for
var/question // The question asked to observers
var/duration // The duration of the poll
var/list/mob/dead/observer/signed_up // The players who signed up to this poll
var/time_started // The world.time at which the poll was created
var/finished = FALSE // Whether the polling is finished
var/hash // Used to categorize in the alerts system
/datum/candidate_poll/New(polled_role, polled_question, poll_duration)
role = polled_role
question = polled_question
duration = poll_duration
signed_up = list()
time_started = world.time
hash = copytext(md5("[question]_[role ? role : "0"]"), 1, 7)
return ..()
/**
* Attempts to sign a (controlled) mob up
*
* Will fail if the mob is already signed up or the poll's timer ran out.
* Does not check for eligibility
* Arguments:
* * M - The (controlled) mob to sign up
* * silent - Whether no messages should appear or not. If not TRUE, signing up to this poll will also sign the mob up for identical polls
*/
/datum/candidate_poll/proc/sign_up(mob/dead/observer/M, silent = FALSE)
. = FALSE
if(!istype(M) || !M.key || !M.client)
return
if(M in signed_up)
if(!silent)
to_chat(M, "<span class='warning'>You have already signed up for this!</span>")
return
if(time_left() <= 0)
if(!silent)
to_chat(M, "<span class='danger'>Sorry, you were too late for the consideration!</span>")
SEND_SOUND(M, 'sound/machines/buzz-sigh.ogg')
return
signed_up += M
if(!silent)
to_chat(M, "<span class='notice'>You have signed up for this role! A candidate will be picked randomly soon..</span>")
// Sign them up for any other polls with the same mob type
for(var/existing_poll in SSghost_spawns.currently_polling)
var/datum/candidate_poll/P = existing_poll
if(src != P && hash == P.hash && !(M in P.signed_up))
P.sign_up(M, TRUE)
return TRUE
/**
* Deletes any candidates who may have disconnected from the list
*/
/datum/candidate_poll/proc/trim_candidates()
listclearnulls(signed_up)
for(var/mob in signed_up)
var/mob/M = mob
if(!M.key || !M.client)
signed_up -= M
/**
* Returns the time left for a poll
*/
/datum/candidate_poll/proc/time_left()
return duration - (world.time - time_started)
+22 -1
View File
@@ -27,7 +27,7 @@ SUBSYSTEM_DEF(jobs)
/datum/controller/subsystem/jobs/fire()
if(!config.sql_enabled || !config.use_exp_tracking)
return
update_exp(5,0)
INVOKE_ASYNC(GLOBAL_PROC, /.proc/update_exp, 5, 0)
/datum/controller/subsystem/jobs/proc/SetupOccupations(var/list/faction = list("Station"))
occupations = list()
@@ -644,6 +644,27 @@ SUBSYSTEM_DEF(jobs)
oldjobdatum.current_positions--
newjobdatum.current_positions++
/datum/controller/subsystem/jobs/proc/notify_dept_head(jobtitle, antext)
// Used to notify the department head of jobtitle X that their employee was brigged, demoted or terminated
if(!jobtitle || !antext)
return
var/datum/job/tgt_job = GetJob(jobtitle)
if(!tgt_job)
return
if(!tgt_job.department_head[1])
return
var/boss_title = tgt_job.department_head[1]
var/obj/item/pda/target_pda
for(var/obj/item/pda/check_pda in GLOB.PDAs)
if(check_pda.ownrank == boss_title)
target_pda = check_pda
break
if(!target_pda)
return
var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger)
if(PM && PM.can_receive())
PM.notify("<b>Automated Notification: </b>\"[antext]\" (Unable to Reply)")
/datum/controller/subsystem/jobs/proc/fetch_transfer_record_html(var/centcom)
var/record_html = "<TABLE border=\"1\">"
@@ -6,17 +6,23 @@ GLOBAL_REAL(SSmentor_tickets, /datum/controller/subsystem/tickets/mentor_tickets
/datum/controller/subsystem/tickets/mentor_tickets
name = "Mentor Tickets"
offline_implications = "Mentor tickets will no longer be marked as stale. No immediate action is needed."
ticket_system_name = "Mentor Tickets"
ticket_name = "Mentor Ticket"
span_class = "mentorhelp"
other_ticket_name = "Admin"
other_ticket_permission = R_ADMIN
close_rights = R_MENTOR | R_ADMIN
offline_implications = "Mentor tickets will no longer be marked as stale. No immediate action is needed."
/datum/controller/subsystem/tickets/mentor_tickets/message_staff(var/msg)
message_mentorTicket(msg)
rights_needed = R_MENTOR | R_ADMIN | R_MOD
/datum/controller/subsystem/tickets/mentor_tickets/Initialize()
close_messages = list("<font color='red' size='3'><b>- [ticket_name] Closed -</b></font>",
"<span class='boldmessage'>Please try to be as descriptive as possible in mentor helps. Mentors do not know the full situation you're in and need more information to give you a helpful response.</span>",
"<span class='[span_class]'>Your [ticket_name] has now been closed.</span>")
return ..()
/datum/controller/subsystem/tickets/mentor_tickets/message_staff(msg)
message_mentorTicket(msg)
/datum/controller/subsystem/tickets/mentor_tickets/create_other_system_ticket(datum/ticket/T)
SStickets.newTicket(T.clientName, T.content, T.title)
+51 -10
View File
@@ -12,18 +12,23 @@
SUBSYSTEM_DEF(tickets)
name = "Admin Tickets"
var/span_class = "adminticket"
var/ticket_system_name = "Admin Tickets"
var/ticket_name = "Admin Ticket"
var/close_rights = R_ADMIN
var/list/close_messages
init_order = INIT_ORDER_TICKETS
wait = 300
priority = FIRE_PRIORITY_TICKETS
offline_implications = "Admin tickets will no longer be marked as stale. No immediate action is needed."
flags = SS_BACKGROUND
var/span_class = "adminticket"
var/ticket_system_name = "Admin Tickets"
var/ticket_name = "Admin Ticket"
var/close_rights = R_ADMIN
var/rights_needed = R_ADMIN | R_MOD
/// The name of the other ticket type to convert to
var/other_ticket_name = "Mentor"
/// Which permission to look for when seeing if there is staff available for the other ticket type
var/other_ticket_permission = R_MENTOR
var/list/close_messages
var/list/allTickets = list() //make it here because someone might ahelp before the system has initialized
var/ticketCounter = 1
@@ -114,9 +119,37 @@ SUBSYSTEM_DEF(tickets)
to_chat_safe(returnClient(N), "<span class='[span_class]'>Your [ticket_name] has now been resolved.</span>")
return TRUE
/datum/controller/subsystem/tickets/proc/convert_to_other_ticket(ticketId)
if(!check_rights(rights_needed))
return
if(alert("Are you sure to convert this ticket to an '[other_ticket_name]' ticket?",,"Yes","No") != "Yes")
return
if(!other_ticket_system_staff_check())
return
var/datum/ticket/T = allTickets[ticketId]
convert_ticket(T)
/datum/controller/subsystem/tickets/proc/other_ticket_system_staff_check()
var/list/staff = staff_countup(other_ticket_permission)
if(!staff[1])
if(alert("No active staff online to answer the ticket. Are you sure you want to convert the ticket?",, "No", "Yes") != "Yes")
return FALSE
return TRUE
/datum/controller/subsystem/tickets/proc/convert_ticket(datum/ticket/T)
T.ticketState = TICKET_CLOSED
var/client/C = usr.client
to_chat_safe(T.clientName, list("<span class='[span_class]'>[key_name_hidden(C)] has converted your ticket to a [other_ticket_name] ticket.</span>",\
"<span class='[span_class]'>Be sure to use the correct type of help next time!</span>"))
message_staff("<span class='[span_class]'>[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.</span>")
log_game("[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.")
create_other_system_ticket(T)
/datum/controller/subsystem/tickets/proc/create_other_system_ticket(datum/ticket/T)
SSmentor_tickets.newTicket(T.clientName, T.content, T.title)
/datum/controller/subsystem/tickets/proc/autoRespond(N)
if(!check_rights(R_ADMIN|R_MOD))
if(!check_rights(rights_needed))
return
var/datum/ticket/T = allTickets[N]
@@ -131,6 +164,7 @@ SUBSYSTEM_DEF(tickets)
"Already Resolved" = "The problem has been resolved already.",
"Mentorhelp" = "Please redirect your question to Mentorhelp, as they are better experienced with these types of questions.",
"Happens Again" = "Thanks, let us know if it continues to happen.",
"Github Issue Report" = "To report a bug, please go to our <a href='[config.githuburl]'>Github page</a>. Then go to 'Issues'. Then 'New Issue'. Then fill out the report form. If the report would reveal current-round information, file it after the round ends.",
"Clear Cache" = "To fix a blank screen, go to the 'Special Verbs' tab and press 'Reload UI Resources'. If that fails, clear your BYOND cache (instructions provided with 'Reload UI Resources'). If that still fails, please adminhelp again, stating you have already done the following." ,
"IC Issue" = "This is an In Character (IC) issue and will not be handled by admins. You could speak to Security, Internal Affairs, a Departmental Head, Nanotrasen Representetive, or any other relevant authority currently on station.",
"Reject" = "Reject",
@@ -157,14 +191,17 @@ SUBSYSTEM_DEF(tickets)
resolveTicket(N)
message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with:<span class='adminticketalt'> [message_key] </span>")
log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]")
if("Mentorhelp")
convert_ticket(T)
else
var/msg_sound = sound('sound/effects/adminhelp.ogg')
SEND_SOUND(returnClient(N), msg_sound)
to_chat(returnClient(N), "<span class='[span_class]'>[key_name_hidden(C)] is autoresponding with: <span/> <span class='adminticketalt'>[response_phrases[message_key]]</span>")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key]
to_chat_safe(returnClient(N), "<span class='[span_class]'>[key_name_hidden(C)] is autoresponding with: <span/> <span class='adminticketalt'>[response_phrases[message_key]]</span>")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key]
message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with:<span class='adminticketalt'> [message_key] </span>") //we want to use the short named keys for this instead of the full sentence which is why we just do message_key
T.lastStaffResponse = "Autoresponse: [message_key]"
resolveTicket(N)
log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]")
//Set ticket state with key N to closed
/datum/controller/subsystem/tickets/proc/closeTicket(N)
var/datum/ticket/T = allTickets[N]
@@ -352,7 +389,7 @@ UI STUFF
dat += "<tr><td>[T.content[i]]</td></tr>"
dat += "</table><br /><br />"
dat += "<a href='?src=[UID()];detailreopen=[T.ticketNum]'>Re-Open</a>[check_rights(R_ADMIN|R_MOD, 0) ? "<a href='?src=[UID()];autorespond=[T.ticketNum]'>Auto</a>": ""]<a href='?src=[UID()];detailresolve=[T.ticketNum]'>Resolve</a><br /><br />"
dat += "<a href='?src=[UID()];detailreopen=[T.ticketNum]'>Re-Open</a>[check_rights(rights_needed, 0) ? "<a href='?src=[UID()];autorespond=[T.ticketNum]'>Auto</a>": ""]<a href='?src=[UID()];detailresolve=[T.ticketNum]'>Resolve</a><br /><br />"
if(!T.staffAssigned)
dat += "No staff member assigned to this [ticket_name] - <a href='?src=[UID()];assignstaff=[T.ticketNum]'>Take Ticket</a><br />"
@@ -367,6 +404,7 @@ UI STUFF
dat += "<br /><br />"
dat += "<a href='?src=[UID()];detailclose=[T.ticketNum]'>Close Ticket</a>"
dat += "<a href='?src=[UID()];convert_ticket=[T.ticketNum]'>Convert Ticket</a>"
var/datum/browser/popup = new(user, "[ticket_system_name]detail", "[ticket_system_name] #[T.ticketNum]", 1000, 600)
popup.set_content(dat)
@@ -449,7 +487,6 @@ UI STUFF
if(closeTicket(indexNum))
showDetailUI(usr, indexNum)
if(href_list["detailreopen"])
var/indexNum = text2num(href_list["detailreopen"])
if(openTicket(indexNum))
@@ -469,6 +506,10 @@ UI STUFF
var/indexNum = text2num(href_list["autorespond"])
autoRespond(indexNum)
if(href_list["convert_ticket"])
var/indexNum = text2num(href_list["convert_ticket"])
convert_to_other_ticket(indexNum)
if(href_list["resolveall"])
if(ticket_system_name == "Mentor Tickets")
usr.client.resolveAllMentorTickets()
+3 -2
View File
@@ -7,7 +7,7 @@
set name = "Restart Controller"
set desc = "Restart one of the various periodic loop controllers for the game (be careful!)"
if(!holder)
if(!check_rights(R_DEBUG))
return
switch(controller)
if("Master")
@@ -26,7 +26,8 @@
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
if(!holder) return
if(!check_rights(R_DEBUG))
return
switch(controller)
if("failsafe")
debug_variables(Failsafe)
+3
View File
@@ -57,6 +57,9 @@
W.plane = initial(W.plane)
W.loc = affected_mob.loc
W.dropped(affected_mob)
if(isobj(affected_mob.loc))
var/obj/O = affected_mob.loc
O.force_eject_occupant()
var/mob/living/new_mob = new new_form(affected_mob.loc)
if(istype(new_mob))
new_mob.a_intent = "harm"
+24 -9
View File
@@ -8,10 +8,10 @@
/datum/log_record/New(_log_type, _who, _what, _target, _where, _raw_time)
log_type = _log_type
who = get_subject_text(_who)
who = get_subject_text(_who, _log_type)
what = _what
target = get_subject_text(_target)
target = get_subject_text(_target, _log_type)
if(!_where)
_where = get_turf(_who)
where = _where
@@ -19,16 +19,31 @@
_raw_time = world.time
raw_time = _raw_time
/datum/log_record/proc/get_subject_text(subject)
/datum/log_record/proc/get_subject_text(subject, log_type)
if(ismob(subject) || isclient(subject) || istype(subject, /datum/mind))
return key_name_admin(subject)
if(isatom(subject))
. = key_name_admin(subject)
if(should_log_health(log_type) && isliving(subject))
. += get_health_string(subject)
else if(isatom(subject))
var/atom/A = subject
return A.name
if(istype(subject, /datum))
. = A.name
else if(istype(subject, /datum))
var/datum/D = subject
return D.type
return subject
else
. = subject
/datum/log_record/proc/get_health_string(var/mob/living/L)
var/OX = L.getOxyLoss() > 50 ? "<b>[L.getOxyLoss()]</b>" : L.getOxyLoss()
var/TX = L.getToxLoss() > 50 ? "<b>[L.getToxLoss()]</b>" : L.getToxLoss()
var/BU = L.getFireLoss() > 50 ? "<b>[L.getFireLoss()]</b>" : L.getFireLoss()
var/BR = L.getBruteLoss() > 50 ? "<b>[L.getBruteLoss()]</b>" : L.getBruteLoss()
return " ([L.health]: <font color='deepskyblue'>[OX]</font> - <font color='green'>[TX]</font> - <font color='#FFA500'>[BU]</font> - <font color='red'>[BR]</font>)"
/datum/log_record/proc/should_log_health(log_type)
if(log_type == ATTACK_LOG || log_type == DEFENSE_LOG)
return TRUE
return FALSE
/proc/compare_log_record(datum/log_record/A, datum/log_record/B)
var/time_diff = A.raw_time - B.raw_time
+84 -17
View File
@@ -1,16 +1,23 @@
#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG)
#define UPDATE_CKEY_MOB(__ckey) var/mob/result = selected_ckeys_mobs[__ckey];\
if(!result || result.ckey != __ckey){\
result = get_mob_by_ckey(__ckey);\
selected_ckeys_mobs[__ckey] = result;\
}
/datum/log_viewer
var/time_from = 0
var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up
var/list/selected_mobs = list() // The mobs in question
var/list/selected_log_types = list() // The log types being searched for
var/list/selected_mobs = list() // The mobs in question.
var/list/selected_ckeys = list() // The ckeys selected to search for. Will show all mobs the ckey is attached to
var/list/mob/selected_ckeys_mobs = list()
var/list/selected_log_types = ALL_LOGS // The log types being searched for
var/list/log_records = list() // Found and sorted records
/datum/log_viewer/proc/clear_all()
selected_mobs.Cut()
selected_log_types.Cut()
selected_log_types = ALL_LOGS
selected_ckeys.Cut()
selected_ckeys_mobs.Cut()
time_from = initial(time_from)
time_to = initial(time_to)
log_records.Cut()
@@ -19,13 +26,17 @@
/datum/log_viewer/proc/search()
log_records.Cut() // Empty the old results
var/list/invalid_mobs = list()
var/list/ckeys = selected_ckeys.Copy()
for(var/i in selected_mobs)
var/mob/M = i
if(!M || QDELETED(M))
if(!M || QDELETED(M) || !M.last_known_ckey)
invalid_mobs |= M
continue
ckeys |= M.last_known_ckey
for(var/ckey in ckeys)
for(var/log_type in selected_log_types)
var/list/logs = M.logs[log_type]
var/list/logs = GLOB.logging.get_logs_by_type(ckey, log_type)
var/len_logs = length(logs)
if(len_logs)
var/start_index = get_earliest_log_index(logs)
@@ -91,9 +102,23 @@
return start
return 0
/datum/log_viewer/proc/add_mob(mob/user, mob/M)
/datum/log_viewer/proc/add_mobs(list/mob/mobs)
if(!mobs?.len)
return
for(var/i in mobs)
add_mob(usr, i, FALSE)
/datum/log_viewer/proc/add_ckey(mob/user, ckey)
if(!user || !user)
return
selected_ckeys |= ckey
UPDATE_CKEY_MOB(ckey)
show_ui(user)
/datum/log_viewer/proc/add_mob(mob/user, mob/M, show_the_ui = TRUE)
if(!M || !user)
return
selected_mobs |= M
show_ui(user)
@@ -103,8 +128,8 @@
var/trStyleTop = "border-top:2px solid; border-bottom:2px solid; padding-top: 5px; padding-bottom: 5px;"
var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;"
var/dat
dat += "<head><style>.adminticket{border:2px solid} td{border:1px solid grey;} th{border:1px solid grey;} span{float:left;width:150px;}</style></head>"
dat += "<div style='height:15vh'>"
dat += "<head><meta http-equiv='X-UA-Compatible' content='IE=edge'><style>.adminticket{border:2px solid} td{border:1px solid grey;} th{border:1px solid grey;} span{float:left;width:150px;}</style></head>"
dat += "<div style='min-height:100px'>"
dat += "<span>Time Search Range:</span> <a href='?src=[UID()];start_time=1'>[gameTimestamp(wtime = time_from)]</a>"
dat += " To: <a href='?src=[UID()];end_time=1'>[gameTimestamp(wtime = time_to)]</a>"
dat += "<BR>"
@@ -115,20 +140,26 @@
if(QDELETED(M))
selected_mobs -= i
continue
dat += "<a href='?src=[UID()];remove_mob=\ref[M]'>[M.name]</a>"
dat += "<a href='?src=[UID()];remove_mob=\ref[M]'>[get_display_name(M)]</a>"
dat += "<a href='?src=[UID()];add_mob=1'>Add Mob</a>"
dat += "<a href='?src=[UID()];clear_mobs=1'>Clear All Mobs</a>"
dat += "<BR>"
dat += "<span>Ckeys being used:</span>"
for(var/ckey in selected_ckeys)
dat += "<a href='?src=[UID()];remove_ckey=[ckey]'>[get_ckey_name(ckey)]</a>"
dat += "<a href='?src=[UID()];add_ckey=1'>Add ckey</a>"
dat += "<a href='?src=[UID()];clear_ckeys=1'>Clear All ckeys</a>"
dat += "<BR>"
dat += "<span>Log Types:</span>"
for(var/i in all_log_types)
var/log_type = i
for(var/log_type in all_log_types)
var/enabled = (log_type in selected_log_types)
var/text
var/style
if(enabled)
text = "<b>[log_type]</b>"
style = "background: [get_logtype_color(i)]"
style = "background: [get_logtype_color(log_type)]"
else
text = log_type
@@ -142,9 +173,9 @@
// Search results
var/tdStyleTime = "width:80px; text-align:center;"
var/tdStyleType = "width:80px; text-align:center;"
var/tdStyleWho = "width:300px; text-align:center;"
var/tdStyleWho = "width:400px; text-align:center;"
var/tdStyleWhere = "width:150px; text-align:center;"
dat += "<div style='overflow-y: auto; max-height:76vh;'>"
dat += "<div style='overflow-y: auto; max-height:calc(100vh - 150px);'>"
dat += "<table style='width:100%; border: 1px solid;'>"
dat += "<tr style='[trStyleTop]'><th style='[tdStyleTime]'>When</th><th style='[tdStyleType]'>Type</th><th style='[tdStyleWho]'>Who</th><th>What</th><th>Target</th><th style='[tdStyleWhere]'>Where</th></tr>"
for(var/i in log_records)
@@ -158,7 +189,7 @@
dat += "</table>"
dat += "</div>"
var/datum/browser/popup = new(user, "Log viewer", "Log viewer", 1400, 600)
var/datum/browser/popup = new(user, "Log Viewer", "Log Viewer", 1500, 600)
popup.set_content(dat)
popup.open()
@@ -198,17 +229,31 @@
selected_mobs.Cut()
show_ui(usr)
return
if(href_list["clear_ckeys"])
selected_ckeys.Cut()
selected_ckeys_mobs.Cut()
show_ui(usr)
return
if(href_list["add_mob"])
var/list/mobs = getpois(TRUE, TRUE)
var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs)
A.on_close(CALLBACK(src, .proc/add_mob, usr))
return
if(href_list["add_ckey"])
var/list/ckeys = GLOB.logging.get_ckeys_logged()
var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a ckey: ", ckeys)
A.on_close(CALLBACK(src, .proc/add_ckey, usr))
return
if(href_list["remove_mob"])
var/mob/M = locate(href_list["remove_mob"])
if(M)
selected_mobs -= M
show_ui(usr)
return
if(href_list["remove_ckey"])
selected_ckeys -= href_list["remove_ckey"]
show_ui(usr)
return
if(href_list["toggle_log_type"])
var/log_type = href_list["toggle_log_type"]
if(log_type in selected_log_types)
@@ -232,4 +277,26 @@
return "deepskyblue"
if(MISC_LOG)
return "gray"
if(DEADCHAT_LOG)
return "#cc00c6"
if(OOC_LOG)
return "#002eb8"
if(LOOC_LOG)
return "#6699CC"
return "slategray"
/datum/log_viewer/proc/get_display_name(mob/M)
var/name = M.name
if(M.name != M.real_name)
name = "[name] ([M.real_name])"
if(isobserver(M))
name = "[name] (DEAD)"
return "\[[M.last_known_ckey]\] [name]"
/datum/log_viewer/proc/get_ckey_name(ckey)
UPDATE_CKEY_MOB(ckey)
var/mob/M = selected_ckeys_mobs[ckey]
return get_display_name(M)
#undef UPDATE_CKEY_MOB
+47
View File
@@ -0,0 +1,47 @@
/datum/logging
var/list/datum/log_record/logs = list() // Assoc list of assoc lists (ckey, (log_type, list/logs))
/datum/logging/proc/add_log(ckey, datum/log_record/log)
if(!ckey)
log_debug("GLOB.logging.add_log called with an invalid ckey")
return
if(!logs[ckey])
logs[ckey] = list()
var/list/log_types_list = logs[ckey]
if(!log_types_list[log.log_type])
log_types_list[log.log_type] = list()
var/list/datum/log_record/log_records = log_types_list[log.log_type]
log_records.Add(log)
/datum/logging/proc/get_ckeys_logged()
var/list/ckeys = list()
for(var/ckey in logs)
ckeys.Add(ckey)
return ckeys
/* Returns the logs of a given ckey and log_type
* If no logs exist it will return an empty list
*/
/datum/logging/proc/get_logs_by_type(ckey, log_type)
if(!ckey)
log_debug("GLOB.logging.get_logs_by_type called with an invalid ckey")
return
if(!log_type || !(log_type in ALL_LOGS))
log_debug("GLOB.logging.get_logs_by_type called with an invalid log_type '[log_type]'")
return
var/list/log_types_list = logs[ckey]
// Check if logs exist for the ckey
if(!length(log_types_list))
return list()
var/list/datum/log_record/log_records = log_types_list[log_type]
// Check if logs exist for this type
if(!log_records)
return list()
return log_records
-8
View File
@@ -100,14 +100,6 @@
current.mind = null
leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it
for(var/log_type in current.logs) // Copy the old logs
var/list/logs = current.logs[log_type]
if(new_character.logs[log_type])
new_character.logs[log_type] += logs.Copy() // Append the old ones
new_character.logs[log_type] = sortTim(new_character.logs[log_type], /proc/compare_log_record) // Sort them on time
else
new_character.logs[log_type] = logs.Copy() // Just copy them
SSnanoui.user_transferred(current, new_character)
if(new_character.mind) //remove any mind currently in our new body's mind variable
+5
View File
@@ -444,6 +444,11 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
return
// Normally, AoE spells will generate an attack log for every turf they loop over, while searching for targets.
// With this override, all /aoe_turf type spells will only generate 1 log, saying that the user has cast the spell.
/obj/effect/proc_holder/spell/aoe_turf/perform(list/targets, recharge, mob/user, make_attack_logs)
add_attack_logs(user, null, "Cast the AoE spell [name]", ATKLOG_ALL)
return ..(targets, recharge, user, FALSE)
/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B)
//Checks for obstacles from A to B
-5
View File
@@ -13,11 +13,6 @@
action_icon_state = "knock"
sound = 'sound/magic/knock.ogg'
// Knock doesn't need to generate an attack log for every turf, set `make_attack_logs` to FALSE and just create a custom one.
/obj/effect/proc_holder/spell/aoe_turf/knock/perform(list/targets, recharge, mob/user)
add_attack_logs(user, user, "cast the spell [name]", ATKLOG_ALL)
return ..(targets, recharge, user, make_attack_logs = FALSE)
/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr)
for(var/turf/T in targets)
for(var/obj/machinery/door/door in T.contents)
+1 -1
View File
@@ -194,7 +194,7 @@ GLOBAL_LIST_INIT(wireColours, list("red", "blue", "green", "black", "orange", "b
return 1
/datum/wires/CanUseTopic(mob/user, datum/topic_state/state)
if(!CanUse(user))
if(!holder || !CanUse(user))
return STATUS_CLOSE
return ..()
+11 -11
View File
@@ -29,15 +29,15 @@ Actual Adjacent procs :
//////////////////////
//A* nodes variables
/PathNode
/datum/pathnode
var/turf/source //turf associated with the PathNode
var/PathNode/prevNode //link to the parent PathNode
var/datum/pathnode/prevNode //link to the parent PathNode
var/f //A* Node weight (f = g + h)
var/g //A* movement cost variable
var/h //A* heuristic variable
var/nt //count the number of Nodes traversed
/PathNode/New(s,p,pg,ph,pnt)
/datum/pathnode/New(s,p,pg,ph,pnt)
source = s
prevNode = p
g = pg
@@ -46,7 +46,7 @@ Actual Adjacent procs :
source.PNode = src
nt = pnt
/PathNode/proc/calc_f()
/datum/pathnode/proc/calc_f()
f = g + h
//////////////////////
@@ -54,11 +54,11 @@ Actual Adjacent procs :
//////////////////////
//the weighting function, used in the A* algorithm
/proc/PathWeightCompare(PathNode/a, PathNode/b)
/proc/PathWeightCompare(datum/pathnode/a, datum/pathnode/b)
return a.f - b.f
//reversed so that the Heap is a MinHeap rather than a MaxHeap
/proc/HeapPathWeightCompare(PathNode/a, PathNode/b)
/proc/HeapPathWeightCompare(datum/pathnode/a, datum/pathnode/b)
return b.f - a.f
//wrapper that returns an empty list if A* failed to find a path
@@ -82,13 +82,13 @@ Actual Adjacent procs :
return 0
maxnodedepth = maxnodes //no need to consider path longer than maxnodes
var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list
var/datum/heap/open = new /datum/heap(/proc/HeapPathWeightCompare) //the open list
var/list/closed = new() //the closed list
var/list/path = null //the returned path, if any
var/PathNode/cur //current processed turf
var/datum/pathnode/cur //current processed turf
//initialization
open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0))
open.Insert(new /datum/pathnode(start,null,0,call(start,dist)(end),0))
//then run the main loop
while(!open.IsEmpty() && !path)
@@ -125,7 +125,7 @@ Actual Adjacent procs :
var/newg = cur.g + call(cur.source,dist)(T)
if(!T.PNode) //is not already in open list, so add it
open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1))
open.Insert(new /datum/pathnode(T,cur,newg,call(T,dist)(end),cur.nt+1))
else //is already in open list, check if it's a better way from the current turf
if(newg < T.PNode.g)
T.PNode.prevNode = cur
@@ -137,7 +137,7 @@ Actual Adjacent procs :
}
//cleaning after us
for(var/PathNode/PN in open.L)
for(var/datum/pathnode/PN in open.L)
PN.source.PNode = null
for(var/turf/T in closed)
T.PNode = null
+10 -4
View File
@@ -61,7 +61,7 @@
/atom/movable/proc/get_cell()
return
/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
/atom/movable/proc/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
if(QDELETED(AM))
return FALSE
if(!(AM.can_be_pulled(src, state, force)))
@@ -87,7 +87,7 @@
if(ismob(AM))
var/mob/M = AM
add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL)
if(!supress_message)
if(show_message)
visible_message("<span class='warning'>[src] has grabbed [M] passively!</span>")
return TRUE
@@ -120,12 +120,18 @@
if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move.
pulledby.stop_pulling()
/atom/movable/proc/can_be_pulled(user, grab_state, force)
/atom/movable/proc/can_be_pulled(user, grab_state, force, show_message = FALSE)
if(src == user || !isturf(loc))
return FALSE
if(anchored || throwing)
if(anchored || move_resist == INFINITY)
if(show_message)
to_chat(user, "<span class='warning'>[src] appears to be anchored to the ground!</span>")
return FALSE
if(throwing)
return FALSE
if(force < (move_resist * MOVE_FORCE_PULL_RATIO))
if(show_message)
to_chat(user, "<span class='warning'>[src] is too heavy to pull!</span>")
return FALSE
return TRUE
+3
View File
@@ -282,6 +282,9 @@
occupant = null
icon_state = "scanner_open"
/obj/machinery/dna_scannernew/force_eject_occupant()
go_out(null, TRUE)
/obj/machinery/dna_scannernew/ex_act(severity)
if(occupant)
occupant.ex_act(severity)
+10 -2
View File
@@ -104,10 +104,18 @@
var/mob/C = null
var/list/candidates = list()
if(!new_overmind)
// Create icon
var/mutable_appearance/MA = new
var/mutable_appearance/MA1 = new(icon, "blob")
MA1.color = overmind ? overmind.blob_reagent_datum.color : color
MA.overlays += MA1
var/mutable_appearance/MA2 = new(icon, "blob_core_overlay")
MA.overlays += MA2
// sendit
if(is_offspring)
candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1)
candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob offspring?", ROLE_BLOB, TRUE, source = MA)
else
candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1)
candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob?", ROLE_BLOB, TRUE, source = MA)
if(length(candidates))
C = pick(candidates)
+1 -1
View File
@@ -237,7 +237,7 @@
blobber.AIStatus = AI_OFF
blobber.LoseTarget()
spawn()
var/list/candidates = pollCandidates("Do you want to play as a blobbernaut?", ROLE_BLOB, 1, 100)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blobbernaut?", ROLE_BLOB, TRUE, 10 SECONDS, source = blobber)
if(candidates.len)
var/mob/C = pick(candidates)
if(C)
+4 -2
View File
@@ -20,8 +20,8 @@ GLOBAL_LIST_EMPTY(all_cults)
var/mob/living/carbon/human/H = mind.current
if(ismindshielded(H)) //mindshield protects against conversions unless removed
return FALSE
// if(mind.offstation_role) cant convert offstation roles such as ghost spawns
// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew
if(mind.offstation_role)
return FALSE
if(issilicon(mind.current))
return FALSE //can't convert machines, that's ratvar's thing
if(isguardian(mind.current))
@@ -231,6 +231,8 @@ GLOBAL_LIST_EMPTY(all_cults)
/datum/game_mode/cult/proc/get_unconvertables()
var/list/ucs = list()
for(var/mob/living/carbon/human/player in GLOB.player_list)
if(player.mind && player.mind.offstation_role)
continue
if(!is_convertable_to_cult(player.mind))
ucs += player.mind
return ucs
+2 -2
View File
@@ -424,9 +424,9 @@ proc/display_roundstart_logout_report()
return nukecode
/datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type)
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [role_type]?", role_type, FALSE, 10 SECONDS)
var/mob/dead/observer/theghost = null
if(candidates.len)
if(length(candidates))
theghost = pick(candidates)
to_chat(M, "<span class='userdanger'>Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!</span>")
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.")
@@ -246,7 +246,7 @@
src.verbs -= /mob/living/proc/guardian_reset
for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.mob_list)
if(G.summoner == src)
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, 0, 100)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = G)
var/mob/dead/observer/new_stand = null
if(candidates.len)
new_stand = pick(candidates)
@@ -312,7 +312,7 @@
used = FALSE
return
to_chat(user, "[use_message]")
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, 0, 100)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = /mob/living/simple_animal/hostile/guardian)
var/mob/dead/observer/theghost = null
if(candidates.len)
@@ -1,7 +1,7 @@
/obj/item/projectile/guardian
name = "crystal spray"
icon_state = "guardian"
damage = 5
damage = 25
damage_type = BRUTE
armour_penetration = 100
@@ -11,7 +11,7 @@
melee_damage_upper = 10
damage_transfer = 0.9
projectiletype = /obj/item/projectile/guardian
ranged_cooldown_time = 1 //fast!
ranged_cooldown_time = 5 //fast!
projectilesound = 'sound/effects/hit_on_shattered_glass.ogg'
ranged = 1
range = 13
@@ -3,7 +3,7 @@
/datum/event/spawn_morph/proc/get_morph()
spawn()
var/list/candidates = pollCandidates("Do you want to play as a morph?", ROLE_MORPH, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a morph?", ROLE_MORPH, TRUE, source = /mob/living/simple_animal/hostile/morph)
if(!candidates.len)
key_of_morph = null
return kill()
@@ -141,7 +141,7 @@
giveObjectivesandGoals()
giveSpells()
else
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a revenant?", poll_time = 15 SECONDS)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", poll_time = 15 SECONDS, source = /mob/living/simple_animal/revenant)
var/mob/dead/observer/theghost = null
if(candidates.len)
theghost = pick(candidates)
@@ -397,7 +397,7 @@
spawn()
if(!key_of_revenant)
message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...")
var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant)
if(!candidates.len)
qdel(R)
message_admins("No candidates were found for the new revenant. Oh well!")
@@ -13,7 +13,7 @@
return
spawn()
var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant)
if(!candidates.len)
key_of_revenant = null
return kill()
@@ -157,7 +157,7 @@
/mob/living/simple_animal/slaughter/cult/New()
..()
spawn(5)
var/list/demon_candidates = pollCandidates("Do you want to play as a slaughter demon?", ROLE_DEMON, 1, 100)
var/list/demon_candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, 10 SECONDS, source = /mob/living/simple_animal/slaughter/cult)
if(!demon_candidates.len)
visible_message("<span class='warning'>[src] disappears in a flash of red light!</span>")
qdel(src)
+1 -2
View File
@@ -53,7 +53,6 @@ proc/issyndicate(mob/living/M as mob)
for(var/datum/mind/synd_mind in syndicates)
synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs.
synd_mind.special_role = SPECIAL_ROLE_NUKEOPS
synd_mind.offstation_role = TRUE
return 1
@@ -113,7 +112,7 @@ proc/issyndicate(mob/living/M as mob)
if(spawnpos > synd_spawn.len)
spawnpos = 2
synd_mind.current.loc = synd_spawn[spawnpos]
synd_mind.offstation_role = TRUE
forge_syndicate_objectives(synd_mind)
create_syndicate(synd_mind)
greet_syndicate(synd_mind)
+1 -1
View File
@@ -74,7 +74,7 @@ Made by Xhuis
required_enemies = 2
recommended_enemies = 2
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
/datum/game_mode/shadowling/announce()
to_chat(world, "<b>The current game mode is - Shadowling!</b>")
@@ -145,7 +145,7 @@
/obj/effect/proc_holder/spell/vampire/self/rejuvenate
name = "Rejuvenate"
desc= "Flush your system with spare blood to remove any incapacitating effects."
desc= "Use reserve blood to enliven your body, removing any incapacitating effects."
action_icon_state = "vampire_rejuvinate"
charge_max = 200
stat_allowed = 1
@@ -158,7 +158,7 @@
user.SetParalysis(0)
user.SetSleeping(0)
U.adjustStaminaLoss(-75)
to_chat(user, "<span class='notice'>You flush your system with clean blood and remove any incapacitating effects.</span>")
to_chat(user, "<span class='notice'>You instill your body with clean blood and remove any incapacitating effects.</span>")
spawn(1)
if(usr.mind.vampire.get_ability(/datum/vampire_passive/regen))
for(var/i = 1 to 5)
+4 -2
View File
@@ -50,7 +50,8 @@
to_chat(H, "You already used this contract!")
return
used = 1
var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, 1)
var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, TRUE, source = source)
if(candidates.len)
var/mob/C = pick(candidates)
new /obj/effect/particle_effect/smoke(H.loc)
@@ -307,7 +308,8 @@ GLOBAL_LIST_EMPTY(multiverse)
if(M.assigned == assigned)
M.cooldown = cooldown
var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, 1, 100)
var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, TRUE, 10 SECONDS, source = source)
if(candidates.len)
var/mob/C = pick(candidates)
spawn_copy(C.client, get_turf(user.loc), user)
+2 -1
View File
@@ -118,7 +118,8 @@
return FALSE
making_mage = TRUE
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS)
var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS, source = source)
var/mob/dead/observer/harry = null
message_admins("SWF is still pissed, sending another wizard - [max_mages - mages_made] left.")
+1 -1
View File
@@ -363,7 +363,7 @@
break
if(!chosen_ghost) //Failing that, we grab a ghost
var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 100)
var/list/consenting_candidates = SSghost_spawns.poll_candidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/shade)
if(consenting_candidates.len)
chosen_ghost = pick(consenting_candidates)
if(!T)
+10 -12
View File
@@ -224,19 +224,17 @@ GLOBAL_LIST_INIT(role_playtime_requirements, list(
else
return "none"
/proc/update_exp(var/mins, var/ann = 0)
if(!establish_db_connection())
return -1
spawn(0)
for(var/client/L in GLOB.clients)
if(L.inactivity >= (10 MINUTES))
continue
spawn(0)
L.update_exp_client(mins, ann)
sleep(10)
/proc/update_exp(mins = 0, ann = 0)
if(!GLOB.dbcon.IsConnected())
return
for(var/client/L in GLOB.clients)
if(L.inactivity >= (10 MINUTES))
continue
L.update_exp_client(mins, ann)
CHECK_TICK
/client/proc/update_exp_client(var/minutes, var/announce_changes = 0)
if(!src ||!ckey)
/client/proc/update_exp_client(minutes = 0, announce_changes = 0)
if(!src || !ckey || !GLOB.dbcon.IsConnected())
return
var/DBQuery/exp_read = GLOB.dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'")
if(!exp_read.Execute())
+3
View File
@@ -411,6 +411,9 @@
for(var/atom/movable/A in contents - component_parts - list(beaker))
A.forceMove(loc)
/obj/machinery/sleeper/force_eject_occupant()
go_out()
/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount)
if(!(chemical in possible_chems))
to_chat(user, "<span class='notice'>The sleeper does not offer that chemical!</span>")
+3
View File
@@ -172,6 +172,9 @@
for(var/atom/movable/A in contents - component_parts)
A.forceMove(loc)
/obj/machinery/bodyscanner/force_eject_occupant()
go_out()
/obj/machinery/bodyscanner/ex_act(severity)
if(occupant)
occupant.ex_act(severity)
+24 -10
View File
@@ -390,7 +390,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(!job_in_department(SSjobs.GetJob(t1)))
return 0
if(t1 == "Custom")
var/temp_t = sanitize(copytext(input("Enter a custom job assignment.","Assignment"),1,MAX_MESSAGE_LEN))
var/temp_t = sanitize(reject_bad_name(copytext(input("Enter a custom job assignment.", "Assignment"), 1, MAX_MESSAGE_LEN), TRUE))
//let custom jobs function as an impromptu alt title, mainly for sechuds
if(temp_t && modify)
SSjobs.log_job_transfer(modify.registered_name, modify.getRankAndAssignment(), temp_t, scan.registered_name)
@@ -419,7 +419,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
message_admins("[key_name_admin(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".")
SSjobs.log_job_transfer(modify.registered_name, jobnamedata, t1, scan.registered_name)
SSjobs.slot_job_transfer(modify.rank, t1)
if(modify.owner_uid)
SSjobs.slot_job_transfer(modify.rank, t1)
var/mob/living/carbon/human/H = modify.getPlayer()
if(istype(H))
@@ -436,7 +437,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(is_authenticated(usr) && !target_dept)
var/t2 = modify
if((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf)))
var/temp_name = reject_bad_name(href_list["reg"])
var/temp_name = reject_bad_name(href_list["reg"], TRUE)
if(temp_name)
modify.registered_name = temp_name
else
@@ -465,6 +466,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(is_authenticated(usr) && !target_dept)
var/delcount = SSjobs.delete_log_records(scan.registered_name, TRUE)
if(delcount)
message_admins("[key_name_admin(usr)] has wiped all ID computer logs.")
usr.create_log(MISC_LOG, "wiped all ID computer logs.")
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
SSnanoui.update_uis(src)
@@ -504,9 +507,16 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if("terminate")
if(is_authenticated(usr) && !target_dept)
var/jobnamedata = modify.getRankAndAssignment()
log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".")
message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".")
var/reason = sanitize(copytext(input("Enter legal reason for termination. Enter nothing to cancel.", "Employment Termination"), 1, MAX_MESSAGE_LEN))
if(!reason || !is_authenticated(usr) || !modify)
return FALSE
var/m_ckey = modify.getPlayerCkey()
var/m_ckey_text = m_ckey ? "([m_ckey])" : "(no ckey)"
log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".")
message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".")
usr.create_log(MISC_LOG, "terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\"")
SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Terminated", scan.registered_name)
SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\" for \"[reason]\".")
modify.assignment = "Terminated"
modify.access = list()
@@ -518,16 +528,20 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(!job_in_department(SSjobs.GetJob(modify.rank), FALSE))
visible_message("<span class='notice'>[src]: Heads may only demote members of their own department.</span>")
return 0
var/reason = sanitize(copytext(input("Enter legal reason for demotion. Enter nothing to cancel.","Legal Demotion"),1,MAX_MESSAGE_LEN))
if(!reason || !is_authenticated(usr) || !modify)
return 0
var/list/access = list()
var/datum/job/jobdatum = new /datum/job/civilian
access = jobdatum.get_access()
var/jobnamedata = modify.getRankAndAssignment()
log_game("[key_name(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Demoted)\".")
message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Demoted)\".")
var/m_ckey = modify.getPlayerCkey()
var/m_ckey_text = m_ckey ? "([m_ckey])" : "(no ckey)"
log_game("[key_name(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" [m_ckey_text] to \"Civilian (Demoted)\" for: \"[reason]\".")
message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" [m_ckey_text] to \"Civilian (Demoted)\" for: \"[reason]\".")
usr.create_log(MISC_LOG, "demoted \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\"")
SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Demoted", scan.registered_name)
SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" for \"[reason]\".")
modify.access = access
modify.rank = "Civilian"
modify.assignment = "Demoted"
@@ -298,10 +298,6 @@
else
to_chat(usr, "<span class='danger'>Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.</span>")
if("ToggleATC")
GLOB.atc.squelched = !GLOB.atc.squelched
to_chat(usr, "<span class='notice'>ATC traffic is now: [GLOB.atc.squelched ? "Disabled" : "Enabled"].</span>")
SSnanoui.update_uis(src)
return 1
@@ -395,8 +391,6 @@
data["shuttle"] = shuttle
data["atcSquelched"] = GLOB.atc.squelched
return data
+3
View File
@@ -443,6 +443,9 @@
for(var/atom/movable/A in contents - component_parts - list(beaker))
A.forceMove(get_step(loc, SOUTH))
/obj/machinery/atmospherics/unary/cryo_cell/force_eject_occupant()
go_out()
/// Called when either the occupant is dead and the AUTO_EJECT_DEAD flag is present, OR the occupant is alive, has no external damage, and the AUTO_EJECT_HEALTHY flag is present.
/obj/machinery/atmospherics/unary/cryo_cell/proc/auto_eject(eject_flag)
on = FALSE
+4
View File
@@ -769,9 +769,13 @@
return ..()
/proc/cryo_ssd(var/mob/living/carbon/person_to_cryo)
if(istype(person_to_cryo.loc, /obj/machinery/cryopod))
return 0
if(isobj(person_to_cryo.loc))
var/obj/O = person_to_cryo.loc
O.force_eject_occupant()
var/list/free_cryopods = list()
for(var/obj/machinery/cryopod/P in GLOB.machines)
if(!P.occupant && istype(get_area(P), /area/crew_quarters/sleep))
+2 -27
View File
@@ -86,8 +86,8 @@
Arresting Officer: [usr.name].[R ? "" : " Detainee record not found, manual record update required."]"
Radio.autosay(announcetext, name, "Security", list(z))
if(prisoner_trank != "unknown")
notify_dept_head(prisoner_trank, announcetext)
if(prisoner_trank != "unknown" && prisoner_trank != "Civilian")
SSjobs.notify_dept_head(prisoner_trank, announcetext)
if(R)
prisoner = R
@@ -104,31 +104,6 @@
update_all_mob_security_hud()
return 1
/obj/machinery/door_timer/proc/notify_dept_head(jobtitle, antext)
if(!jobtitle || !antext)
return
if(jobtitle == "Civilian")
// Don't notify the HoP about greytiding civilians
return
var/datum/job/brigged_job = SSjobs.GetJob(jobtitle)
if(!brigged_job)
return
if(!brigged_job.department_head[1])
return
var/boss_title = brigged_job.department_head[1]
var/obj/item/pda/target_pda
for(var/obj/item/pda/check_pda in GLOB.PDAs)
if(check_pda.ownrank == boss_title)
target_pda = check_pda
if(!target_pda)
return
var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger)
if(PM && PM.can_receive())
PM.notify("<b>Message from Brig Timer (Automated), </b>\"[antext]\" (Unable to Reply)")
/obj/machinery/door_timer/Initialize()
..()
+75 -56
View File
@@ -1,15 +1,17 @@
#define RECHARGER_POWER_USAGE_GUN 250
#define RECHARGER_POWER_USAGE_MISC 200
/obj/machinery/recharger
name = "recharger"
icon = 'icons/obj/stationobjs.dmi'
icon_state = "recharger0"
desc = "A charging dock for energy based weaponry."
anchored = 1
anchored = TRUE
use_power = IDLE_POWER_USE
idle_power_usage = 4
active_power_usage = 200
pass_flags = PASSTABLE
var/obj/item/charging = null
var/using_power = FALSE
var/list/allowed_devices = list(/obj/item/gun/energy, /obj/item/melee/baton, /obj/item/modular_computer, /obj/item/rcs, /obj/item/bodyanalyzer)
var/icon_state_off = "rechargeroff"
var/icon_state_charged = "recharger2"
@@ -17,6 +19,9 @@
var/icon_state_idle = "recharger0"
var/recharge_coeff = 1
var/obj/item/charging = null // The item that is being charged
var/using_power = FALSE // Whether the recharger is actually transferring power or not, used for icon
/obj/machinery/recharger/New()
..()
component_parts = list()
@@ -34,32 +39,32 @@
if(allowed)
if(anchored)
if(charging)
return 1
return TRUE
//Checks to make sure he's not in space doing it, and that the area got proper power.
var/area/a = get_area(src)
if(!isarea(a) || a.power_equip == 0)
if(!isarea(a) || !a.power_equip)
to_chat(user, "<span class='notice'>[src] blinks red as you try to insert [G].</span>")
return 1
return TRUE
if(istype(G, /obj/item/gun/energy))
var/obj/item/gun/energy/E = G
if(!E.can_charge)
to_chat(user, "<span class='notice'>Your gun has no external power connector.</span>")
return 1
return TRUE
if(!user.drop_item())
return 1
return TRUE
G.forceMove(src)
charging = G
use_power = ACTIVE_POWER_USE
using_power = check_cell_needs_recharging(get_cell_from(G))
update_icon()
else
to_chat(user, "<span class='notice'>[src] isn't connected to anything!</span>")
return 1
return TRUE
return ..()
/obj/machinery/recharger/crowbar_act(mob/user, obj/item/I)
if(panel_open && !charging && default_deconstruction_crowbar(user, I))
return TRUE
@@ -106,57 +111,15 @@
if(stat & (NOPOWER|BROKEN) || !anchored)
return
using_power = FALSE
if(charging)
if(istype(charging, /obj/item/gun/energy))
var/obj/item/gun/energy/E = charging
if(E.cell.charge < E.cell.maxcharge)
E.cell.give(E.cell.chargerate * recharge_coeff)
E.on_recharge()
use_power(250)
using_power = TRUE
if(istype(charging, /obj/item/melee/baton))
var/obj/item/melee/baton/B = charging
if(B.cell)
if(B.cell.give(B.cell.chargerate))
use_power(200)
using_power = TRUE
if(istype(charging, /obj/item/modular_computer))
var/obj/item/modular_computer/C = charging
var/obj/item/computer_hardware/battery/battery_module = C.all_components[MC_CELL]
if(battery_module)
var/obj/item/computer_hardware/battery/B = battery_module
if(B.battery)
if(B.battery.charge < B.battery.maxcharge)
B.battery.give(B.battery.chargerate)
use_power(200)
using_power = TRUE
if(istype(charging, /obj/item/rcs))
var/obj/item/rcs/R = charging
if(R.rcell)
if(R.rcell.give(R.rcell.chargerate))
use_power(200)
using_power = TRUE
if(istype(charging, /obj/item/bodyanalyzer))
var/obj/item/bodyanalyzer/B = charging
if(B.cell)
if(B.cell.give(B.cell.chargerate))
use_power(200)
using_power = TRUE
update_icon(using_power)
using_power = try_recharging_if_possible()
update_icon()
/obj/machinery/recharger/emp_act(severity)
if(stat & (NOPOWER|BROKEN) || !anchored)
..(severity)
return
if(istype(charging, /obj/item/gun/energy))
if(istype(charging, /obj/item/gun/energy))
var/obj/item/gun/energy/E = charging
if(E.cell)
E.cell.emp_act(severity)
@@ -167,7 +130,11 @@
B.cell.charge = 0
..(severity)
/obj/machinery/recharger/update_icon(using_power = FALSE) //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier.
/obj/machinery/recharger/power_change()
..()
update_icon()
/obj/machinery/recharger/update_icon() //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier.
if(stat & (NOPOWER|BROKEN) || !anchored)
icon_state = icon_state_off
return
@@ -179,6 +146,55 @@
return
icon_state = icon_state_idle
/obj/machinery/recharger/proc/get_cell_from(obj/item/I)
if(istype(I, /obj/item/gun/energy))
var/obj/item/gun/energy/E = I
return E.cell
if(istype(I, /obj/item/melee/baton))
var/obj/item/melee/baton/B = I
return B.cell
if(istype(I, /obj/item/modular_computer))
var/obj/item/modular_computer/C = I
var/obj/item/computer_hardware/battery/B = C.all_components[MC_CELL]
if(B)
return B.battery
if(istype(I, /obj/item/rcs))
var/obj/item/rcs/R = I
return R.rcell
if(istype(I, /obj/item/bodyanalyzer))
var/obj/item/bodyanalyzer/B = I
return B.cell
return null
/obj/machinery/recharger/proc/check_cell_needs_recharging(obj/item/stock_parts/cell/C)
if(!C || C.charge >= C.maxcharge)
return FALSE
return TRUE
/obj/machinery/recharger/proc/recharge_cell(obj/item/stock_parts/cell/C, power_usage)
C.give(C.chargerate * recharge_coeff)
use_power(power_usage)
/obj/machinery/recharger/proc/try_recharging_if_possible()
var/obj/item/stock_parts/cell/C = get_cell_from(charging)
if(!check_cell_needs_recharging(C))
return FALSE
if(istype(charging, /obj/item/gun/energy))
recharge_cell(C, RECHARGER_POWER_USAGE_GUN)
var/obj/item/gun/energy/E = charging
E.on_recharge()
else
recharge_cell(C, RECHARGER_POWER_USAGE_MISC)
return TRUE
/obj/machinery/recharger/examine(mob/user)
. = ..()
if(charging && (!in_range(user, src) && !issilicon(user) && !isobserver(user)))
@@ -204,3 +220,6 @@
icon_state_idle = "wrecharger0"
icon_state_charging = "wrecharger1"
icon_state_charged = "wrecharger2"
#undef RECHARGER_POWER_USAGE_GUN
#undef RECHARGER_POWER_USAGE_MISC
+7 -4
View File
@@ -740,10 +740,11 @@
if(!occupant)
return
if(user != occupant)
to_chat(occupant, "<span class='warning'>The machine kicks you out!</span>")
if(user.loc != loc)
to_chat(occupant, "<span class='warning'>You leave the not-so-cozy confines of the SSU.</span>")
if(user)
if(user != occupant)
to_chat(occupant, "<span class='warning'>The machine kicks you out!</span>")
if(user.loc != loc)
to_chat(occupant, "<span class='warning'>You leave the not-so-cozy confines of [src].</span>")
occupant.forceMove(loc)
occupant = null
if(!state_open)
@@ -751,6 +752,8 @@
update_icon()
return
/obj/machinery/suit_storage_unit/force_eject_occupant()
eject_occupant()
/obj/machinery/suit_storage_unit/verb/get_out()
set name = "Eject Suit Storage Unit"
-280
View File
@@ -1,280 +0,0 @@
/sound/turntable/test
file = 'sound/turntable/testloop1.ogg'
falloff = 2
repeat = 1
/mob/var/music = 0
/obj/machinery/party/turntable
name = "turntable"
desc = "A turntable used for parties and shit."
icon = 'icons/effects/lasers2.dmi'
icon_state = "turntable"
var/playing = 0
anchored = 1
/obj/machinery/party/mixer
name = "mixer"
desc = "A mixing board for mixing music"
icon = 'icons/effects/lasers2.dmi'
icon_state = "mixer"
anchored = 1
/obj/machinery/party/turntable/New()
..()
sleep(2)
new /sound/turntable/test(src)
return
/obj/machinery/party/turntable/attack_hand(mob/user as mob)
var/t = "<B>Turntable Interface</B><br><br>"
//t += "<A href='?src=[UID()];on=1'>On</A><br>"
t += "<A href='?src=[UID()];off=1'>Off</A><br><br>"
t += "<A href='?src=[UID()];on1=Testloop1'>One</A><br>"
t += "<A href='?src=[UID()];on2=Testloop2'>TestLoop2</A><br>"
t += "<A href='?src=[UID()];on3=Testloop3'>TestLoop3</A><br>"
user << browse(t, "window=turntable;size=420x700")
/obj/machinery/party/turntable/Topic(href, href_list)
..()
if( href_list["on1"] )
if(src.playing == 0)
// to_chat(world, "Should be working...")
var/sound/S = sound('sound/turntable/testloop1.ogg')
S.repeat = 1
S.channel = 10
S.falloff = 2
S.wait = 1
S.environment = 0
//for(var/mob/M in world)
// if(M.loc.loc == src.loc.loc && M.music == 0)
// to_chat(world, "Found the song...")
// M << S
// M.music = 1
var/area/A = src.loc.loc
for(var/obj/machinery/party/lasermachine/L in A)
L.turnon()
playing = 1
while(playing == 1)
for(var/mob/M in world)
if((M.loc.loc in A) && M.music == 0)
// to_chat(world, "Found the song...")
M << S
M.music = 1
else if(!(M.loc.loc in A) && M.music == 1)
var/sound/Soff = sound(null)
Soff.channel = 10
M << Soff
M.music = 0
sleep(10)
return
if( href_list["on2"] )
if(src.playing == 0)
// to_chat(world, "Should be working...")
var/sound/S = sound('sound/turntable/testloop2.ogg')
S.repeat = 1
S.channel = 10
S.falloff = 2
S.wait = 1
S.environment = 0
//for(var/mob/M in world)
// if(M.loc.loc == src.loc.loc && M.music == 0)
// to_chat(world, "Found the song...")
// M << S
// M.music = 1
var/area/A = src.loc.loc
for(var/obj/machinery/party/lasermachine/L in A)
L.turnon()
playing = 1
while(playing == 1)
for(var/mob/M in world)
if(M.loc.loc == src.loc.loc && M.music == 0)
// to_chat(world, "Found the song...")
M << S
M.music = 1
else if(M.loc.loc != src.loc.loc && M.music == 1)
var/sound/Soff = sound(null)
Soff.channel = 10
M << Soff
M.music = 0
sleep(10)
return
if( href_list["on3"] )
if(src.playing == 0)
// to_chat(world, "Should be working...")
var/sound/S = sound('sound/turntable/testloop3.ogg')
S.repeat = 1
S.channel = 10
S.falloff = 2
S.wait = 1
S.environment = 0
//for(var/mob/M in world)
// if(M.loc.loc == src.loc.loc && M.music == 0)
// to_chat(world, "Found the song...")
// M << S
// M.music = 1
var/area/A = src.loc.loc
for(var/obj/machinery/party/lasermachine/L in A)
L.turnon()
playing = 1
while(playing == 1)
for(var/mob/M in world)
if(M.loc.loc == src.loc.loc && M.music == 0)
// to_chat(world, "Found the song...")
M << S
M.music = 1
else if(M.loc.loc != src.loc.loc && M.music == 1)
var/sound/Soff = sound(null)
Soff.channel = 10
M << Soff
M.music = 0
sleep(10)
return
if( href_list["off"] )
if(src.playing == 1)
var/sound/S = sound(null)
S.channel = 10
S.wait = 1
for(var/mob/M in world)
M << S
M.music = 0
playing = 0
var/area/A = src.loc.loc
for(var/obj/machinery/party/lasermachine/L in A)
L.turnoff()
/obj/machinery/party/lasermachine
name = "laser machine"
desc = "A laser machine that shoots lasers."
icon = 'icons/effects/lasers2.dmi'
icon_state = "lasermachine"
anchored = 1
var/mirrored = 0
/obj/effect/turntable_laser
name = "laser"
desc = "A laser..."
icon = 'icons/effects/lasers2.dmi'
icon_state = "laserred1"
anchored = 1
layer = 4
/obj/item/lasermachine/New()
..()
/obj/machinery/party/lasermachine/proc/turnon()
var/wall = 0
var/cycle = 1
var/area/A = get_area(src)
var/X = 1
var/Y = 0
if(mirrored == 0)
while(wall == 0)
if(cycle == 1)
var/obj/effect/turntable_laser/F = new(src)
F.x = src.x+X
F.y = src.y+Y
F.z = src.z
F.icon_state = "laserred1"
var/area/AA = get_area(F)
var/turf/T = get_turf(F)
if(T.density == 1 || AA.name != A.name)
qdel(F)
return
cycle++
if(cycle > 3)
cycle = 1
X++
if(cycle == 2)
var/obj/effect/turntable_laser/F = new(src)
F.x = src.x+X
F.y = src.y+Y
F.z = src.z
F.icon_state = "laserred2"
var/area/AA = get_area(F)
var/turf/T = get_turf(F)
if(T.density == 1 || AA.name != A.name)
qdel(F)
return
cycle++
if(cycle > 3)
cycle = 1
Y++
if(cycle == 3)
var/obj/effect/turntable_laser/F = new(src)
F.x = src.x+X
F.y = src.y+Y
F.z = src.z
F.icon_state = "laserred3"
var/area/AA = get_area(F)
var/turf/T = get_turf(F)
if(T.density == 1 || AA.name != A.name)
qdel(F)
return
cycle++
if(cycle > 3)
cycle = 1
X++
if(mirrored == 1)
while(wall == 0)
if(cycle == 1)
var/obj/effect/turntable_laser/F = new(src)
F.x = src.x+X
F.y = src.y-Y
F.z = src.z
F.icon_state = "laserred1m"
var/area/AA = get_area(F)
var/turf/T = get_turf(F)
if(T.density == 1 || AA.name != A.name)
qdel(F)
return
cycle++
if(cycle > 3)
cycle = 1
Y++
if(cycle == 2)
var/obj/effect/turntable_laser/F = new(src)
F.x = src.x+X
F.y = src.y-Y
F.z = src.z
F.icon_state = "laserred2m"
var/area/AA = get_area(F)
var/turf/T = get_turf(F)
if(T.density == 1 || AA.name != A.name)
qdel(F)
return
cycle++
if(cycle > 3)
cycle = 1
X++
if(cycle == 3)
var/obj/effect/turntable_laser/F = new(src)
F.x = src.x+X
F.y = src.y-Y
F.z = src.z
F.icon_state = "laserred3m"
var/area/AA = get_area(F)
var/turf/T = get_turf(F)
if(T.density == 1 || AA.name != A.name)
qdel(F)
return
cycle++
if(cycle > 3)
cycle = 1
X++
/obj/machinery/party/lasermachine/proc/turnoff()
var/area/A = src.loc.loc
for(var/obj/effect/turntable_laser/F in A)
qdel(F)
@@ -94,7 +94,7 @@
/obj/item/mecha_parts/mecha_equipment/drill/proc/drill_mob(mob/living/target, mob/user)
target.visible_message("<span class='danger'>[chassis] is drilling [target] with [src]!</span>",
"<span class='userdanger'>[chassis] is drilling you with [src]!</span>")
add_attack_logs(user, target, "DRILLED with [src] (INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])")
add_attack_logs(user, target, "DRILLED with [src] ([uppertext(user.a_intent)]) ([uppertext(damtype)])")
if(target.stat == DEAD && target.getBruteLoss() >= 200)
add_attack_logs(user, target, "gibbed")
if(LAZYLEN(target.butcher_results))
@@ -60,7 +60,7 @@
target.visible_message("<span class='danger'>[chassis] squeezes [target].</span>", \
"<span class='userdanger'>[chassis] squeezes [target].</span>",\
"<span class='italics'>You hear something crack.</span>")
add_attack_logs(chassis.occupant, M, "Squeezed with [src] (INTENT: [uppertext(chassis.occupant.a_intent)]) (DAMTYE: [uppertext(damtype)])")
add_attack_logs(chassis.occupant, M, "Squeezed with [src] ([uppertext(chassis.occupant.a_intent)]) ([uppertext(damtype)])")
start_cooldown()
else
step_away(M,chassis)
+2 -2
View File
@@ -385,7 +385,7 @@
return
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang
name = "SGL-6 Grenade Launcher"
name = "SGL-6 Flashbang Launcher"
icon_state = "mecha_grenadelnchr"
origin_tech = "combat=4;engineering=4"
projectile = /obj/item/grenade/flashbang
@@ -411,7 +411,7 @@
return
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang/clusterbang//Because I am a heartless bastard -Sieve
name = "SOB-3 Grenade Launcher"
name = "SOB-3 Clusterbang Launcher"
desc = "A weapon for combat exosuits. Launches primed clusterbangs. You monster."
origin_tech = "combat=4;materials=4"
projectiles = 3
+4 -1
View File
@@ -870,7 +870,7 @@
return 0
use_power(melee_energy_drain)
if(M.damtype == BRUTE || M.damtype == BURN)
add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])")
add_attack_logs(M.occupant, src, "Mecha-attacked with [M] ([uppertext(M.occupant.a_intent)]) ([uppertext(M.damtype)])")
. = ..()
/obj/mecha/emag_act(mob/user)
@@ -1273,6 +1273,9 @@
L.client.RemoveViewMod("mecha")
zoom_mode = FALSE
/obj/mecha/force_eject_occupant()
go_out()
/////////////////////////
////// Access stuff /////
/////////////////////////
+1 -1
View File
@@ -180,7 +180,7 @@
if(player_spiders && !selecting_player)
selecting_player = 1
spawn()
var/list/candidates = pollCandidates("Do you want to play as a spider?", ROLE_GSPIDER, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a giant spider?", ROLE_GSPIDER, TRUE, source = S)
if(candidates.len)
var/mob/C = pick(candidates)
+20 -1
View File
@@ -2,6 +2,8 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
/obj/item
name = "item"
icon = 'icons/obj/items.dmi'
move_resist = null // Set in the Initialise depending on the item size. Unless it's overriden by a specific item
var/discrete = 0 // used in item_attack.dm to make an item not show an attack message to viewers
var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite
var/blood_overlay_color = null
@@ -113,6 +115,23 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
hitsound = 'sound/items/welder.ogg'
if(damtype == "brute")
hitsound = "swing_hit"
if(!move_resist)
determine_move_resist()
/obj/item/proc/determine_move_resist()
switch(w_class)
if(WEIGHT_CLASS_TINY)
move_resist = MOVE_FORCE_EXTREMELY_WEAK
if(WEIGHT_CLASS_SMALL)
move_resist = MOVE_FORCE_VERY_WEAK
if(WEIGHT_CLASS_NORMAL)
move_resist = MOVE_FORCE_WEAK
if(WEIGHT_CLASS_BULKY)
move_resist = MOVE_FORCE_NORMAL
if(WEIGHT_CLASS_HUGE)
move_resist = MOVE_FORCE_NORMAL
if(WEIGHT_CLASS_GIGANTIC)
move_resist = MOVE_FORCE_NORMAL
/obj/item/Destroy()
flags &= ~DROPDEL //prevent reqdels
@@ -506,7 +525,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect
"<span class='userdanger'>You stab yourself in the eyes with [src]!</span>" \
)
add_attack_logs(user, M, "Eye-stabbed with [src] (INTENT: [uppertext(user.a_intent)])")
add_attack_logs(user, M, "Eye-stabbed with [src] ([uppertext(user.a_intent)])")
if(istype(H))
var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes)
@@ -191,7 +191,7 @@
if(CanUse(U))
if(!Use(U))
return
to_chat(U, "<span class='notice'>You replace [target.fitting] with [src].</span>")
to_chat(U, "<span class='notice'>You replace the light [target.fitting] with [src].</span>")
if(target.status != LIGHT_EMPTY)
AddShards(1, U)
@@ -18,13 +18,6 @@
name = "syndicate personal AI device"
faction = list("syndicate")
/obj/item/paicard/relaymove(var/mob/user, var/direction)
if(user.stat || user.stunned)
return
var/obj/item/rig/rig = get_rig()
if(istype(rig))
rig.forced_move(direction, user)
/obj/item/paicard/New()
..()
overlays += "pai-off"
@@ -411,11 +411,16 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
jobname = "Unknown"
voicemask = TRUE
// Copy the message pieces so we can safely edit comms line without affecting the actual line
var/list/message_pieces_copy = list()
for(var/datum/multilingual_say_piece/S in message_pieces)
message_pieces_copy += new /datum/multilingual_say_piece(S.speaking, S.message)
// Make us a message datum!
var/datum/tcomms_message/tcm = new
tcm.sender_name = displayname
tcm.sender_job = jobname
tcm.message_pieces = message_pieces
tcm.message_pieces = message_pieces_copy
tcm.source_level = position.z
tcm.freq = connection.frequency
tcm.vmask = voicemask
+1 -1
View File
@@ -29,7 +29,7 @@
"<span class='userdanger'>[user] has prodded you with [src]!</span>")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
add_attack_logs(user, M, "Stunned with [src] (INTENT: [uppertext(user.a_intent)])")
add_attack_logs(user, M, "Stunned with [src] ([uppertext(user.a_intent)])")
/obj/item/borg/overdrive
name = "Overdrive"
@@ -15,6 +15,7 @@
*/
GLOBAL_LIST_INIT(metal_recipes, list(
new /datum/stack_recipe("stool", /obj/structure/chair/stool, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("barstool", /obj/structure/chair/stool/bar, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("chair", /obj/structure/chair, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("shuttle seat", /obj/structure/chair/comfy/shuttle, 2, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("sofa (middle)", /obj/structure/chair/sofa, one_per_turf = 1, on_floor = 1),
+50 -15
View File
@@ -344,56 +344,56 @@
/obj/item/toy/prize/ripley
name = "toy ripley"
desc = "Mini-Mecha action figure! Collect them all! 1/11."
desc = "Mini-Mecha action figure! Collect them all! 1/11. This one is a ripley, a mining and engineering mecha."
/obj/item/toy/prize/fireripley
name = "toy firefighting ripley"
desc = "Mini-Mecha action figure! Collect them all! 2/11."
desc = "Mini-Mecha action figure! Collect them all! 2/11. This one is a firefighter ripley, a fireproof mining and engineering mecha."
icon_state = "fireripleytoy"
/obj/item/toy/prize/deathripley
name = "toy deathsquad ripley"
desc = "Mini-Mecha action figure! Collect them all! 3/11."
desc = "Mini-Mecha action figure! Collect them all! 3/11. This one is the black ripley used by the hero of DeathSquad, that TV drama about loose-cannon ERT officers!"
icon_state = "deathripleytoy"
/obj/item/toy/prize/gygax
name = "toy gygax"
desc = "Mini-Mecha action figure! Collect them all! 4/11."
desc = "Mini-Mecha action figure! Collect them all! 4/11. This one is the speedy gygax combat mecha. Zoom zoom, pew pew!"
icon_state = "gygaxtoy"
/obj/item/toy/prize/durand
name = "toy durand"
desc = "Mini-Mecha action figure! Collect them all! 5/11."
desc = "Mini-Mecha action figure! Collect them all! 5/11. This one is the heavy durand combat mecha. Stomp stomp!"
icon_state = "durandprize"
/obj/item/toy/prize/honk
name = "toy H.O.N.K."
desc = "Mini-Mecha action figure! Collect them all! 6/11."
desc = "Mini-Mecha action figure! Collect them all! 6/11. This one is the infamous H.O.N.K mech!"
icon_state = "honkprize"
/obj/item/toy/prize/marauder
name = "toy marauder"
desc = "Mini-Mecha action figure! Collect them all! 7/11."
desc = "Mini-Mecha action figure! Collect them all! 7/11. This one is the powerful marauder combat mecha! Run for cover!"
icon_state = "marauderprize"
/obj/item/toy/prize/seraph
name = "toy seraph"
desc = "Mini-Mecha action figure! Collect them all! 8/11."
desc = "Mini-Mecha action figure! Collect them all! 8/11. This one is the powerful seraph combat mecha! Someone's in trouble!"
icon_state = "seraphprize"
/obj/item/toy/prize/mauler
name = "toy mauler"
desc = "Mini-Mecha action figure! Collect them all! 9/11."
desc = "Mini-Mecha action figure! Collect them all! 9/11. This one is the deadly mauler combat mecha! Look out!"
icon_state = "maulerprize"
/obj/item/toy/prize/odysseus
name = "toy odysseus"
desc = "Mini-Mecha action figure! Collect them all! 10/11."
desc = "Mini-Mecha action figure! Collect them all! 10/11. This one is the spindly, syringe-firing odysseus medical mecha."
icon_state = "odysseusprize"
/obj/item/toy/prize/phazon
name = "toy phazon"
desc = "Mini-Mecha action figure! Collect them all! 11/11."
desc = "Mini-Mecha action figure! Collect them all! 11/11. This one is the mysterious Phazon combat mecha! Nobody's safe!"
icon_state = "phazonprize"
@@ -1591,182 +1591,217 @@ obj/item/toy/cards/deck/syndicate/black
/obj/item/toy/figure/cmo
name = "Chief Medical Officer action figure"
desc = "The ever-suffering CMO, from Space Life's SS12 figurine collection."
icon_state = "cmo"
toysay = "Suit sensors!"
/obj/item/toy/figure/assistant
name = "Assistant action figure"
desc = "The faceless, hairless scourge of the station, from Space Life's SS12 figurine collection."
icon_state = "assistant"
toysay = "Grey tide station wide!"
/obj/item/toy/figure/atmos
name = "Atmospheric Technician action figure"
desc = "The faithful atmospheric technician, from Space Life's SS12 figurine collection."
icon_state = "atmos"
toysay = "Glory to Atmosia!"
/obj/item/toy/figure/bartender
name = "Bartender action figure"
desc = "The suave bartender, from Space Life's SS12 figurine collection."
icon_state = "bartender"
toysay = "Wheres my monkey?"
/obj/item/toy/figure/borg
name = "Cyborg action figure"
desc = "The iron-willed cyborg, from Space Life's SS12 figurine collection."
icon_state = "borg"
toysay = "I. LIVE. AGAIN."
/obj/item/toy/figure/botanist
name = "Botanist action figure"
desc = "The drug-addicted botanist, from Space Life's SS12 figurine collection."
icon_state = "botanist"
toysay = "Dude, I see colors..."
/obj/item/toy/figure/captain
name = "Captain action figure"
desc = "The inept captain, from Space Life's SS12 figurine collection."
icon_state = "captain"
toysay = "Crew, the Nuke Disk is safely up my ass."
/obj/item/toy/figure/cargotech
name = "Cargo Technician action figure"
desc = "The hard-working cargo tech, from Space Life's SS12 figurine collection."
icon_state = "cargotech"
toysay = "For Cargonia!"
/obj/item/toy/figure/ce
name = "Chief Engineer action figure"
desc = "The expert Chief Engineer, from Space Life's SS12 figurine collection."
icon_state = "ce"
toysay = "Wire the solars!"
/obj/item/toy/figure/chaplain
name = "Chaplain action figure"
desc = "The obsessed Chaplain, from Space Life's SS12 figurine collection."
icon_state = "chaplain"
toysay = "Gods make me a killing machine please!"
/obj/item/toy/figure/chef
name = "Chef action figure"
desc = "The cannibalistic chef, from Space Life's SS12 figurine collection."
icon_state = "chef"
toysay = "I swear it's not human meat."
/obj/item/toy/figure/chemist
name = "Chemist action figure"
desc = "The legally dubious Chemist, from Space Life's SS12 figurine collection."
icon_state = "chemist"
toysay = "Get your pills!"
/obj/item/toy/figure/clown
name = "Clown action figure"
desc = "The mischevious Clown, from Space Life's SS12 figurine collection."
icon_state = "clown"
toysay = "Honk!"
/obj/item/toy/figure/ian
name = "Ian action figure"
desc = "The adorable corgi, from Space Life's SS12 figurine collection."
icon_state = "ian"
toysay = "Arf!"
/obj/item/toy/figure/detective
name = "Detective action figure"
desc = "The clever detective, from Space Life's SS12 figurine collection."
icon_state = "detective"
toysay = "This airlock has grey jumpsuit and insulated glove fibers on it."
/obj/item/toy/figure/dsquad
name = "Death Squad Officer action figure"
desc = "It's a member of the DeathSquad, a TV drama where loose-cannon ERT officers face up against the threats of the galaxy! It's from Space Life's special edition SS12 figurine collection."
icon_state = "dsquad"
toysay = "Eliminate all threats!"
/obj/item/toy/figure/engineer
name = "Engineer action figure"
desc = "The frantic engineer, from Space Life's SS12 figurine collection."
icon_state = "engineer"
toysay = "Oh god, the singularity is loose!"
/obj/item/toy/figure/geneticist
name = "Geneticist action figure"
desc = "The balding geneticist, from Space Life's SS12 figurine collection."
icon_state = "geneticist"
toysay = "I'm not qualified for this job."
/obj/item/toy/figure/hop
name = "Head of Personnel action figure"
desc = "The officious Head of Personnel, from Space Life's SS12 figurine collection."
icon_state = "hop"
toysay = "Giving out all access!"
toysay = "Papers, please!"
/obj/item/toy/figure/hos
name = "Head of Security action figure"
desc = "The bloodlust-filled Head of Security, from Space Life's SS12 figurine collection."
icon_state = "hos"
toysay = "I'm here to win, anything else is secondary."
toysay = "Space law? What?"
/obj/item/toy/figure/qm
name = "Quartermaster action figure"
desc = "The nationalistic Quartermaster, from Space Life's SS12 figurine collection."
icon_state = "qm"
toysay = "Hail Cargonia!"
/obj/item/toy/figure/janitor
name = "Janitor action figure"
desc = "The water-using Janitor, from Space Life's SS12 figurine collection."
icon_state = "janitor"
toysay = "Look at the signs, you idiot."
/obj/item/toy/figure/lawyer
name = "Internal Affairs Agent action figure"
desc = "The unappreciated Internal Affairs Agent, from Space Life's SS12 figurine collection."
icon_state = "lawyer"
toysay = "Standard Operating Procedure says they're guilty! Hacking is proof they're an Enemy of the Corporation!"
/obj/item/toy/figure/librarian
name = "Librarian action figure"
desc = "The quiet Librarian, from Space Life's SS12 figurine collection."
icon_state = "librarian"
toysay = "One day while..."
/obj/item/toy/figure/md
name = "Medical Doctor action figure"
desc = "The stressed-out doctor, from Space Life's SS12 figurine collection."
icon_state = "md"
toysay = "The patient is already dead!"
/obj/item/toy/figure/mime
name = "Mime action figure"
desc = "A \"Space Life\" brand Mime action figure."
desc = "... from Space Life's SS12 figurine collection."
icon_state = "mime"
toysay = "..."
/obj/item/toy/figure/miner
name = "Shaft Miner action figure"
desc = "The gun-toting Shaft Miner, from Space Life's SS12 figurine collection."
icon_state = "miner"
toysay = "Oh god it's eating my intestines!"
/obj/item/toy/figure/ninja
name = "Ninja action figure"
desc = "It's the mysterious ninja! It's from Space Life's special edition SS12 figurine collection."
icon_state = "ninja"
toysay = "Oh god! Stop shooting, I'm friendly!"
/obj/item/toy/figure/wizard
name = "Wizard action figure"
desc = "It's the deadly, spell-slinging wizard! It's from Space Life's special edition SS12 figurine collection."
icon_state = "wizard"
toysay = "Ei Nath!"
/obj/item/toy/figure/rd
name = "Research Director action figure"
desc = "The ambitious RD, from Space Life's SS12 figurine collection."
icon_state = "rd"
toysay = "Blowing all of the borgs!"
/obj/item/toy/figure/roboticist
name = "Roboticist action figure"
desc = "The skillful Roboticist, from Space Life's SS12 figurine collection."
icon_state = "roboticist"
toysay = "He asked to be borged!"
/obj/item/toy/figure/scientist
name = "Scientist action figure"
desc = "The mad Scientist, from Space Life's SS12 figurine collection."
icon_state = "scientist"
toysay = "Someone else must have made those bombs!"
/obj/item/toy/figure/syndie
name = "Nuclear Operative action figure"
desc = "It's the red-suited Nuclear Operative! It's from Space Life's special edition SS12 figurine collection."
icon_state = "syndie"
toysay = "Get that fucking disk!"
/obj/item/toy/figure/secofficer
name = "Security Officer action figure"
desc = "The power-tripping Security Officer, from Space Life's SS12 figurine collection."
icon_state = "secofficer"
toysay = "I am the law!"
/obj/item/toy/figure/virologist
name = "Virologist action figure"
desc = "The pandemic-starting Virologist, from Space Life's SS12 figurine collection."
icon_state = "virologist"
toysay = "The cure is potassium!"
toysay = "It's not my virus!"
/obj/item/toy/figure/warden
name = "Warden action figure"
desc = "The amnesiac Warden, from Space Life's SS12 figurine collection."
icon_state = "warden"
toysay = "Execute him for breaking in!"
+133
View File
@@ -0,0 +1,133 @@
/// Delay in deci-seconds between two non-lethal attacks
#define BATON_STUN_COOLDOWN 4 SECONDS
/// Force of the telescopic baton when deployed
#define BATON_TELESCOPIC_FORCE_DEPLOYED 10
/**
* # Police Baton
*
* Knocks down the hit mob when not on harm intent and when [/obj/item/melee/classic_baton/on] is TRUE
*
* A non-lethal attack has a cooldown to avoid spamming
*/
/obj/item/melee/classic_baton
name = "police baton"
desc = "A wooden truncheon for beating criminal scum."
icon_state = "baton"
item_state = "classic_baton"
slot_flags = SLOT_BELT
force = 12 //9 hit crit
w_class = WEIGHT_CLASS_NORMAL
/// Whether the baton is on cooldown
var/on_cooldown = FALSE
/// Whether the baton is toggled on (to allow attacking)
var/on = TRUE
/obj/item/melee/classic_baton/attack(mob/living/target, mob/living/user)
if(!on)
return ..()
add_fingerprint(user)
if((CLUMSY in user.mutations) && prob(50))
user.visible_message("<span class='danger'>[user] accidentally clubs [user.p_them()]self with [src]!</span>", \
"<span class='userdanger'>You accidentally club yourself with [src]!</span>")
user.Weaken(force * 3)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.apply_damage(force * 2, BRUTE, "head")
else
user.take_organ_damage(force * 2)
return
if(user.a_intent == INTENT_HARM || isrobot(target)) // Lethal attack or it's a borg (can't knock them down!)
return ..()
else if(!on_cooldown) // Non-lethal attack - knock them down
// Check for shield/countering
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
return
if(check_martial_counter(H, user))
return
// Visuals and sound
user.do_attack_animation(target)
playsound(target, 'sound/effects/woodhit.ogg', 75, TRUE, -1)
add_attack_logs(user, target, "Stunned with [src]")
target.visible_message("<span class='danger'>[user] has knocked down [target] with \the [src]!</span>", \
"<span class='userdanger'>[user] has knocked down [target] with \the [src]!</span>")
// Hit 'em
target.LAssailant = iscarbon(user) ? user : null
target.Weaken(3)
on_cooldown = TRUE
addtimer(CALLBACK(src, .proc/cooldown_finished), BATON_STUN_COOLDOWN)
/**
* Called some time after a non-lethal attack
*/
/obj/item/melee/classic_baton/proc/cooldown_finished()
on_cooldown = FALSE
/**
* # Fancy Cane
*/
/obj/item/melee/classic_baton/ntcane
name = "fancy cane"
desc = "A cane with special engraving on it. It seems well suited for fending off assailants..."
icon_state = "cane_nt"
item_state = "cane_nt"
needs_permit = FALSE
/obj/item/melee/classic_baton/ntcane/is_crutch()
return TRUE
/**
* # Telescopic Baton
*/
/obj/item/melee/classic_baton/telescopic
name = "telescopic baton"
desc = "A compact yet robust personal defense weapon. Can be concealed when folded."
icon_state = "telebaton_0"
item_state = null
slot_flags = SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
needs_permit = FALSE
force = 0
on = FALSE
/// Attack verbs when concealed (created on Initialize)
var/static/list/attack_verb_off
/// Attack verbs when extended (created on Initialize)
var/static/list/attack_verb_on
/obj/item/melee/classic_baton/telescopic/Initialize(mapload)
. = ..()
if(!attack_verb_off)
attack_verb_off = list("hit", "poked")
attack_verb_on = list("smacked", "struck", "cracked", "beaten")
attack_verb = on ? attack_verb_on : attack_verb_off
/obj/item/melee/classic_baton/telescopic/attack_self(mob/user)
on = !on
icon_state = "telebaton_[on]"
if(on)
to_chat(user, "<span class='warning'>You extend the baton.</span>")
item_state = "nullrod"
w_class = WEIGHT_CLASS_BULKY //doesnt fit in backpack when its on for balance
force = BATON_TELESCOPIC_FORCE_DEPLOYED //stunbaton damage
attack_verb = attack_verb_on
else
to_chat(user, "<span class='notice'>You collapse the baton.</span>")
item_state = null //no sprite for concealment even when in hand
slot_flags = SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
force = 0 //not so robust now
attack_verb = attack_verb_off
// Update mob hand visuals
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.update_inv_l_hand()
H.update_inv_r_hand()
playsound(loc, 'sound/weapons/batonextend.ogg', 50, TRUE)
add_fingerprint(user)
#undef BATON_STUN_COOLDOWN
#undef BATON_TELESCOPIC_FORCE_DEPLOYED
@@ -212,6 +212,11 @@
return M
owner_ckey = null
/obj/item/card/id/proc/getPlayerCkey()
var/mob/living/carbon/human/H = getPlayer()
if(istype(H))
return H.ckey
/obj/item/card/id/proc/is_untrackable()
return untrackable
@@ -5,39 +5,51 @@
origin_tech = "materials=2;combat=3"
light_power = 10
light_color = LIGHT_COLOR_WHITE
var/light_time = 2
var/range = 7
var/light_time = 0.2 SECONDS // The duration the area is illuminated
var/range = 7 // The range in tiles of the flashbang
/obj/item/grenade/flashbang/prime()
update_mob()
var/flashbang_turf = get_turf(src)
if(!flashbang_turf)
return
var/turf/T = get_turf(src)
if(T)
// VFX and SFX
do_sparks(rand(5, 9), FALSE, src)
playsound(T, 'sound/effects/bang.ogg', 100, TRUE)
new /obj/effect/dummy/lighting_obj(T, light_color, range + 2, light_power, light_time)
set_light(7)
do_sparks(rand(5, 9), FALSE, src)
playsound(flashbang_turf, 'sound/effects/bang.ogg', 25, 1)
bang(flashbang_turf, src, range)
for(var/obj/structure/blob/B in hear(8, flashbang_turf)) //Blob damage here
var/damage = round(30 / (get_dist(B, get_turf(src)) + 1))
B.take_damage(damage, BURN, "melee", 0)
spawn(light_time)
qdel(src)
// Stunning & damaging mechanic
bang(T, src, range)
qdel(src)
/**
* Creates a flashing effect that blinds and deafens mobs within range
*
* Also damages blobs
* Arguments:
* * T - The turf to flash
* * A - The flashing atom
* * range - The range in tiles of the flash
* * flash - Whether to flash (blind)
* * bang - Whether to bang (deafen)
*/
/proc/bang(turf/T, atom/A, range = 7, flash = TRUE, bang = TRUE)
// Blob damage
for(var/obj/structure/blob/B in hear(range + 1, T))
var/damage = round(30 / (get_dist(B, T) + 1))
B.take_damage(damage, BURN, "melee", FALSE)
// Flashing mechanic
var/source_turf = get_turf(A)
for(var/mob/living/M in hearers(range, T))
if(M.stat == DEAD)
continue
M.show_message("<span class='warning'>BANG</span>", 2)
//Checking for protections
var/ear_safety = M.check_ear_prot()
var/distance = max(1, get_dist(get_turf(A), get_turf(M)))
var/distance = max(1, get_dist(source_turf, get_turf(M)))
var/stun_amount = max(10 / distance, 3)
//Flash
// Flash
if(flash)
if(M.weakeyes)
M.visible_message("<span class='disarm'><b>[M]</b> screams and collapses!</span>")
@@ -49,21 +61,20 @@
var/mob/living/carbon/human/H = M
var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes)
if(E)
E.receive_damage(8, 1)
E.receive_damage(8, TRUE)
if(M.flash_eyes(affect_silicon = TRUE))
M.Stun(max(10 / distance, 3))
M.Weaken(max(10 / distance, 3))
M.Stun(stun_amount)
M.Weaken(stun_amount)
//Bang
// Bang
var/ear_safety = M.check_ear_prot()
if(bang)
if(!distance || A.loc == M || A.loc == M.loc) //Holding on person or being exactly where lies is significantly more dangerous and voids protection
if(!distance || A.loc == M || A.loc == M.loc) // Holding on person or being exactly where lies is significantly more dangerous and voids protection
M.Stun(10)
M.Weaken(10)
if(!ear_safety)
M.Stun(max(10 / distance, 3))
M.Weaken(max(10 / distance, 3))
M.Stun(stun_amount)
M.Weaken(stun_amount)
M.AdjustEarDamage(rand(0, 5), 15)
if(iscarbon(M))
var/mob/living/carbon/C = M
@@ -74,6 +85,5 @@
if(prob(ears.ear_damage - 5))
to_chat(M, "<span class='warning'>You can't hear anything!</span>")
M.BecomeDeaf()
else
if(ears.ear_damage >= 5)
to_chat(M, "<span class='warning'>Your ears start to ring!</span>")
else if(ears.ear_damage >= 5)
to_chat(M, "<span class='warning'>Your ears start to ring!</span>")
@@ -255,7 +255,7 @@
possessed = TRUE
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, 0, 100)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, FALSE, 10 SECONDS, source = src)
var/mob/dead/observer/theghost = null
if(candidates.len)
@@ -205,7 +205,7 @@
if(empty)
return
new /obj/item/reagent_containers/hypospray/combat(src)
new /obj/item/reagent_containers/applicator/dual(src) // Because you ain't got no time to look at what damage dey taking yo
new /obj/item/reagent_containers/applicator/dual/syndi(src) // Because you ain't got no time to look at what damage dey taking yo
new /obj/item/defibrillator/compact/combat/loaded(src)
new /obj/item/clothing/glasses/hud/health/night(src)
@@ -1,119 +0,0 @@
/* Weapons
* Contains:
* Banhammer
* Classic Baton
*/
/*
* Banhammer
*/
/obj/item/banhammer/attack(mob/M, mob/user)
to_chat(M, "<font color='red'><b> You have been banned FOR NO REISIN by [user]<b></font>")
to_chat(user, "<font color='red'> You have <b>BANNED</b> [M]</font>")
playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much
/*
* Classic Baton
*/
/obj/item/melee/classic_baton
name = "police baton"
desc = "A wooden truncheon for beating criminal scum."
icon_state = "baton"
item_state = "classic_baton"
slot_flags = SLOT_BELT
force = 12 //9 hit crit
w_class = WEIGHT_CLASS_NORMAL
var/cooldown = 0
var/on = 1
/obj/item/melee/classic_baton/attack(mob/target as mob, mob/living/user as mob)
if(on)
add_fingerprint(user)
if((CLUMSY in user.mutations) && prob(50))
to_chat(user, "<span class ='danger'>You club yourself over the head.</span>")
user.Weaken(3 * force)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.apply_damage(2*force, BRUTE, "head")
else
user.take_organ_damage(2*force)
return
if(isrobot(target))
..()
return
if(!isliving(target))
return
if(user.a_intent == INTENT_HARM)
if(!..()) return
if(!isrobot(target)) return
else
if(cooldown <= 0)
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
return
if(check_martial_counter(H, user))
return
playsound(get_turf(src), 'sound/effects/woodhit.ogg', 75, 1, -1)
target.Weaken(3)
add_attack_logs(user, target, "Stunned with [src]")
add_fingerprint(user)
target.visible_message("<span class ='danger'>[user] has knocked down [target] with \the [src]!</span>", \
"<span class ='userdanger'>[user] has knocked down [target] with \the [src]!</span>")
if(!iscarbon(user))
target.LAssailant = null
else
target.LAssailant = user
cooldown = 1
spawn(40)
cooldown = 0
return
else
return ..()
/obj/item/melee/classic_baton/ntcane
name = "fancy cane"
desc = "A cane with special engraving on it. It seems well suited for fending off assailants..."
icon_state = "cane_nt"
item_state = "cane_nt"
needs_permit = 0
/obj/item/melee/classic_baton/ntcane/is_crutch()
return 1
//Telescopic baton
/obj/item/melee/classic_baton/telescopic
name = "telescopic baton"
desc = "A compact yet robust personal defense weapon. Can be concealed when folded."
icon_state = "telebaton_0"
item_state = null
slot_flags = SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
needs_permit = 0
force = 0
on = 0
/obj/item/melee/classic_baton/telescopic/attack_self(mob/user as mob)
on = !on
if(on)
to_chat(user, "<span class ='warning'>You extend the baton.</span>")
icon_state = "telebaton_1"
item_state = "nullrod"
w_class = WEIGHT_CLASS_BULKY //doesnt fit in backpack when its on for balance
force = 10 //stunbaton damage
attack_verb = list("smacked", "struck", "cracked", "beaten")
else
to_chat(user, "<span class ='notice'>You collapse the baton.</span>")
icon_state = "telebaton_0"
item_state = null //no sprite for concealment even when in hand
slot_flags = SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
force = 0 //not so robust now
attack_verb = list("hit", "poked")
if(istype(user,/mob/living/carbon/human))
var/mob/living/carbon/human/H = user
H.update_inv_l_hand()
H.update_inv_r_hand()
playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1)
add_fingerprint(user)
@@ -219,28 +219,3 @@
turn_off(cur_user)
return
..()
/obj/item/tank/jetpack/rig
name = "jetpack"
var/obj/item/rig/holder
actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization)
/obj/item/tank/jetpack/rig/examine()
. = list("It's a jetpack. If you can see this, report it on the bug tracker.")
/obj/item/tank/jetpack/rig/allow_thrust(num, mob/living/user)
if(!on)
return 0
if(!istype(holder) || !holder.air_supply)
return 0
var/datum/gas_mixture/removed = holder.air_supply.air_contents.remove(num)
if(removed.total_moles() < 0.005)
turn_off(user)
return 0
var/turf/T = get_turf(user)
T.assume_air(removed)
return 1
+8 -1
View File
@@ -1,3 +1,6 @@
/**
* # Banhammer
*/
/obj/item/banhammer
desc = "A banhammer"
name = "banhammer"
@@ -13,11 +16,15 @@
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70)
resistance_flags = FIRE_PROOF
/obj/item/banhammer/suicide_act(mob/user)
to_chat(viewers(user), "<span class='suicide'>[user] is hitting [user.p_them()]self with the [src.name]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.</span>")
return BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS
/obj/item/banhammer/attack(mob/M, mob/user)
to_chat(M, "<font color='red'><b> You have been banned FOR NO REISIN by [user]<b></font>")
to_chat(user, "<font color='red'> You have <b>BANNED</b> [M]</font>")
playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much
/obj/item/sord
name = "\improper SORD"
desc = "This thing is so unspeakably shitty you are having a hard time even holding it."
+6
View File
@@ -351,3 +351,9 @@ a {
/obj/proc/check_uplink_validity()
return TRUE
/obj/proc/force_eject_occupant()
// This proc handles safely removing occupant mobs from the object if they must be teleported out (due to being SSD/AFK, by admin teleport, etc) or transformed.
// In the event that the object doesn't have an overriden version of this proc to do it, log a runtime so one can be added.
CRASH("Proc force_eject_occupant() is not overriden on a machine containing a mob.")
@@ -354,6 +354,11 @@
/obj/structure/closet/AllowDrop()
return TRUE
/obj/structure/closet/force_eject_occupant()
// Its okay to silently teleport mobs out of lockers, since the only thing affected is their contents list.
return
/obj/structure/closet/bluespace
name = "bluespace closet"
desc = "A storage unit that moves and stores through the fourth dimension."
+1 -1
View File
@@ -40,7 +40,7 @@
return
if(!in_range(src, user))
return
if(!iscarbon(usr))
if(!iscarbon(usr) && !isrobot(usr))
return
playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
opened = !opened
+1
View File
@@ -53,6 +53,7 @@
wet_overlay = image('icons/effects/water.dmi', src, "ice_floor")
else
wet_overlay = image('icons/effects/water.dmi', src, "wet_static")
wet_overlay.plane = FLOOR_OVERLAY_PLANE
overlays += wet_overlay
if(time == INFINITY)
return
+1 -1
View File
@@ -25,7 +25,7 @@
var/blocks_air = 0
var/PathNode/PNode = null //associated PathNode in the A* algorithm
var/datum/pathnode/PNode = null //associated PathNode in the A* algorithm
flags = 0
+2 -1
View File
@@ -53,6 +53,7 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00")
return
log_ooc(msg, src)
mob.create_log(OOC_LOG, msg)
var/display_colour = GLOB.normal_ooc_colour
if(holder && !holder.fakekey)
@@ -205,7 +206,7 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00")
return
log_looc(msg, src)
mob.create_log(LOOC_LOG, msg)
var/mob/source = mob.get_looc_source()
var/list/heard = get_mobs_in_view(7, source)
+6 -4
View File
@@ -299,7 +299,8 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null)
datum/admins/proc/DB_ban_unban_by_id(var/id)
if(!check_rights(R_BAN)) return
if(!check_rights(R_BAN))
return
var/sql = "SELECT ckey FROM [format_table_name("ban")] WHERE id = [id]"
@@ -343,9 +344,9 @@ datum/admins/proc/DB_ban_unban_by_id(var/id)
/client/proc/DB_ban_panel()
set category = "Admin"
set name = "Banning Panel"
set desc = "Edit admin permissions"
set desc = "DB Ban Panel"
if(!holder)
if(!check_rights(R_BAN))
return
holder.DB_ban_panel()
@@ -356,7 +357,8 @@ datum/admins/proc/DB_ban_unban_by_id(var/id)
if(!usr.client)
return
if(!check_rights(R_BAN)) return
if(!check_rights(R_BAN))
return
establish_db_connection()
if(!GLOB.dbcon.IsConnected())
+8 -16
View File
@@ -69,7 +69,10 @@ GLOBAL_VAR_INIT(nologevent, 0)
body += "<body>Options panel for <b>[M]</b>"
if(M.client)
body += " played by <b>[M.client]</b> "
body += "\[<A href='?_src_=holder;editrights=rank;ckey=[M.ckey]'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\] "
if(check_rights(R_PERMISSIONS, 0))
body += "\[<A href='?_src_=holder;editrights=rank;ckey=[M.ckey]'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\] "
else
body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] "
body += "\[<A href='?_src_=holder;getplaytimewindow=[M.UID()]'>" + M.client.get_exp_type(EXP_TYPE_CREW) + " as [EXP_TYPE_CREW]</a>\]"
if(isnewplayer(M))
@@ -78,6 +81,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
body += " \[<A href='?_src_=holder;revive=[M.UID()]'>Heal</A>\] "
body += "<br><br>\[ "
body += "<a href='?_src_=holder;open_logging_view=[M.UID()];'>LOGS</a> - "
body += "<a href='?_src_=vars;Vars=[M.UID()]'>VV</a> - "
body += "[ADMIN_TP(M,"TP")] - "
if(M.client)
@@ -109,9 +113,10 @@ GLOBAL_VAR_INIT(nologevent, 0)
else
body += "<A href='?_src_=holder;watchadd=[M.ckey]'>Add to Watchlist</A> "
if(M.client)
body += "| <A href='?_src_=holder;sendtoprison=[M.UID()]'>Prison</A> | "
body += "\ <A href='?_src_=holder;sendbacktolobby=[M.UID()]'>Send back to Lobby</A> | "
body += "\ <A href='?_src_=holder;eraseflavortext=[M.UID()]'>Erase Flavor Text</A> | "
body += "\ <A href='?_src_=holder;userandomname=[M.UID()]'>Use Random Name</A> | "
var/muted = M.client.prefs.muted
body += {"<br><b>Mute: </b>
\[<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_IC]'><font color='[(muted & MUTE_IC)?"red":"blue"]'>IC</font></a> |
@@ -280,7 +285,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
/datum/admins/proc/vpn_whitelist()
set category = "Admin"
set name = "VPN Ckey Whitelist"
if(!check_rights(R_ADMIN))
if(!check_rights(R_BAN))
return
var/key = stripped_input(usr, "Enter ckey to add/remove, or leave blank to cancel:", "VPN Whitelist add/remove", max_length=32)
if(key)
@@ -760,19 +765,6 @@ GLOBAL_VAR_INIT(nologevent, 0)
world.update_status()
feedback_add_details("admin_verb","TR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggle_aliens()
set category = "Event"
set desc="Toggle alien mobs"
set name="Toggle Aliens"
if(!check_rights(R_EVENT))
return
GLOB.aliens_allowed = !GLOB.aliens_allowed
log_admin("[key_name(usr)] toggled aliens to [GLOB.aliens_allowed].")
message_admins("[key_name_admin(usr)] toggled aliens [GLOB.aliens_allowed ? "on" : "off"].")
feedback_add_details("admin_verb","TA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/delay()
set category = "Server"
set desc="Delay the game start/end"
+2 -1
View File
@@ -33,7 +33,8 @@
/client/proc/investigate_show( subject in GLOB.investigate_log_subjects )
set name = "Investigate"
set category = "Admin"
if(!holder) return
if(!check_rights(R_ADMIN))
return
switch(subject)
if("notes")
show_note()
+25 -55
View File
@@ -10,11 +10,8 @@ GLOBAL_LIST_INIT(admin_verbs_default, list(
GLOBAL_LIST_INIT(admin_verbs_admin, list(
/client/proc/check_antagonists, /*shows all antags*/
/datum/admins/proc/show_player_panel,
/client/proc/player_panel, /*shows an interface for all players, with links to various panels (old style)*/
/client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/
/client/proc/invisimin, /*allows our mob to go invisible/visible*/
/datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
/datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/
/datum/admins/proc/announce, /*priority announce something to all clients.*/
/client/proc/colorooc, /*allows us to set a custom colour for everything we say in ooc*/
/client/proc/resetcolorooc, /*allows us to set a reset our ooc color*/
@@ -23,7 +20,6 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list(
/client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
/client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/
/client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
/client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/
/client/proc/cmd_admin_open_logging_view,
@@ -37,6 +33,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list(
/client/proc/jumptoturf, /*allows us to jump to a specific turf*/
/client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/
/client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcomm*/
/client/proc/admin_deny_shuttle, /*toggles availability of shuttle calling*/
/client/proc/check_ai_laws, /*shows AI and borg laws*/
/client/proc/manage_silicon_laws, /* Allows viewing and editing silicon laws. */
/client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/
@@ -54,36 +51,30 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list(
/datum/admins/proc/PlayerNotes,
/client/proc/cmd_mentor_say,
/datum/admins/proc/show_player_notes,
/datum/admins/proc/vpn_whitelist,
/client/proc/free_slot, /*frees slot for chosen job*/
/client/proc/toggleattacklogs,
/client/proc/toggleadminlogs,
/client/proc/toggledebuglogs,
/client/proc/update_mob_sprite,
/client/proc/toggledrones,
/client/proc/man_up,
/client/proc/global_man_up,
/client/proc/delbook,
/client/proc/view_flagged_books,
/client/proc/view_asays,
/client/proc/empty_ai_core_toggle_latejoin,
/client/proc/aooc,
/client/proc/freeze,
/client/proc/alt_check,
/client/proc/secrets,
/client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */
/client/proc/change_human_appearance_self, /* Allows the human-based mob itself to change its basic appearance */
/client/proc/debug_variables,
/client/proc/reset_all_tcs, /*resets all telecomms scripts*/
/client/proc/toggle_mentor_chat,
/client/proc/toggle_advanced_interaction, /*toggle admin ability to interact with not only machines, but also atoms such as buttons and doors*/
/client/proc/list_ssds_afks,
/client/proc/cmd_admin_headset_message,
/client/proc/spawn_floor_cluwne
/client/proc/list_ssds_afks
))
GLOBAL_LIST_INIT(admin_verbs_ban, list(
/client/proc/unban_panel,
/client/proc/jobbans,
/client/proc/stickybanpanel
/client/proc/ban_panel,
/client/proc/stickybanpanel,
/datum/admins/proc/vpn_whitelist
))
GLOBAL_LIST_INIT(admin_verbs_sounds, list(
/client/proc/play_local_sound,
@@ -99,7 +90,6 @@ GLOBAL_LIST_INIT(admin_verbs_event, list(
/client/proc/drop_bomb,
/client/proc/cinematic,
/client/proc/one_click_antag,
/datum/admins/proc/toggle_aliens,
/client/proc/cmd_admin_add_freeform_ai_law,
/client/proc/cmd_admin_add_random_ai_law,
/client/proc/make_sound,
@@ -109,6 +99,7 @@ GLOBAL_LIST_INIT(admin_verbs_event, list(
/client/proc/show_tip,
/client/proc/cmd_admin_change_custom_event,
/datum/admins/proc/access_news_network, /*allows access of newscasters*/
/client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/
/client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/
/client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/
/client/proc/response_team, // Response Teams admin verb
@@ -116,7 +107,10 @@ GLOBAL_LIST_INIT(admin_verbs_event, list(
/client/proc/fax_panel,
/client/proc/event_manager_panel,
/client/proc/modify_goals,
/client/proc/outfit_manager
/client/proc/outfit_manager,
/client/proc/cmd_admin_headset_message,
/client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */
/client/proc/change_human_appearance_self /* Allows the human-based mob itself to change its basic appearance */
))
GLOBAL_LIST_INIT(admin_verbs_spawn, list(
@@ -125,24 +119,27 @@ GLOBAL_LIST_INIT(admin_verbs_spawn, list(
/client/proc/admin_deserialize
))
GLOBAL_LIST_INIT(admin_verbs_server, list(
/client/proc/reload_admins,
/client/proc/Set_Holiday,
/datum/admins/proc/startnow,
/datum/admins/proc/restart,
/datum/admins/proc/delay,
/datum/admins/proc/toggleaban,
/datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
/datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/
/client/proc/toggle_log_hrefs,
/client/proc/everyone_random,
/datum/admins/proc/toggleAI,
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
/client/proc/cmd_debug_del_all,
/client/proc/cmd_debug_del_sing,
/datum/admins/proc/toggle_aliens,
/client/proc/delbook,
/client/proc/view_flagged_books,
/client/proc/view_asays,
/client/proc/toggle_antagHUD_use,
/client/proc/toggle_antagHUD_restrictions,
/client/proc/set_ooc,
/client/proc/reset_ooc
/client/proc/reset_ooc,
/client/proc/toggledrones
))
GLOBAL_LIST_INIT(admin_verbs_debug, list(
/client/proc/cmd_admin_list_open_jobs,
@@ -151,9 +148,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list(
/client/proc/debug_controller,
/client/proc/cmd_debug_mob_lists,
/client/proc/cmd_admin_delete,
/client/proc/cmd_debug_del_all,
/client/proc/cmd_debug_del_sing,
/client/proc/reload_admins,
/client/proc/restart_controller,
/client/proc/enable_debug_verbs,
/client/proc/toggledebuglogs,
@@ -196,7 +191,7 @@ GLOBAL_LIST_INIT(admin_verbs_mod, list(
/client/proc/player_panel_new,
/client/proc/dsay,
/datum/admins/proc/show_player_panel,
/client/proc/jobbans,
/client/proc/ban_panel,
/client/proc/debug_variables /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/
))
GLOBAL_LIST_INIT(admin_verbs_mentor, list(
@@ -366,19 +361,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
to_chat(mob, "<span class='notice'>Invisimin on. You are now as invisible as a ghost.</span>")
mob.remove_from_all_data_huds()
/client/proc/player_panel()
set name = "Player Panel"
set category = "Admin"
if(!check_rights(R_ADMIN))
return
holder.player_panel_old()
feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/player_panel_new()
set name = "Player Panel New"
set name = "Player Panel"
set category = "Admin"
if(!check_rights(R_ADMIN|R_MOD))
@@ -400,22 +384,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/jobbans()
set name = "Display Job bans"
set category = "Admin"
if(!check_rights(R_ADMIN|R_MOD))
return
if(config.ban_legacy_system)
holder.Jobbans()
else
holder.DB_ban_panel()
feedback_add_details("admin_verb","VJB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
/client/proc/unban_panel()
set name = "Unban Panel"
/client/proc/ban_panel()
set name = "Ban Panel"
set category = "Admin"
if(!check_rights(R_BAN))
@@ -808,7 +778,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
set desc = "Allows you to change the mob appearance"
set category = null
if(!check_rights(R_ADMIN))
if(!check_rights(R_EVENT))
return
if(!istype(H))
@@ -834,7 +804,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
set desc = "Allows the mob to change its appearance"
set category = null
if(!check_rights(R_ADMIN))
if(!check_rights(R_EVENT))
return
if(!istype(H))
@@ -979,8 +949,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
else
to_chat(usr, "You now won't get debug log messages")
/client/proc/man_up(mob/T as mob in GLOB.mob_list)
set category = "Admin"
/client/proc/man_up(mob/T as mob in GLOB.player_list)
set category = null
set name = "Man Up"
set desc = "Tells mob to man up and deal with it."
-14
View File
@@ -42,20 +42,6 @@ GLOBAL_DATUM_INIT(jobban_regex, /regex, regex("(\[\\S]+) - (\[^#]+\[^# ])(?: ##
else
return 0
/*
DEBUG
/mob/verb/list_all_jobbans()
set name = "list all jobbans"
for(var/s in jobban_keylist)
to_chat(world, s)
/mob/verb/reload_jobbans()
set name = "reload jobbans"
jobban_loadbanfile()
*/
/proc/jobban_loadbanfile()
if(config.ban_legacy_system)
var/savefile/S=new("data/job_full.ban")
+5 -5
View File
@@ -1,20 +1,20 @@
/proc/machine_upgrade(obj/machinery/M in world)
set name = "Tweak Component Ratings"
set category = "Debug"
set category = null
if(!check_rights(R_DEBUG))
if(!check_rights(R_DEBUG))
return
if(!istype(M))
to_chat(usr, "<span class='danger'>This can only be used on subtypes of /obj/machinery.</span>")
return
var/new_rating = input("Enter new rating:","Num") as num
if(!isnull(new_rating) && M.component_parts)
for(var/obj/item/stock_parts/P in M.component_parts)
P.rating = new_rating
M.RefreshParts()
message_admins("[key_name_admin(usr)] has set the component rating of [M] to [new_rating]")
log_admin("[key_name(usr)] has set the component rating of [M] to [new_rating]")
-59
View File
@@ -332,65 +332,6 @@
usr << browse(dat, "window=players;size=600x480")
//The old one
/datum/admins/proc/player_panel_old()
if(!usr.client.holder)
return
var/dat = "<html><head><title>Player Menu</title></head>"
dat += "<body><table border=1 cellspacing=5><B><tr><th>Name</th><th>Real Name</th><th>Assigned Job</th><th>Key</th><th>Options</th><th>PM</th><th>Traitor?</th></tr></B>"
//add <th>IP:</th> to this if wanting to add back in IP checking
//add <td>(IP: [M.lastKnownIP])</td> if you want to know their ip to the lists below
var/list/mobs = sortmobs()
for(var/mob/M in mobs)
if(!M.ckey) continue
dat += "<tr><td>[M.name]</td>"
if(isAI(M))
dat += "<td>AI</td>"
else if(isrobot(M))
dat += "<td>Cyborg</td>"
else if(issmall(M))
dat += "<td>Monkey</td>"
else if(ishuman(M))
dat += "<td>[M.real_name]</td>"
else if(istype(M, /mob/living/silicon/pai))
dat += "<td>pAI</td>"
else if(isnewplayer(M))
dat += "<td>New Player</td>"
else if(isobserver(M))
dat += "<td>Ghost</td>"
else if(isalien(M))
dat += "<td>Alien</td>"
else
dat += "<td>Unknown</td>"
if(istype(M,/mob/living/carbon/human))
var/mob/living/carbon/human/H = M
if(H.mind && H.mind.assigned_role)
dat += "<td>[H.mind.assigned_role]</td>"
else
dat += "<td>NA</td>"
dat += {"<td>[(M.client ? "[M.client]" : "No client")]</td>
<td align=center><A HREF='?src=[UID()];adminplayeropts=[M.UID()]'>X</A></td>
<td align=center><A href='?src=[usr.UID()];priv_msg=[M.client ? M.client.UID() : null]'>PM</A></td>
"}
switch(is_special_character(M))
if(0)
dat += {"<td align=center><A HREF='?src=[UID()];traitor=[M.UID()]'>Traitor?</A></td>"}
if(1)
dat += {"<td align=center><A HREF='?src=[UID()];traitor=[M.UID()]'><font color=red>Traitor?</font></A></td>"}
if(2)
dat += {"<td align=center><A HREF='?src=[UID()];traitor=[M.UID()]'><font color=red><b>Traitor?</b></font></A></td>"}
dat += "</table></body></html>"
usr << browse(dat, "window=players;size=640x480")
/datum/admins/proc/check_antagonists_line(mob/M, caption = "", close = 1)
var/logout_status
@@ -5,7 +5,7 @@
set name = "Open Admin Ticket Interface"
set category = "Admin"
if(!holder || !check_rights(R_ADMIN))
if(!check_rights(R_ADMIN))
return
SStickets.showUI(usr)
@@ -14,7 +14,7 @@
set name = "Resolve All Open Admin Tickets"
set category = null
if(!holder || !check_rights(R_ADMIN))
if(!check_rights(R_ADMIN))
return
if(alert("Are you sure you want to resolve ALL open admin tickets?","Resolve all open admin tickets?","Yes","No") != "Yes")
@@ -5,7 +5,7 @@
set name = "Open Mentor Ticket Interface"
set category = "Admin"
if(!holder || !check_rights(R_MENTOR|R_ADMIN))
if(!check_rights(R_MENTOR|R_ADMIN))
return
SSmentor_tickets.showUI(usr)
@@ -14,7 +14,7 @@
set name = "Resolve All Open Mentor Tickets"
set category = null
if(!holder || !check_rights(R_ADMIN))
if(!check_rights(R_ADMIN))
return
if(alert("Are you sure you want to resolve ALL open mentor tickets?","Resolve all open mentor tickets?","Yes","No") != "Yes")
File diff suppressed because it is too large Load Diff
+13 -7
View File
@@ -125,22 +125,28 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo
ticketNum = T.ticketNum // ticketNum is the number of their ticket.
T.addResponse(src, msg)
msg = "[span][selected_type]: </span><span class='boldnotice'>[key_name(src, TRUE, selected_type)] ([ADMIN_QUE(mob,"?")]) ([ADMIN_PP(mob,"PP")]) ([ADMIN_VV(mob,"VV")]) ([ADMIN_TP(mob,"TP")]) ([ADMIN_SM(mob,"SM")]) ([admin_jump_link(mob)]) (<A HREF='?_src_=holder;[isMhelp ? "openmentorticket" : "openadminticket"]=[ticketNum]'>TICKET</A>) [ai_found ? "(<A HREF='?_src_=holder;adminchecklaws=[mob.UID()]'>CL</A>)" : ""] (<A HREF='?_src_=holder;take_question=[ticketNum][isMhelp ? ";is_mhelp=1" : ""]'>TAKE</A>) (<A HREF='?_src_=holder;resolve=[ticketNum][isMhelp ? ";is_mhelp=1" : ""]'>RESOLVE</A>) [isMhelp ? "" : "<A HREF='?_src_=holder;autorespond=[ticketNum]'>(AUTO)</A>"] :</span> [span][msg]</span>"
var/finalised_msg = "[span][selected_type]: </span><span class='boldnotice'>[key_name(src, TRUE, selected_type)] "
finalised_msg += "([ADMIN_QUE(mob,"?")]) ([ADMIN_PP(mob,"PP")]) ([ADMIN_VV(mob,"VV")]) ([ADMIN_TP(mob,"TP")]) ([ADMIN_SM(mob,"SM")]) "
finalised_msg += "([admin_jump_link(mob)]) (<A HREF='?_src_=holder;[isMhelp ? "openmentorticket" : "openadminticket"]=[ticketNum]'>TICKET</A>) "
finalised_msg += "[ai_found ? "(<A HREF='?_src_=holder;adminchecklaws=[mob.UID()]'>CL</A>)" : ""] (<A HREF='?_src_=holder;take_question=[ticketNum][isMhelp ? ";is_mhelp=1" : ""]'>TAKE</A>) "
finalised_msg += "(<A HREF='?_src_=holder;resolve=[ticketNum][isMhelp ? ";is_mhelp=1" : ""]'>RESOLVE</A>) [isMhelp ? "" : "<A HREF='?_src_=holder;autorespond=[ticketNum]'>(AUTO)</A>"] "
finalised_msg += "<a href='?_src_=holder;convert_ticket=[ticketNum][isMhelp ? ";is_mhelp=1" : ""]'>(CONVERT)</a> :</span> [span][msg]</span>"
if(isMhelp)
//Open a new adminticket and inform the user.
SSmentor_tickets.newTicket(src, prunedmsg, msg)
SSmentor_tickets.newTicket(src, prunedmsg, finalised_msg)
for(var/client/X in mentorholders + modholders + adminholders)
if(X.prefs.sound & SOUND_MENTORHELP)
X << 'sound/effects/adminhelp.ogg'
to_chat(X, msg)
SEND_SOUND(X, 'sound/effects/adminhelp.ogg')
to_chat(X, finalised_msg)
else //Ahelp
//Open a new adminticket and inform the user.
SStickets.newTicket(src, prunedmsg, msg)
SStickets.newTicket(src, prunedmsg, finalised_msg)
for(var/client/X in modholders + adminholders)
if(X.prefs.sound & SOUND_ADMINHELP)
X << 'sound/effects/adminhelp.ogg'
SEND_SOUND(X, 'sound/effects/adminhelp.ogg')
window_flash(X)
to_chat(X, msg)
to_chat(X, finalised_msg)
+28 -3
View File
@@ -22,6 +22,10 @@
to_chat(src, "Nowhere to jump to!")
return
if(isobj(usr.loc))
var/obj/O = usr.loc
O.force_eject_occupant()
admin_forcemove(usr, T)
log_admin("[key_name(usr)] jumped to [A]")
if(!isobserver(usr))
@@ -35,6 +39,9 @@
if(!check_rights(R_ADMIN))
return
if(isobj(usr.loc))
var/obj/O = usr.loc
O.force_eject_occupant()
log_admin("[key_name(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]")
if(!isobserver(usr))
message_admins("[key_name_admin(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]", 1)
@@ -52,6 +59,9 @@
log_admin("[key_name(usr)] jumped to [key_name(M)]")
if(!isobserver(usr))
message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1)
if(isobj(usr.loc))
var/obj/O = usr.loc
O.force_eject_occupant()
if(src.mob)
var/mob/A = src.mob
var/turf/T = get_turf(M)
@@ -70,6 +80,9 @@
var/turf/T = locate(tx, ty, tz)
if(T)
if(isobj(usr.loc))
var/obj/O = usr.loc
O.force_eject_occupant()
admin_forcemove(usr, T)
if(isobserver(usr))
var/mob/dead/observer/O = usr
@@ -96,13 +109,15 @@
log_admin("[key_name(usr)] jumped to [key_name(M)]")
if(!isobserver(usr))
message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1)
if(isobj(usr.loc))
var/obj/O = usr.loc
O.force_eject_occupant()
admin_forcemove(usr, M.loc)
feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/Getmob(var/mob/M in GLOB.mob_list)
set category = "Admin"
set category = null
set name = "Get Mob"
set desc = "Mob to teleport"
@@ -111,11 +126,15 @@
log_admin("[key_name(usr)] teleported [key_name(M)]")
message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]", 1)
if(isobj(M.loc))
var/obj/O = M.loc
O.force_eject_occupant()
admin_forcemove(M, get_turf(usr))
feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/Getkey()
set category = "Admin"
set category = null
set name = "Get Key"
set desc = "Key to teleport"
@@ -135,6 +154,9 @@
log_admin("[key_name(usr)] teleported [key_name(M)]")
message_admins("[key_name_admin(usr)] teleported [key_name(M)]", 1)
if(M)
if(isobj(M.loc))
var/obj/O = M.loc
O.force_eject_occupant()
admin_forcemove(M, get_turf(usr))
admin_forcemove(usr, M.loc)
feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -148,6 +170,9 @@
var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas()
if(A)
if(isobj(M.loc))
var/obj/O = M.loc
O.force_eject_occupant()
admin_forcemove(M, pick(get_area_turfs(A)))
feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] teleported [key_name(M)] to [A]")
+6 -7
View File
@@ -2,19 +2,19 @@
/client/proc/cmd_admin_pm_context(mob/M as mob in GLOB.mob_list)
set category = null
set name = "Admin PM Mob"
if(!holder)
to_chat(src, "<span class='danger'>Error: Admin-PM-Context: Only administrators may use this command.</span>")
if(!check_rights(R_ADMIN|R_MENTOR))
return
if(!ismob(M) || !M.client)
return
if( !ismob(M) || !M.client ) return
cmd_admin_pm(M.client,null)
feedback_add_details("admin_verb","APMM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//shows a list of clients we could send PMs to, then forwards our choice to cmd_admin_pm
/client/proc/cmd_admin_pm_panel()
set category = "Admin"
set name = "Admin PM Name"
if(!holder)
to_chat(src, "<span class='danger'>Error: Admin-PM-Panel: Only administrators may use this command.</span>")
if(!check_rights(R_ADMIN|R_MENTOR))
return
var/list/client/targets[0]
for(var/client/T)
@@ -36,8 +36,7 @@
/client/proc/cmd_admin_pm_by_key_panel()
set category = "Admin"
set name = "Admin PM Key"
if(!holder)
to_chat(src, "<span class='danger'>Error: Admin-PM-Panel: Only administrators may use this command.</span>")
if(!check_rights(R_ADMIN|R_MENTOR))
return
var/list/client/targets[0]
for(var/client/T)
+2
View File
@@ -7,6 +7,8 @@
msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN))
if(!msg) return
var/datum/asays/asay = new(usr.ckey, usr.client.holder.rank, msg, world.timeofday)
GLOB.asays += asay
log_adminsay(msg, src)
if(check_rights(R_ADMIN,0))
-20
View File
@@ -1,20 +0,0 @@
/client/proc/alt_check()
set category = "Admin"
set name = "Alt Account Checker"
var/dat = {"<B>Just to be sure you should try to also look up computer IDs/IPs on the server logs for a second opinion.</B>
<br>Additionally make an attempt to introduce new players to the server
<HR>"}
if(GLOB.dbcon.IsConnected())
for(var/client/C in GLOB.clients)
dat += "<p>[C.ckey] (Player Age: <font color = 'red'>[C.player_age]</font>) - <b>[C.computer_id]</b> / <b>[C.address]</b><br>"
if(C.related_accounts_cid.len)
dat += "--Accounts associated with CID: "
dat += "<b>[jointext(C.related_accounts_cid, " - ")]</b><br>"
if(C.related_accounts_ip.len)
dat += "--Accounts associated with IP: "
dat += "<b>[jointext(C.related_accounts_ip, " - ")]</b> "
usr << browse(dat, "window=alt_panel;size=640x480")
return
+70
View File
@@ -0,0 +1,70 @@
GLOBAL_LIST_EMPTY(asays)
/datum/asays
var/ckey
var/rank
var/message
var/time
/datum/asays/New(ckey = "", rank = "", message = "", time = 0)
src.ckey = ckey
src.rank = rank
src.message = message
src.time = time
/client/proc/view_asays()
set name = "Asays"
set desc = "View Asays from the current round."
set category = "Admin"
if(!check_rights(R_ADMIN))
return
var/list/output = list({"
<style>
td, th
{
border: 1px solid #425c6e;
padding: 3px;
}
thead
{
color: #517087;
font-weight: bold;
table-layout: fixed;
}
</style>
<a href='byond://?src=[holder.UID()];asays=1'>Refresh</a>
<table style='width: 100%; border-collapse: collapse; table-layout: auto; margin-top: 3px;'>
"})
// Header & body start
output += {"
<thead>
<tr>
<th width="5%">Time</th>
<th width="10%">Ckey</th>
<th width="85%">Message</th>
</tr>
</thead>
<tbody>
"}
for(var/datum/asays/A in GLOB.asays)
var/timestr = time2text(A.time, "hh:mm:ss")
output += {"
<tr>
<td width="5%">[timestr]</td>
<td width="10%"><b>[A.ckey] ([A.rank])</b></td>
<td width="85%">[A.message]</td>
</tr>
"}
output += {"
</tbody>
</table>"}
var/datum/browser/popup = new(src, "asays", "<div align='center'>Current Round Asays</div>", 1200, 825)
popup.set_content(output.Join())
popup.open(0)
+1 -2
View File
@@ -3,8 +3,7 @@
set category = "Event"
set name = "Change Custom Event"
if(!holder)
to_chat(src, "Only administrators may use this command.")
if(!check_rights(R_EVENT))
return
var/input = input(usr, "Enter the description of the custom event. Be descriptive. To cancel the event, make this blank or hit cancel.", "Custom Event", GLOB.custom_event_msg) as message|null

Some files were not shown because too many files have changed in this diff Show More