Merge remote-tracking branch 'upstream/master' into wires-tgui

This commit is contained in:
SteelSlayer
2020-08-25 16:36:13 -05:00
146 changed files with 2247 additions and 1406 deletions
@@ -152,12 +152,7 @@
/turf/simulated/floor/wood,
/area/ruin/unpowered)
"az" = (
/obj/item/spellbook/oneuse/charge{
desc = "For the technology-hating wizard.";
spell = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech;
spellname = "Disable Technology";
used = 1
},
/obj/item/spellbook/oneuse/emp/used,
/turf/simulated/floor/wood,
/area/ruin/unpowered)
"aA" = (
+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
+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)
-10
View File
@@ -1919,16 +1919,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
return pois
/*
* Returns all player mobs in an assoc list with ckey as key and the mob as value
*/
/proc/get_assoc_mob_list_by_ckey()
var/list/mob/mobs = list()
for(var/i in GLOB.player_list)
var/mob/M = i
mobs[M.ckey] = M
return mobs
/proc/flash_color(mob_or_client, flash_color="#960000", flash_time=20)
var/client/C
if(istype(mob_or_client, /mob))
+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"))
+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"
+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\">"
+3 -3
View File
@@ -61,7 +61,7 @@ SUBSYSTEM_DEF(ticker)
if(GAME_STATE_STARTUP)
// This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails
round_start_time = world.time + (config.pregame_timestart * 10)
to_chat(world, "<B><FONT color='blue'>Welcome to the pre-game lobby!</FONT></B>")
to_chat(world, "<B><span class='darkmblue'>Welcome to the pre-game lobby!</span></B>")
to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds")
current_state = GAME_STATE_PREGAME
fire() // TG says this is a good idea
@@ -217,11 +217,11 @@ SUBSYSTEM_DEF(ticker)
for(var/obj/effect/landmark/spacepod/random/R in L)
qdel(R)
to_chat(world, "<FONT color='blue'><B>Enjoy the game!</B></FONT>")
to_chat(world, "<span class='darkmblue'><B>Enjoy the game!</B></span>")
world << sound('sound/AI/welcome.ogg')// Skie
if(SSholiday.holidays)
to_chat(world, "<font color='blue'>and...</font>")
to_chat(world, "<span class='darkmblue'>and...</span>")
for(var/holidayname in SSholiday.holidays)
var/datum/holiday/holiday = SSholiday.holidays[holidayname]
to_chat(world, "<h4>[holiday.greet()]</h4>")
@@ -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)
+50 -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]
@@ -158,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]
@@ -353,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 />"
@@ -368,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)
@@ -450,7 +487,6 @@ UI STUFF
if(closeTicket(indexNum))
showDetailUI(usr, indexNum)
if(href_list["detailreopen"])
var/indexNum = text2num(href_list["detailreopen"])
if(openTicket(indexNum))
@@ -470,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)
+65 -20
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_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 = 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)
@@ -95,13 +106,19 @@
if(!mobs?.len)
return
for(var/i in mobs)
var/mob/M = i
if(istype(M))
selected_mobs |= M
add_mob(usr, i, FALSE)
/datum/log_viewer/proc/add_mob(mob/user, mob/M)
/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)
@@ -112,7 +129,7 @@
var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;"
var/dat
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:95px'>"
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>"
@@ -125,19 +142,24 @@
continue
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()];add_mob_ckey=1'>Add Mob (by ckey)</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
@@ -153,7 +175,7 @@
var/tdStyleType = "width:80px; 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:calc(100vh - 145px);'>"
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)
@@ -207,15 +229,20 @@
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_mob_ckey"])
var/list/mobs = get_assoc_mob_list_by_ckey()
var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a ckey: ", mobs)
A.on_close(CALLBACK(src, .proc/add_mob, usr))
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"])
@@ -223,6 +250,10 @@
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)
@@ -246,6 +277,12 @@
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)
@@ -254,4 +291,12 @@
name = "[name] ([M.real_name])"
if(isobserver(M))
name = "[name] (DEAD)"
return name
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
+2 -2
View File
@@ -222,10 +222,10 @@
name = "NT Undercover Operative"
// Disguised NT special forces, sent to quietly eliminate or keep tabs on people in high positions (e.g: captain)
uniform = /obj/item/clothing/under/color/black
uniform = /obj/item/clothing/under/color/random
back = /obj/item/storage/backpack
belt = /obj/item/storage/belt/utility/full/multitool
gloves = /obj/item/clothing/gloves/combat
gloves = /obj/item/clothing/gloves/color/yellow
shoes = /obj/item/clothing/shoes/chameleon/noslip
l_ear = /obj/item/radio/headset/centcom
id = /obj/item/card/id
+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)
+10 -1
View File
@@ -1,7 +1,16 @@
// List is required to compile the resources into the game when it loads.
// Dynamically loading it has bad results with sounds overtaking each other, even with the wait variable.
GLOBAL_LIST_INIT(vox_sounds, list("," = 'sound/vox_fem/,.ogg',
GLOBAL_LIST_INIT(vox_alerts, list(
"bizwarn" = 'sound/vox_fem/bizwarn.ogg',
"bloop" = 'sound/vox_fem/bloop.ogg',
"buzwarn" = 'sound/vox_fem/buzwarn.ogg',
"dadeda" = 'sound/vox_fem/dadeda.ogg',
"deeoo" = 'sound/vox_fem/deeoo.ogg'
))
GLOBAL_LIST_INIT(vox_sounds, list(
"," = 'sound/vox_fem/,.ogg',
"." = 'sound/vox_fem/..ogg',
"a" = 'sound/vox_fem/a.ogg',
"abortions" = 'sound/vox_fem/abortions.ogg',
+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
+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)
+6
View File
@@ -140,6 +140,7 @@
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
allowed = list(/obj/item/tome,/obj/item/melee/cultblade)
var/current_charges = 3
var/shield_state = "shield-cult"
hoodtype = /obj/item/clothing/head/hooded/cult_hoodie
/obj/item/clothing/head/hooded/cult_hoodie
@@ -164,13 +165,18 @@
if(current_charges)
owner.visible_message("<span class='danger'>\The [attack_text] is deflected in a burst of blood-red sparks!</span>")
current_charges--
playsound(loc, "sparks", 100, 1)
new /obj/effect/temp_visual/cult/sparks(get_turf(owner))
if(!current_charges)
owner.visible_message("<span class='danger'>The runed shield around [owner] suddenly disappears!</span>")
shield_state = "broken"
owner.update_inv_wear_suit()
return 1
return 0
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/special_overlays()
return mutable_appearance('icons/effects/cult_effects.dmi', shield_state, MOB_LAYER + 0.01)
/obj/item/clothing/suit/hooded/cultrobes/berserker
name = "flagellant's robes"
desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage."
+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 -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>")
+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())
+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"
+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
+4 -17
View File
@@ -404,7 +404,7 @@
cashmoney.use(currently_vending.price)
// Vending machines have no idea who paid with cash
credit_purchase("(cash)")
GLOB.vendor_account.credit(currently_vending.price, "Sale of [currently_vending.name]", name, "(cash)")
return 1
/**
@@ -444,26 +444,12 @@
return 0
else
// Okay to move the money at this point
var/paid = customer_account.charge(currently_vending.price, GLOB.vendor_account,
customer_account.charge(currently_vending.price, GLOB.vendor_account,
"Purchase of [currently_vending.name]", name, GLOB.vendor_account.owner_name,
"Sale of [currently_vending.name]", customer_account.owner_name)
if(paid)
// Give the vendor the money. We use the account owner name, which means
// that purchases made with stolen/borrowed card will look like the card
// owner made them
credit_purchase(customer_account.owner_name)
return paid
return TRUE
/**
* Add money for current purchase to the vendor account.
*
* Called after the money has already been taken from the customer.
*/
/obj/machinery/vending/proc/credit_purchase(var/target as text)
GLOB.vendor_account.money += currently_vending.price
GLOB.vendor_account.credit(currently_vending.price, "Sale of [currently_vending.name]",
name, target)
/obj/machinery/vending/attack_ai(mob/user)
return attack_hand(user)
@@ -1481,6 +1467,7 @@
/obj/item/clothing/under/victsuit/redblk = 1,
/obj/item/clothing/under/victsuit/red = 1,
/obj/item/clothing/suit/tailcoat = 1,
/obj/item/clothing/under/tourist_suit = 1,
/obj/item/clothing/suit/draculacoat = 1,
/obj/item/clothing/head/zepelli = 1,
/obj/item/clothing/under/redhawaiianshirt = 1,
+3 -3
View File
@@ -58,7 +58,7 @@
icon_state = "eggs"
var/amount_grown = 0
var/player_spiders = 0
var/list/faction = list()
var/list/faction = list("spiders")
/obj/structure/spider/eggcluster/New()
..()
@@ -90,7 +90,7 @@
var/obj/machinery/atmospherics/unary/vent_pump/entry_vent
var/travelling_in_vent = 0
var/player_spiders = 0
var/list/faction = list()
var/list/faction = list("spiders")
var/selecting_player = 0
/obj/structure/spider/spiderling/New()
@@ -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)
+19
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
@@ -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)
+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
+3 -4
View File
@@ -55,16 +55,15 @@
/obj/item/lipstick/random/Initialize(mapload)
. = ..()
var/lscolor = pick(lipstick_colors) // A random color is picked from the var defined initially in a new var.
colour = lipstick_colors[lscolor] // The color of the lipstick is pulled from the new variable (right hand side, HTML & Hex RGB)
name = "[lscolor] lipstick" // The new variable is also used to match the name to the color of the lipstick. Kudos to Desolate & Lemon
colour = pick(lipstick_colors)
name = "[colour] lipstick"
/obj/item/lipstick/attack_self(mob/user)
cut_overlays()
to_chat(user, "<span class='notice'>You twist \the [src] [open ? "closed" : "open"].</span>")
open = !open
if(open)
var/image/colored = mutable_appearance('icons/obj/items.dmi', "lipstick_uncap_color")
var/mutable_appearance/colored = mutable_appearance('icons/obj/items.dmi', "lipstick_uncap_color")
colored.color = lipstick_colors[colour]
icon_state = "lipstick_uncap"
add_overlay(colored)
@@ -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)
@@ -204,8 +204,10 @@
/obj/item/flashlight/pen,
/obj/item/seeds,
/obj/item/wirecutters,
/obj/item/wrench,
)
/obj/item/wrench,
/obj/item/reagent_containers/spray/weedspray,
/obj/item/reagent_containers/spray/pestspray
)
/obj/item/storage/belt/security
name = "security belt"
@@ -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)
+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."
@@ -25,9 +25,13 @@
..()
spawn(1)
if(!opened) // if closed, any item at the crate's loc is put in the contents
var/itemcount = 0
for(var/obj/item/I in loc)
if(I.density || I.anchored || I == src) continue
I.forceMove(src)
// Ensure the storage cap is respected
if(++itemcount >= storage_capacity)
break
// Fix for #383 - C4 deleting fridges with corpses
/obj/structure/closet/Destroy()
+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
+4 -3
View File
@@ -1,6 +1,6 @@
GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
GLOBAL_VAR_INIT(normal_ooc_colour, "#275FC5")
GLOBAL_VAR_INIT(member_ooc_colour, "#035417")
GLOBAL_VAR_INIT(mentor_ooc_colour, "#0099cc")
GLOBAL_VAR_INIT(mentor_ooc_colour, "#00B0EB")
GLOBAL_VAR_INIT(moderator_ooc_colour, "#184880")
GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00")
@@ -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())
+14 -23
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))
@@ -110,17 +113,18 @@ 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> |
<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_OOC]'><font color='[(muted & MUTE_OOC)?"red":"blue"]'>OOC</font></a> |
<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_PRAY]'><font color='[(muted & MUTE_PRAY)?"red":"blue"]'>PRAY</font></a> |
<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_ADMINHELP]'><font color='[(muted & MUTE_ADMINHELP)?"red":"blue"]'>ADMINHELP</font></a> |
<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_DEADCHAT]'><font color='[(muted & MUTE_DEADCHAT)?"red":"blue"]'>DEADCHAT</font></a>\]
(<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_ALL]'><font color='[(muted & MUTE_ALL)?"red":"blue"]'>toggle all</font></a>)
\[<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_IC]'><font color='[(muted & MUTE_IC)?"red":"#6685f5"]'>IC</font></a> |
<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_OOC]'><font color='[(muted & MUTE_OOC)?"red":"#6685f5"]'>OOC</font></a> |
<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_PRAY]'><font color='[(muted & MUTE_PRAY)?"red":"#6685f5"]'>PRAY</font></a> |
<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_ADMINHELP]'><font color='[(muted & MUTE_ADMINHELP)?"red":"#6685f5"]'>ADMINHELP</font></a> |
<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_DEADCHAT]'><font color='[(muted & MUTE_DEADCHAT)?"red":"#6685f5"]'>DEADCHAT</font></a>\]
(<A href='?_src_=holder;mute=[M.UID()];mute_type=[MUTE_ALL]'><font color='[(muted & MUTE_ALL)?"red":"#6685f5"]'>toggle all</font></a>)
"}
var/jumptoeye = ""
@@ -281,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)
@@ -700,7 +704,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
var/msg = ""
if(SSticker.current_state == GAME_STATE_STARTUP)
msg = " (The server is still setting up, but the round will be started as soon as possible.)"
message_admins("<font color='blue'>[usr.key] has started the game.[msg]</font>")
message_admins("<span class='darkmblue'>[usr.key] has started the game.[msg]</span>")
feedback_add_details("admin_verb","SN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return 1
else
@@ -761,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()
+24 -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,
@@ -55,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,
@@ -100,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,
@@ -110,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
@@ -117,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(
@@ -126,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,
@@ -152,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,
@@ -197,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(
@@ -367,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))
@@ -401,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))
@@ -809,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))
@@ -835,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))
@@ -980,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
+14 -8
View File
@@ -80,7 +80,7 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo
mobs_found += found
if(!ai_found && isAI(found))
ai_found = 1
msg += "<b><font color='black'>[original_word] </font></b> "
msg += "<b><span class='adminnotice'>[original_word] </span></b> "
continue
msg += "[original_word] "
@@ -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)
+2 -2
View File
@@ -117,7 +117,7 @@
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"
@@ -134,7 +134,7 @@
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"
+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
+1 -20
View File
@@ -168,7 +168,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
#endif
/client/proc/callproc_datum(var/A as null|area|mob|obj|turf)
set category = "Debug"
set category = null
set name = "Atom ProcCall"
if(!check_rights(R_PROCCALL))
@@ -418,25 +418,6 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
else
alert("Invalid mob")
//TODO: merge the vievars version into this or something maybe mayhaps
/client/proc/cmd_debug_del_all()
set category = "Debug"
set name = "Del-All"
if(!check_rights(R_DEBUG))
return
// to prevent REALLY stupid deletions
var/blocked = list(/mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/dead, /mob/dead/observer, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai)
var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in subtypesof(/obj) + subtypesof(/mob) - blocked
if(hsbitem)
for(var/atom/O in world)
if(istype(O, hsbitem))
qdel(O)
log_admin("[key_name(src)] has deleted all instances of [hsbitem].")
message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0)
feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_debug_del_sing()
set category = "Debug"
set name = "Del Singulo / Tesla"
+1 -1
View File
@@ -108,7 +108,7 @@
/client/proc/reload_admins()
set name = "Reload Admins"
set category = "Debug"
set category = "Server"
if(!check_rights(R_SERVER))
return
+2 -3
View File
@@ -39,6 +39,7 @@
if(alert("Do you want these characters automatically classified as antagonists?",,"Yes","No")=="Yes")
is_syndicate = 1
var/datum/outfit/O = outfit_list[dresscode]
var/list/players_to_spawn = list()
if(pick_manually)
var/list/possible_ghosts = list()
@@ -52,14 +53,12 @@
players_to_spawn += candidate
else
to_chat(src, "Polling candidates...")
players_to_spawn = pollCandidates("Do you want to play as an event character?")
players_to_spawn = SSghost_spawns.poll_candidates("Do you want to play as \a [O.name]?")
if(!players_to_spawn.len)
to_chat(src, "Nobody volunteered.")
return 0
var/datum/outfit/O = outfit_list[dresscode]
var/players_spawned = 0
for(var/mob/thisplayer in players_to_spawn)
var/mob/living/carbon/human/H = new /mob/living/carbon/human(T)
@@ -55,7 +55,8 @@ GLOBAL_VAR_INIT(sent_syndicate_infiltration_team, 0)
infiltrators += candidate
else
to_chat(src, "Polling candidates...")
infiltrators = pollCandidates("Do you want to play as a SYNDICATE INFILTRATOR?", ROLE_TRAITOR, 1)
var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "syndicate")
infiltrators = SSghost_spawns.poll_candidates("Do you want to play as a SYNDICATE INFILTRATOR?", ROLE_TRAITOR, TRUE, source = ma)
if(!infiltrators.len)
to_chat(src, "Nobody volunteered.")
@@ -2,8 +2,9 @@
set category = "Debug"
set name = "Map template - Place"
if(!holder)
if(!check_rights(R_DEBUG))
return
var/datum/map_template/template
var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in GLOB.map_templates
@@ -36,6 +37,9 @@
set category = "Debug"
set name = "Map Template - Upload"
if(!check_rights(R_DEBUG))
return
var/map = input(usr, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file
if(!map)
return
+2 -1
View File
@@ -137,7 +137,8 @@ client/proc/one_click_antag()
var/confirm = alert("Are you sure?", "Confirm creation", "Yes", "No")
if(confirm != "Yes")
return 0
var/list/candidates = pollCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard")
var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "wizard")
var/list/candidates = SSghost_spawns.poll_candidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard", source = ma)
log_admin("[key_name(owner)] tried making a Wizard with One-Click-Antag")
message_admins("[key_name_admin(owner)] tried making a Wizard with One-Click-Antag")
+6
View File
@@ -2,6 +2,9 @@
set name = "Possess Obj"
set category = null
if(!check_rights(R_POSSESS))
return
if(istype(O,/obj/singularity))
if(config.forbid_singulo_possession)
to_chat(usr, "It is forbidden to possess singularities.")
@@ -35,6 +38,9 @@
set category = null
//usr.loc = get_turf(usr)
if(!check_rights(R_POSSESS))
return
if(usr.control_object && usr.name_archive) //if you have a name archived and if you are actually relassing an object
usr.real_name = usr.name_archive
usr.name = usr.real_name
+7 -7
View File
@@ -51,7 +51,7 @@
if(!ismob(M))
return
if(!check_rights(R_SERVER|R_EVENT))
if(!check_rights(R_EVENT))
return
var/msg = clean_input("Message:", text("Subtle PM to [M.key]"))
@@ -123,7 +123,7 @@
feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE
set category = "Event"
set category = null
set name = "Direct Narrate"
if(!check_rights(R_SERVER|R_EVENT))
@@ -158,7 +158,7 @@
/client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null)
var/mob/living/carbon/human/H = M
if(!check_rights(R_ADMIN))
if(!check_rights(R_EVENT))
return
if(!istype(H))
@@ -572,7 +572,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_rejuvenate(mob/living/M as mob in GLOB.mob_list)
set category = "Event"
set category = null
set name = "Rejuvenate"
if(!check_rights(R_REJUVINATE))
@@ -638,7 +638,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in view())
set category = "Admin"
set category = null
set name = "Delete"
if(!check_rights(R_ADMIN))
@@ -1078,12 +1078,12 @@ Traitors and the like can also be revived with the previous role mostly intact.
message_admins("Admin [key_name_admin(usr)] has disabled ERT calling.", 1)
/client/proc/show_tip()
set category = "Admin"
set category = "Event"
set name = "Show Custom Tip"
set desc = "Sends a tip (that you specify) to all players. After all \
you're the experienced player here."
if(!check_rights(R_ADMIN))
if(!check_rights(R_EVENT))
return
var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null
@@ -1,30 +0,0 @@
/client/proc/spawn_floor_cluwne()
set category = "Event"
set name = "Unleash Floor Cluwne"
set desc = "Pick a specific target or just let it select randomly and spawn the floor cluwne mob on the station. Be warned: spawning more than one may cause issues!"
var/mob/living/carbon/human/target
if(!check_rights(R_EVENT))
return
var/confirm = alert("Are you sure you want to release a floor cluwne and kill a lot of people?", "Confirm Massacre", "Yes", "No")
if(confirm == "Yes")
var/turf/T = get_turf(usr)
var/list/potential_targets = list()
for(var/mob/M in GLOB.player_list)
var/mob/living/carbon/human/H = M
if(!istype(H))
continue
if(H.mind.assigned_role == "Cluwne")
continue
potential_targets += H
if(!potential_targets.len) //You're probably the only player on this damn station, spawn it yourself
to_chat(src, "<span class='notice'>No valid targets!</span>")
return
target = input("Any specific target in mind? Please note only live, non cluwned, human targets are valid.", "Target", target) as null|anything in potential_targets
var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T)
if(target)
FC.Acquire_Victim(target)
log_admin("[key_name(usr)] spawned floor cluwne[target ? ", initially targetting [target]": null].")
message_admins("[key_name(usr)] spawned floor cluwne[target ? ", initially targetting [target]": null].")
+2 -1
View File
@@ -37,7 +37,8 @@ GLOBAL_VAR_INIT(sent_strike_team, 0)
break
// Find ghosts willing to be DS
var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE)
var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_deathsquad")
var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE, source = source)
if(!commando_ghosts.len)
to_chat(usr, "<span class='userdanger'>Nobody volunteered to join the DeathSquad.</span>")
return
@@ -45,7 +45,7 @@ GLOBAL_VAR_INIT(sent_syndicate_strike_team, 0)
break
// Find ghosts willing to be SST
var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE)
var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE)
if(!commando_ghosts.len)
to_chat(usr, "<span class='userdanger'>Nobody volunteered to join the SST.</span>")
return
@@ -73,7 +73,7 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/replace_banned_player()
set waitfor = FALSE
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [name]?", job_rank, TRUE, 50)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [name]?", job_rank, TRUE, 5 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
@@ -46,7 +46,8 @@
checking = TRUE
to_chat(user, "<span class='notice'>You activate [src] and wait for confirmation.</span>")
var/list/nuke_candidates = pollCandidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 150)
var/mutable_appearance/ma = new('icons/mob/simple_human.dmi', "syndicate_space_sword")
var/list/nuke_candidates = SSghost_spawns.poll_candidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 15 SECONDS, source = ma)
if(LAZYLEN(nuke_candidates))
checking = FALSE
if(QDELETED(src) || !check_usability(user))
@@ -180,7 +181,7 @@
var/type = "slaughter"
if(demon_type == /mob/living/simple_animal/slaughter/laughter)
type = "laughter"
var/list/candidates = pollCandidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, 1, 100)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, TRUE, 10 SECONDS, source = demon_type)
if(candidates.len > 0)
var/mob/C = pick(candidates)
@@ -252,7 +253,7 @@
used = TRUE
to_chat(user, "<span class='notice'>You break the seal on the bottle, calling upon the dire sludge to awaken...</span>")
var/list/candidates = pollCandidates("Do you want to play as a magical morph awakened by [user.real_name]?", ROLE_MORPH, 1, 100)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a magical morph awakened by [user.real_name]?", ROLE_MORPH, 1, 10 SECONDS, source = morph_type)
if(candidates.len > 0)
var/mob/C = pick(candidates)
@@ -193,7 +193,7 @@
servant_mind.objectives += O
servant_mind.transfer_to(H)
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 300)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 30 SECONDS, source = H)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant")
@@ -4,10 +4,18 @@
name = "Mission Briefing"
info = "To the Magnificent Z.A.P.<BR>A small mining base has been created within our territory by wandless scum. Send them a message from the wizard federation they will not forget. I know your kind is rather fragile, but a group of lightly armed miners should not pose any threat to you at all. Just be warned they have a security cyborg for self defence, you might want to tune your spells to that threat. I look forward to hearing of your success.<BR>Grand Magus Abra the Wonderous"
/obj/item/spellbook/oneuse/emp
spell = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech
spellname = "Disable Technology"
icon_state = "bookcharge" //it's a lightning bolt, seems appropriate enough
desc = "For the tech-hating wizard on the go."
/obj/item/spellbook/oneuse/emp/used
used = TRUE //spawns used
/obj/effect/spawner/lootdrop/wizardcrash
loot = list(
/obj/item/guardiancreator = 1, //jackpot.
/obj/item/guardiancreator = 1, //jackpot.
/obj/item/spellbook/oneuse/knock = 1, //tresspassing charges incoming
/obj/item/gun/magic/wand/resurrection = 1, //medbay's best friend
/obj/item/spellbook/oneuse/charge = 20, //and now for less useful stuff to dilute the good loot chances
@@ -30,7 +30,8 @@
set name = "Show/Hide RadioChatter"
set category = "Preferences"
set desc = "Toggle seeing radiochatter from radios and speakers"
if(!holder) return
if(!check_rights(R_ADMIN))
return
prefs.toggles ^= CHAT_RADIO
prefs.save_preferences(src)
to_chat(usr, "You will [(prefs.toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from radios or speakers")
@@ -49,7 +50,8 @@
set name = "Hear/Silence Admin Bwoinks"
set category = "Preferences"
set desc = "Toggle hearing a notification when admin PMs are recieved"
if(!holder) return
if(!check_rights(R_ADMIN))
return
prefs.sound ^= SOUND_ADMINHELP
prefs.save_preferences(src)
to_chat(usr, "You will [(prefs.sound & SOUND_ADMINHELP) ? "now" : "no longer"] hear a sound when adminhelps arrive.")
@@ -59,7 +61,7 @@
set name = "Hear/Silence Mentorhelp Bwoinks"
set category = "Preferences"
set desc = "Toggle hearing a notification when mentorhelps are recieved"
if(!holder)
if(!check_rights(R_ADMIN|R_MENTOR))
return
prefs.sound ^= SOUND_MENTORHELP
prefs.save_preferences(src)
@@ -296,7 +298,7 @@
to_chat(src, "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTPDA) ? "see all PDA messages" : "no longer see PDA messages"].")
prefs.save_preferences(src)
feedback_add_details("admin_verb","TGP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/verb/silence_current_midi()
set name = "Silence Current Midi"
set category = "Preferences"
@@ -838,3 +838,10 @@
icon_state = "cuban_suit"
item_state = "cuban_suit"
item_color = "cuban_suit"
/obj/item/clothing/under/tourist_suit
name = "tourist outfit"
desc = "A light blue shirt with brown shorts. Feels oddly spooky."
icon_state = "tourist"
icon_state = "tourist"
item_color = "tourist"
+1 -1
View File
@@ -9,7 +9,7 @@
makeAbductorTeam()
/datum/event/abductor/proc/makeAbductorTeam()
var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for an Abductor Team?", ROLE_ABDUCTOR, 1)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you wish to be considered for an Abductor Team?", ROLE_ABDUCTOR, TRUE)
if(candidates.len >= 2)
//Oh god why we can't have static functions
+1 -1
View File
@@ -23,7 +23,7 @@
vents += temp_vent
spawn()
var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an alien?", ROLE_ALIEN, TRUE, source = /mob/living/carbon/alien/larva)
while(spawncount > 0 && vents.len && candidates.len)
var/obj/vent = pick_n_take(vents)
+3 -3
View File
@@ -12,14 +12,14 @@
if(!T)
return kill()
var/list/candidates = pollCandidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, 1)
if(!candidates.len)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, TRUE, source = /mob/living/simple_animal/mouse/blobinfected)
if(!length(candidates))
return kill()
var/list/vents = list()
for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in GLOB.all_vent_pumps)
if(is_station_level(temp_vent.loc.z) && !temp_vent.welded)
if(temp_vent.parent.other_atmosmch.len > 50)
if(length(temp_vent.parent.other_atmosmch) > 50)
vents += temp_vent
var/obj/vent = pick(vents)
+2 -2
View File
@@ -1,9 +1,9 @@
/client/proc/forceEvent(var/type in SSevents.allEvents)
set name = "Trigger Event (Debug Only)"
set name = "Trigger Event"
set category = "Debug"
if(!holder)
if(!check_rights(R_EVENT))
return
if(ispath(type))
+1 -2
View File
@@ -3,8 +3,7 @@
/datum/event/sentience/start()
processing = FALSE //so it won't fire again in next tick
var/ghostmsg = "Do you want to awaken as a sentient being?"
var/list/candidates = pollCandidates(ghostmsg, ROLE_SENTIENT, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to awaken as a sentient being?", ROLE_SENTIENT, TRUE)
var/list/potential = list()
var/sentience_type = SENTIENCE_ORGANIC
+1 -1
View File
@@ -3,7 +3,7 @@
/datum/event/spawn_slaughter/proc/get_slaughter(var/end_if_fail = 0)
spawn()
var/list/candidates = pollCandidates("Do you want to play as a slaughter demon?", ROLE_DEMON, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, source = /mob/living/simple_animal/slaughter)
if(!candidates.len)
key_of_slaughter = null
return kill()
+1 -1
View File
@@ -34,7 +34,7 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol"))
trader_objectives = forge_trader_objectives()
spawn()
var/list/candidates = pollCandidates("Do you want to play as a trader?", ROLE_TRADER, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a Sol Trader?", ROLE_TRADER, TRUE)
var/index = 1
while(spawn_count > 0 && candidates.len > 0)
if(index > spawnlocs.len)
File diff suppressed because it is too large Load Diff
+2 -4
View File
@@ -3,8 +3,7 @@
set desc = "Permamently deletes a book from the database."
set category = "Admin"
if(!holder)
to_chat(src, "Only administrators may use this command.")
if(!check_rights(R_ADMIN))
return
var/isbn = input("ISBN number?", "Delete Book") as num | null
@@ -25,8 +24,7 @@
set desc = "View books flagged for content."
set category = "Admin"
if(!holder)
to_chat(src, "Only administrators may use this command.")
if(!check_rights(R_ADMIN))
return
holder.view_flagged_books()
@@ -53,7 +53,6 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
if(ismob(body))
T = get_turf(body) //Where is the body located?
attack_log_old = body.attack_log_old //preserve our attack logs by copying them to our ghost
logs = body.logs.Copy()
var/mutable_appearance/MA = copy_appearance(body)
if(body.mind && body.mind.name)
+4 -4
View File
@@ -47,7 +47,7 @@
. = trim(. + trim(msg))
. += "\""
/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency)
/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE)
if(!client)
return 0
@@ -74,7 +74,7 @@
return 0
var/speaker_name = speaker.name
if(ishuman(speaker))
if(use_voice && ishuman(speaker))
var/mob/living/carbon/human/H = speaker
speaker_name = H.GetVoice()
@@ -99,9 +99,9 @@
if(speaker == src)
to_chat(src, "<span class='warning'>You cannot hear yourself speak!</span>")
else
to_chat(src, "<span class='name'>[speaker_name]</span>[speaker.GetAltName()] talks but you cannot hear [speaker.p_them()].")
to_chat(src, "<span class='name'>[speaker.name]</span> talks but you cannot hear [speaker.p_them()].")
else
to_chat(src, "<span class='game say'><span class='name'>[speaker_name]</span>[speaker.GetAltName()] [track][message]</span>")
to_chat(src, "<span class='game say'><span class='name'>[speaker_name]</span>[use_voice ? speaker.GetAltName() : ""] [track][message]</span>")
if(speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z))
var/turf/source = speaker? get_turf(speaker) : get_turf(src)
playsound_local(source, speech_sound, sound_vol, 1, sound_frequency)

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