Merge remote-tracking branch 'citadel/master' into combat_v7
This commit is contained in:
@@ -614,7 +614,7 @@
|
||||
used_key_list[input_key] = 1
|
||||
return input_key
|
||||
|
||||
#if DM_VERSION > 513
|
||||
#if DM_VERSION > 514
|
||||
#error Remie said that lummox was adding a way to get a lists
|
||||
#error contents via list.values, if that is true remove this
|
||||
#error otherwise, update the version and bug lummox
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
|
||||
Here's how to use the chat system with configs
|
||||
|
||||
send2adminchat is a simple function that broadcasts to admin channels
|
||||
|
||||
send2chat is a bit verbose but can be very specific
|
||||
|
||||
The second parameter is a string, this string should be read from a config.
|
||||
What this does is dictacte which TGS4 channels can be sent to.
|
||||
|
||||
For example if you have the following channels in tgs4 set up
|
||||
- Channel 1, Tag: asdf
|
||||
- Channel 2, Tag: bombay,asdf
|
||||
- Channel 3, Tag: Hello my name is asdf
|
||||
- Channel 4, No Tag
|
||||
- Channel 5, Tag: butts
|
||||
|
||||
and you make the call:
|
||||
|
||||
send2chat("I sniff butts", CONFIG_GET(string/where_to_send_sniff_butts))
|
||||
|
||||
and the config option is set like:
|
||||
|
||||
WHERE_TO_SEND_SNIFF_BUTTS asdf
|
||||
|
||||
It will be sent to channels 1 and 2
|
||||
|
||||
Alternatively if you set the config option to just:
|
||||
|
||||
WHERE_TO_SEND_SNIFF_BUTTS
|
||||
|
||||
it will be sent to all connected chats.
|
||||
|
||||
In TGS3 it will always be sent to all connected designated game chats.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sends a message to TGS chat channels.
|
||||
*
|
||||
* message - The message to send.
|
||||
* channel_tag - Required. If "", the message with be sent to all connected (Game-type for TGS3) channels. Otherwise, it will be sent to TGS4 channels with that tag (Delimited by ','s).
|
||||
*/
|
||||
/proc/send2chat(message, channel_tag)
|
||||
if(channel_tag == null || !world.TgsAvailable())
|
||||
return
|
||||
|
||||
var/datum/tgs_version/version = world.TgsVersion()
|
||||
if(channel_tag == "" || version.suite == 3)
|
||||
world.TgsTargetedChatBroadcast(message, FALSE)
|
||||
return
|
||||
|
||||
var/list/channels_to_use = list()
|
||||
for(var/I in world.TgsChatChannelInfo())
|
||||
var/datum/tgs_chat_channel/channel = I
|
||||
var/list/applicable_tags = splittext(channel.custom_tag, ",")
|
||||
if(channel_tag in applicable_tags)
|
||||
channels_to_use += channel
|
||||
|
||||
if(channels_to_use.len)
|
||||
world.TgsChatBroadcast(message, channels_to_use)
|
||||
|
||||
/**
|
||||
* Sends a message to TGS admin chat channels.
|
||||
*
|
||||
* category - The category of the mssage.
|
||||
* message - The message to send.
|
||||
*/
|
||||
/proc/send2adminchat(category, message, embed_links = FALSE)
|
||||
category = replacetext(replacetext(category, "\proper", ""), "\improper", "")
|
||||
message = replacetext(replacetext(message, "\proper", ""), "\improper", "")
|
||||
// if(!embed_links)
|
||||
// message = GLOB.has_discord_embeddable_links.Replace(replacetext(message, "`", ""), " ```$1``` ")
|
||||
world.TgsTargetedChatBroadcast("[category] | [message]", TRUE)
|
||||
@@ -0,0 +1,319 @@
|
||||
#define ICON_NOT_SET "Not Set"
|
||||
|
||||
//This is stored as a nested list instead of datums or whatever because it json encodes nicely for usage in tgui
|
||||
GLOBAL_LIST_INIT(master_filter_info, list(
|
||||
"alpha" = list(
|
||||
"defaults" = list(
|
||||
"x" = 0,
|
||||
"y" = 0,
|
||||
"icon" = ICON_NOT_SET,
|
||||
"render_source" = "",
|
||||
"flags" = 0
|
||||
),
|
||||
"flags" = list(
|
||||
"MASK_INVERSE" = MASK_INVERSE,
|
||||
"MASK_SWAP" = MASK_SWAP
|
||||
)
|
||||
),
|
||||
"angular_blur" = list(
|
||||
"defaults" = list(
|
||||
"x" = 0,
|
||||
"y" = 0,
|
||||
"size" = 1
|
||||
)
|
||||
),
|
||||
/* Not supported because making a proper matrix editor on the frontend would be a huge dick pain.
|
||||
Uncomment if you ever implement it
|
||||
"color" = list(
|
||||
"defaults" = list(
|
||||
"color" = matrix(),
|
||||
"space" = FILTER_COLOR_RGB
|
||||
)
|
||||
),
|
||||
*/
|
||||
"displace" = list(
|
||||
"defaults" = list(
|
||||
"x" = 0,
|
||||
"y" = 0,
|
||||
"size" = null,
|
||||
"icon" = ICON_NOT_SET,
|
||||
"render_source" = ""
|
||||
)
|
||||
),
|
||||
"drop_shadow" = list(
|
||||
"defaults" = list(
|
||||
"x" = 1,
|
||||
"y" = -1,
|
||||
"size" = 1,
|
||||
"offset" = 0,
|
||||
"color" = COLOR_HALF_TRANSPARENT_BLACK
|
||||
)
|
||||
),
|
||||
"blur" = list(
|
||||
"defaults" = list(
|
||||
"size" = 1
|
||||
)
|
||||
),
|
||||
"layer" = list(
|
||||
"defaults" = list(
|
||||
"x" = 0,
|
||||
"y" = 0,
|
||||
"icon" = ICON_NOT_SET,
|
||||
"render_source" = "",
|
||||
"flags" = FILTER_OVERLAY,
|
||||
"color" = "",
|
||||
"transform" = null,
|
||||
"blend_mode" = BLEND_DEFAULT
|
||||
)
|
||||
),
|
||||
"motion_blur" = list(
|
||||
"defaults" = list(
|
||||
"x" = 0,
|
||||
"y" = 0
|
||||
)
|
||||
),
|
||||
"outline" = list(
|
||||
"defaults" = list(
|
||||
"size" = 0,
|
||||
"color" = COLOR_BLACK,
|
||||
"flags" = NONE
|
||||
),
|
||||
"flags" = list(
|
||||
"OUTLINE_SHARP" = OUTLINE_SHARP,
|
||||
"OUTLINE_SQUARE" = OUTLINE_SQUARE
|
||||
)
|
||||
),
|
||||
"radial_blur" = list(
|
||||
"defaults" = list(
|
||||
"x" = 0,
|
||||
"y" = 0,
|
||||
"size" = 0.01
|
||||
)
|
||||
),
|
||||
"rays" = list(
|
||||
"defaults" = list(
|
||||
"x" = 0,
|
||||
"y" = 0,
|
||||
"size" = 16,
|
||||
"color" = COLOR_WHITE,
|
||||
"offset" = 0,
|
||||
"density" = 10,
|
||||
"threshold" = 0.5,
|
||||
"factor" = 0,
|
||||
"flags" = FILTER_OVERLAY | FILTER_UNDERLAY
|
||||
),
|
||||
"flags" = list(
|
||||
"FILTER_OVERLAY" = FILTER_OVERLAY,
|
||||
"FILTER_UNDERLAY" = FILTER_UNDERLAY
|
||||
)
|
||||
),
|
||||
"ripple" = list(
|
||||
"defaults" = list(
|
||||
"x" = 0,
|
||||
"y" = 0,
|
||||
"size" = 1,
|
||||
"repeat" = 2,
|
||||
"radius" = 0,
|
||||
"falloff" = 1,
|
||||
"flags" = NONE
|
||||
),
|
||||
"flags" = list(
|
||||
"WAVE_BOUNDED" = WAVE_BOUNDED
|
||||
)
|
||||
),
|
||||
"wave" = list(
|
||||
"defaults" = list(
|
||||
"x" = 0,
|
||||
"y" = 0,
|
||||
"size" = 1,
|
||||
"offset" = 0,
|
||||
"flags" = NONE
|
||||
),
|
||||
"flags" = list(
|
||||
"WAVE_SIDEWAYS" = WAVE_SIDEWAYS,
|
||||
"WAVE_BOUNDED" = WAVE_BOUNDED
|
||||
)
|
||||
)
|
||||
))
|
||||
|
||||
#undef ICON_NOT_SET
|
||||
|
||||
//Helpers to generate lists for filter helpers
|
||||
//This is the only practical way of writing these that actually produces sane lists
|
||||
/proc/alpha_mask_filter(x, y, icon/icon, render_source, flags)
|
||||
. = list("type" = "alpha")
|
||||
if(!isnull(x))
|
||||
.["x"] = x
|
||||
if(!isnull(y))
|
||||
.["y"] = y
|
||||
if(!isnull(icon))
|
||||
.["icon"] = icon
|
||||
if(!isnull(render_source))
|
||||
.["render_source"] = render_source
|
||||
if(!isnull(flags))
|
||||
.["flags"] = flags
|
||||
|
||||
/proc/angular_blur_filter(x, y, size)
|
||||
. = list("type" = "angular_blur")
|
||||
if(!isnull(x))
|
||||
.["x"] = x
|
||||
if(!isnull(y))
|
||||
.["y"] = y
|
||||
if(!isnull(size))
|
||||
.["size"] = size
|
||||
|
||||
/proc/color_matrix_filter(matrix/in_matrix, space)
|
||||
. = list("type" = "color")
|
||||
.["color"] = in_matrix
|
||||
if(!isnull(space))
|
||||
.["space"] = space
|
||||
|
||||
/proc/displacement_map_filter(icon, render_source, x, y, size = 32)
|
||||
. = list("type" = "displace")
|
||||
if(!isnull(icon))
|
||||
.["icon"] = icon
|
||||
if(!isnull(render_source))
|
||||
.["render_source"] = render_source
|
||||
if(!isnull(x))
|
||||
.["x"] = x
|
||||
if(!isnull(y))
|
||||
.["y"] = y
|
||||
if(!isnull(size))
|
||||
.["size"] = size
|
||||
|
||||
/proc/drop_shadow_filter(x, y, size, offset, color)
|
||||
. = list("type" = "drop_shadow")
|
||||
if(!isnull(x))
|
||||
.["x"] = x
|
||||
if(!isnull(y))
|
||||
.["y"] = y
|
||||
if(!isnull(size))
|
||||
.["size"] = size
|
||||
if(!isnull(offset))
|
||||
.["offset"] = offset
|
||||
if(!isnull(color))
|
||||
.["color"] = color
|
||||
|
||||
/proc/gauss_blur_filter(size)
|
||||
. = list("type" = "blur")
|
||||
if(!isnull(size))
|
||||
.["size"] = size
|
||||
|
||||
/proc/layering_filter(icon, render_source, x, y, flags, color, transform, blend_mode)
|
||||
. = list("type" = "layer")
|
||||
if(!isnull(icon))
|
||||
.["icon"] = icon
|
||||
if(!isnull(render_source))
|
||||
.["render_source"] = render_source
|
||||
if(!isnull(x))
|
||||
.["x"] = x
|
||||
if(!isnull(y))
|
||||
.["y"] = y
|
||||
if(!isnull(color))
|
||||
.["color"] = color
|
||||
if(!isnull(flags))
|
||||
.["flags"] = flags
|
||||
if(!isnull(transform))
|
||||
.["transform"] = transform
|
||||
if(!isnull(blend_mode))
|
||||
.["blend_mode"] = blend_mode
|
||||
|
||||
/proc/motion_blur_filter(x, y)
|
||||
. = list("type" = "motion_blur")
|
||||
if(!isnull(x))
|
||||
.["x"] = x
|
||||
if(!isnull(y))
|
||||
.["y"] = y
|
||||
|
||||
/proc/outline_filter(size, color, flags)
|
||||
. = list("type" = "outline")
|
||||
if(!isnull(size))
|
||||
.["size"] = size
|
||||
if(!isnull(color))
|
||||
.["color"] = color
|
||||
if(!isnull(flags))
|
||||
.["flags"] = flags
|
||||
|
||||
/proc/radial_blur_filter(size, x, y)
|
||||
. = list("type" = "radial_blur")
|
||||
if(!isnull(size))
|
||||
.["size"] = size
|
||||
if(!isnull(x))
|
||||
.["x"] = x
|
||||
if(!isnull(y))
|
||||
.["y"] = y
|
||||
|
||||
/proc/rays_filter(size, color, offset, density, threshold, factor, x, y, flags)
|
||||
. = list("type" = "rays")
|
||||
if(!isnull(size))
|
||||
.["size"] = size
|
||||
if(!isnull(color))
|
||||
.["color"] = color
|
||||
if(!isnull(offset))
|
||||
.["offset"] = offset
|
||||
if(!isnull(density))
|
||||
.["density"] = density
|
||||
if(!isnull(threshold))
|
||||
.["threshold"] = threshold
|
||||
if(!isnull(factor))
|
||||
.["factor"] = factor
|
||||
if(!isnull(x))
|
||||
.["x"] = x
|
||||
if(!isnull(y))
|
||||
.["y"] = y
|
||||
if(!isnull(flags))
|
||||
.["flags"] = flags
|
||||
|
||||
/proc/ripple_filter(radius, size, falloff, repeat, x, y, flags)
|
||||
. = list("type" = "ripple")
|
||||
if(!isnull(radius))
|
||||
.["radius"] = radius
|
||||
if(!isnull(size))
|
||||
.["size"] = size
|
||||
if(!isnull(falloff))
|
||||
.["falloff"] = falloff
|
||||
if(!isnull(repeat))
|
||||
.["repeat"] = repeat
|
||||
if(!isnull(flags))
|
||||
.["flags"] = flags
|
||||
if(!isnull(x))
|
||||
.["x"] = x
|
||||
if(!isnull(y))
|
||||
.["y"] = y
|
||||
|
||||
/proc/wave_filter(x, y, size, offset, flags)
|
||||
. = list("type" = "wave")
|
||||
if(!isnull(size))
|
||||
.["size"] = size
|
||||
if(!isnull(x))
|
||||
.["x"] = x
|
||||
if(!isnull(y))
|
||||
.["y"] = y
|
||||
if(!isnull(offset))
|
||||
.["offset"] = offset
|
||||
if(!isnull(flags))
|
||||
.["flags"] = flags
|
||||
|
||||
/proc/apply_wibbly_filters(atom/in_atom, length)
|
||||
for(var/i in 1 to 7)
|
||||
//This is a very baffling and strange way of doing this but I am just preserving old functionality
|
||||
var/X
|
||||
var/Y
|
||||
var/rsq
|
||||
do
|
||||
X = 60*rand() - 30
|
||||
Y = 60*rand() - 30
|
||||
rsq = X*X + Y*Y
|
||||
while(rsq<100 || rsq>900) // Yeah let's just loop infinitely due to bad luck what's the worst that could happen?
|
||||
var/random_roll = rand()
|
||||
in_atom.add_filter("wibbly-[i]", 5, wave_filter(x = X, y = Y, size = rand() * 2.5 + 0.5, offset = random_roll))
|
||||
var/filter = in_atom.get_filter("wibbly-[i]")
|
||||
animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL)
|
||||
animate(offset = random_roll - 1, time = rand() * 20 + 10)
|
||||
|
||||
/proc/remove_wibbly_filters(atom/in_atom)
|
||||
var/filter
|
||||
for(var/i in 1 to 7)
|
||||
filter = in_atom.get_filter("wibbly-[i]")
|
||||
animate(filter)
|
||||
in_atom.remove_filter("wibbly-[i]")
|
||||
+235
-87
@@ -1,81 +1,102 @@
|
||||
#define POPCOUNT_SURVIVORS "survivors" //Not dead at roundend
|
||||
#define POPCOUNT_ESCAPEES "escapees" //Not dead and on centcom/shuttles marked as escaped
|
||||
#define POPCOUNT_SHUTTLE_ESCAPEES "shuttle_escapees" //Emergency shuttle only.
|
||||
#define POPCOUNT_SURVIVORS "survivors" //Not dead at roundend
|
||||
#define POPCOUNT_ESCAPEES "escapees" //Not dead and on centcom/shuttles marked as escaped
|
||||
#define POPCOUNT_SHUTTLE_ESCAPEES "shuttle_escapees" //Emergency shuttle only.
|
||||
#define PERSONAL_LAST_ROUND "personal last round"
|
||||
#define SERVER_LAST_ROUND "server last round"
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/gather_roundend_feedback()
|
||||
var/datum/station_state/end_state = new /datum/station_state()
|
||||
end_state.count()
|
||||
station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100)
|
||||
gather_antag_data()
|
||||
record_nuke_disk_location()
|
||||
var/json_file = file("[GLOB.log_directory]/round_end_data.json")
|
||||
// All but npcs sublists and ghost category contain only mobs with minds
|
||||
var/list/file_data = list("escapees" = list("humans" = list(), "silicons" = list(), "others" = list(), "npcs" = list()), "abandoned" = list("humans" = list(), "silicons" = list(), "others" = list(), "npcs" = list()), "ghosts" = list(), "additional data" = list())
|
||||
var/num_survivors = 0
|
||||
var/num_escapees = 0
|
||||
var/num_shuttle_escapees = 0
|
||||
var/num_survivors = 0 //Count of non-brain non-camera mobs with mind that are alive
|
||||
var/num_escapees = 0 //Above and on centcom z
|
||||
var/num_shuttle_escapees = 0 //Above and on escape shuttle
|
||||
var/list/area/shuttle_areas
|
||||
if(SSshuttle && SSshuttle.emergency)
|
||||
if(SSshuttle?.emergency)
|
||||
shuttle_areas = SSshuttle.emergency.shuttle_areas
|
||||
for(var/mob/m in GLOB.mob_list)
|
||||
var/escaped
|
||||
var/category
|
||||
|
||||
for(var/mob/M in GLOB.mob_list)
|
||||
var/list/mob_data = list()
|
||||
if(isnewplayer(m))
|
||||
if(isnewplayer(M))
|
||||
continue
|
||||
if (m.client && m.client.prefs && m.client.prefs.auto_ooc)
|
||||
if (!(m.client.prefs.chat_toggles & CHAT_OOC))
|
||||
m.client.prefs.chat_toggles ^= CHAT_OOC
|
||||
if(m.mind)
|
||||
if(m.stat != DEAD && !isbrain(m) && !iscameramob(m))
|
||||
// enable their ooc?
|
||||
if (M.client?.prefs?.auto_ooc)
|
||||
if (!(M.client.prefs.chat_toggles & CHAT_OOC))
|
||||
M.client.prefs.chat_toggles ^= CHAT_OOC
|
||||
|
||||
var/escape_status = "abandoned" //default to abandoned
|
||||
var/category = "npcs" //Default to simple count only bracket
|
||||
var/count_only = TRUE //Count by name only or full info
|
||||
|
||||
mob_data["name"] = M.name
|
||||
if(M.mind)
|
||||
count_only = FALSE
|
||||
mob_data["ckey"] = M.mind.key
|
||||
if(M.stat != DEAD && !isbrain(M) && !iscameramob(M))
|
||||
num_survivors++
|
||||
mob_data += list("name" = m.name, "ckey" = ckey(m.mind.key))
|
||||
if(isobserver(m))
|
||||
escaped = "ghosts"
|
||||
else if(isliving(m))
|
||||
var/mob/living/L = m
|
||||
mob_data += list("location" = get_area(L), "health" = L.health)
|
||||
if(EMERGENCY_ESCAPED_OR_ENDGAMED && (M.onCentCom() || M.onSyndieBase()))
|
||||
num_escapees++
|
||||
escape_status = "escapees"
|
||||
if(shuttle_areas[get_area(M)])
|
||||
num_shuttle_escapees++
|
||||
if(isliving(M))
|
||||
var/mob/living/L = M
|
||||
mob_data["location"] = get_area(L)
|
||||
mob_data["health"] = L.health
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
category = "humans"
|
||||
mob_data += list("job" = H.mind.assigned_role, "species" = H.dna.species.name)
|
||||
if(H.mind)
|
||||
mob_data["job"] = H.mind.assigned_role
|
||||
else
|
||||
mob_data["job"] = "Unknown"
|
||||
mob_data["species"] = H.dna.species.name
|
||||
else if(issilicon(L))
|
||||
category = "silicons"
|
||||
if(isAI(L))
|
||||
mob_data += list("module" = "AI")
|
||||
if(isAI(L))
|
||||
mob_data += list("module" = "pAI")
|
||||
if(iscyborg(L))
|
||||
mob_data["module"] = "AI"
|
||||
else if(ispAI(L))
|
||||
mob_data["module"] = "pAI"
|
||||
else if(iscyborg(L))
|
||||
var/mob/living/silicon/robot/R = L
|
||||
mob_data += list("module" = R.module)
|
||||
else
|
||||
category = "others"
|
||||
mob_data += list("typepath" = m.type)
|
||||
if(!escaped)
|
||||
if(EMERGENCY_ESCAPED_OR_ENDGAMED && (m.onCentCom() || m.onSyndieBase()))
|
||||
escaped = "escapees"
|
||||
num_escapees++
|
||||
if(shuttle_areas[get_area(m)])
|
||||
num_shuttle_escapees++
|
||||
else
|
||||
escaped = "abandoned"
|
||||
if(!m.mind && (!ishuman(m) || !issilicon(m)))
|
||||
var/list/npc_nest = file_data["[escaped]"]["npcs"]
|
||||
if(npc_nest.Find(initial(m.name)))
|
||||
file_data["[escaped]"]["npcs"]["[initial(m.name)]"] += 1
|
||||
else
|
||||
file_data["[escaped]"]["npcs"]["[initial(m.name)]"] = 1
|
||||
else
|
||||
if(isobserver(m))
|
||||
var/pos = length(file_data["[escaped]"]) + 1
|
||||
file_data["[escaped]"]["[pos]"] = mob_data
|
||||
else
|
||||
if(!category)
|
||||
mob_data["module"] = R.module.name
|
||||
else
|
||||
category = "others"
|
||||
mob_data += list("name" = m.name, "typepath" = m.type)
|
||||
var/pos = length(file_data["[escaped]"]["[category]"]) + 1
|
||||
file_data["[escaped]"]["[category]"]["[pos]"] = mob_data
|
||||
mob_data["typepath"] = M.type
|
||||
//Ghosts don't care about minds, but we want to retain ckey data etc
|
||||
if(isobserver(M))
|
||||
count_only = FALSE
|
||||
escape_status = "ghosts"
|
||||
if(!M.mind)
|
||||
mob_data["ckey"] = M.key
|
||||
category = null //ghosts are one list deep
|
||||
//All other mindless stuff just gets counts by name
|
||||
if(count_only)
|
||||
var/list/npc_nest = file_data["[escape_status]"]["npcs"]
|
||||
var/name_to_use = initial(M.name)
|
||||
if(ishuman(M))
|
||||
name_to_use = "Unknown Human" //Monkeymen and other mindless corpses
|
||||
if(npc_nest.Find(name_to_use))
|
||||
file_data["[escape_status]"]["npcs"][name_to_use] += 1
|
||||
else
|
||||
file_data["[escape_status]"]["npcs"][name_to_use] = 1
|
||||
else
|
||||
//Mobs with minds and ghosts get detailed data
|
||||
if(category)
|
||||
var/pos = length(file_data["[escape_status]"]["[category]"]) + 1
|
||||
file_data["[escape_status]"]["[category]"]["[pos]"] = mob_data
|
||||
else
|
||||
var/pos = length(file_data["[escape_status]"]) + 1
|
||||
file_data["[escape_status]"]["[pos]"] = mob_data
|
||||
|
||||
var/datum/station_state/end_state = new /datum/station_state()
|
||||
end_state.count()
|
||||
station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100)
|
||||
file_data["additional data"]["station integrity"] = station_integrity
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
SSblackbox.record_feedback("nested tally", "round_end_stats", num_survivors, list("survivors", "total"))
|
||||
SSblackbox.record_feedback("nested tally", "round_end_stats", num_escapees, list("escapees", "total"))
|
||||
SSblackbox.record_feedback("nested tally", "round_end_stats", GLOB.joined_player_list.len, list("players", "total"))
|
||||
@@ -169,10 +190,34 @@
|
||||
file_data["wanted"] = list("author" = "[GLOB.news_network.wanted_issue.scannedUser]", "criminal" = "[GLOB.news_network.wanted_issue.criminal]", "description" = "[GLOB.news_network.wanted_issue.body]", "photo file" = "[GLOB.news_network.wanted_issue.photo_file]")
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
///Handles random hardcore point rewarding if it applies.
|
||||
/datum/controller/subsystem/ticker/proc/HandleRandomHardcoreScore(client/player_client)
|
||||
if(!ishuman(player_client.mob))
|
||||
return FALSE
|
||||
var/mob/living/carbon/human/human_mob = player_client.mob
|
||||
if(!human_mob.hardcore_survival_score) ///no score no glory
|
||||
return FALSE
|
||||
|
||||
if(human_mob.mind && (human_mob.mind.special_role || length(human_mob.mind.antag_datums) > 0))
|
||||
var/didthegamerwin = TRUE
|
||||
for(var/a in human_mob.mind.antag_datums)
|
||||
var/datum/antagonist/antag_datum = a
|
||||
for(var/i in antag_datum.objectives)
|
||||
var/datum/objective/objective_datum = i
|
||||
if(!objective_datum.check_completion())
|
||||
didthegamerwin = FALSE
|
||||
if(!didthegamerwin)
|
||||
return FALSE
|
||||
player_client.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score))
|
||||
else if(human_mob.onCentCom())
|
||||
player_client.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score))
|
||||
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/declare_completion()
|
||||
set waitfor = FALSE
|
||||
|
||||
to_chat(world, "<BR><BR><BR><span class='big bold'>The round has ended.</span>")
|
||||
log_game("The round has ended.")
|
||||
if(LAZYLEN(GLOB.round_end_notifiees))
|
||||
world.TgsTargetedChatBroadcast("[GLOB.round_end_notifiees.Join(", ")] the round has ended.", FALSE)
|
||||
|
||||
@@ -186,6 +231,19 @@
|
||||
C.RollCredits()
|
||||
C.playtitlemusic(40)
|
||||
CONFIG_SET(flag/suicide_allowed,TRUE) // EORG suicides allowed
|
||||
|
||||
var/speed_round = FALSE
|
||||
if(world.time - SSticker.round_start_time <= 300 SECONDS)
|
||||
speed_round = TRUE
|
||||
|
||||
for(var/client/C in GLOB.clients)
|
||||
if(!C.credits)
|
||||
C.RollCredits()
|
||||
C.playtitlemusic(40)
|
||||
if(speed_round)
|
||||
C.give_award(/datum/award/achievement/misc/speed_round, C.mob)
|
||||
HandleRandomHardcoreScore(C)
|
||||
|
||||
var/popcount = gather_roundend_feedback()
|
||||
display_report(popcount)
|
||||
|
||||
@@ -204,7 +262,7 @@
|
||||
|
||||
var/survival_rate = GLOB.joined_player_list.len ? "[PERCENT(popcount[POPCOUNT_SURVIVORS]/GLOB.joined_player_list.len)]%" : "there's literally no player"
|
||||
|
||||
send2irc("Server", "A round of [mode.name] just ended[mode_result == "undefined" ? "." : " with a [mode_result]."] Survival rate: [survival_rate]")
|
||||
send2adminchat("Server", "A round of [mode.name] just ended[mode_result == "undefined" ? "." : " with a [mode_result]."] Survival rate: [survival_rate]")
|
||||
|
||||
if(length(CONFIG_GET(keyed_list/cross_server)))
|
||||
send_news_report()
|
||||
@@ -215,6 +273,11 @@
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
// handle_hearts()
|
||||
set_observer_default_invisibility(0, "<span class='warning'>The round is over! You are now visible to the living.</span>")
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
//These need update to actually reflect the real antagonists
|
||||
//Print a list of antagonists to the server log
|
||||
var/list/total_antagonists = list()
|
||||
@@ -233,16 +296,13 @@
|
||||
for(var/antag_name in total_antagonists)
|
||||
var/list/L = total_antagonists[antag_name]
|
||||
log_game("[antag_name]s :[L.Join(", ")].")
|
||||
set_observer_default_invisibility(0, "<span class='warning'>The round is over! You are now visible to the living.</span>")
|
||||
|
||||
CHECK_TICK
|
||||
SSdbcore.SetRoundEnd()
|
||||
//Collects persistence features
|
||||
if(mode.station_was_nuked)
|
||||
SSpersistence.station_was_destroyed = TRUE
|
||||
if(!mode.allow_persistence_save)
|
||||
SSpersistence.station_persistence_save_disabled = TRUE
|
||||
SSpersistence.CollectData()
|
||||
if(mode.allow_persistence_save)
|
||||
SSpersistence.SaveTCGCards()
|
||||
SSpersistence.CollectData()
|
||||
|
||||
//stop collecting feedback during grifftime
|
||||
SSblackbox.Seal()
|
||||
@@ -277,11 +337,15 @@
|
||||
//Antagonists
|
||||
parts += antag_report()
|
||||
|
||||
parts += hardcore_random_report()
|
||||
|
||||
CHECK_TICK
|
||||
//Medals
|
||||
parts += medal_report()
|
||||
//Station Goals
|
||||
parts += goal_report()
|
||||
//Economy & Money
|
||||
parts += market_report()
|
||||
|
||||
listclearnulls(parts)
|
||||
|
||||
@@ -324,9 +388,9 @@
|
||||
parts += "[FOURSPACES]<i>Nobody died this shift!</i>"
|
||||
if(istype(SSticker.mode, /datum/game_mode/dynamic))
|
||||
var/datum/game_mode/dynamic/mode = SSticker.mode
|
||||
mode.update_playercounts()
|
||||
parts += "[FOURSPACES]Final threat level: [mode.threat_level]"
|
||||
parts += "[FOURSPACES]Final threat: [mode.threat]"
|
||||
mode.update_playercounts() // ?
|
||||
parts += "[FOURSPACES]Threat level: [mode.threat_level]"
|
||||
parts += "[FOURSPACES]Threat left: [mode.threat]"
|
||||
parts += "[FOURSPACES]Average threat: [mode.threat_average]"
|
||||
parts += "[FOURSPACES]Executed rules:"
|
||||
for(var/datum/dynamic_ruleset/rule in mode.executed_rules)
|
||||
@@ -343,20 +407,47 @@
|
||||
/client/proc/roundend_report_file()
|
||||
return "data/roundend_reports/[ckey].html"
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/show_roundend_report(client/C, previous = FALSE)
|
||||
/**
|
||||
* Log the round-end report as an HTML file
|
||||
*
|
||||
* Composits the roundend report, and saves it in two locations.
|
||||
* The report is first saved along with the round's logs
|
||||
* Then, the report is copied to a fixed directory specifically for
|
||||
* housing the server's last roundend report. In this location,
|
||||
* the file will be overwritten at the end of each shift.
|
||||
*/
|
||||
/datum/controller/subsystem/ticker/proc/log_roundend_report()
|
||||
var/roundend_file = file("[GLOB.log_directory]/round_end_data.html")
|
||||
var/list/parts = list()
|
||||
parts += "<div class='panel stationborder'>"
|
||||
parts += GLOB.survivor_report
|
||||
parts += "</div>"
|
||||
parts += GLOB.common_report
|
||||
var/content = parts.Join()
|
||||
//Log the rendered HTML in the round log directory
|
||||
fdel(roundend_file)
|
||||
WRITE_FILE(roundend_file, content)
|
||||
//Place a copy in the root folder, to be overwritten each round.
|
||||
roundend_file = file("data/server_last_roundend_report.html")
|
||||
fdel(roundend_file)
|
||||
WRITE_FILE(roundend_file, content)
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/show_roundend_report(client/C, report_type = null)
|
||||
var/datum/browser/roundend_report = new(C, "roundend")
|
||||
roundend_report.width = 800
|
||||
roundend_report.height = 600
|
||||
var/content
|
||||
var/filename = C.roundend_report_file()
|
||||
if(!previous)
|
||||
if(report_type == PERSONAL_LAST_ROUND) //Look at this player's last round
|
||||
content = file2text(filename)
|
||||
else if (report_type == SERVER_LAST_ROUND) //Look at the last round that this server has seen
|
||||
content = file2text("data/server_last_roundend_report.html")
|
||||
else //report_type is null, so make a new report based on the current round and show that to the player
|
||||
var/list/report_parts = list(personal_report(C), GLOB.common_report)
|
||||
content = report_parts.Join()
|
||||
remove_verb(C, /client/proc/show_previous_roundend_report)
|
||||
fdel(filename)
|
||||
text2file(content, filename)
|
||||
else
|
||||
content = file2text(filename)
|
||||
|
||||
roundend_report.set_content(content)
|
||||
roundend_report.stylesheets = list()
|
||||
roundend_report.add_stylesheet("roundend", 'html/browser/roundend.css')
|
||||
@@ -393,8 +484,9 @@
|
||||
/datum/controller/subsystem/ticker/proc/display_report(popcount)
|
||||
GLOB.common_report = build_roundend_report()
|
||||
GLOB.survivor_report = survivor_report(popcount)
|
||||
log_roundend_report()
|
||||
for(var/client/C in GLOB.clients)
|
||||
show_roundend_report(C, FALSE)
|
||||
show_roundend_report(C)
|
||||
give_show_report_button(C)
|
||||
CHECK_TICK
|
||||
|
||||
@@ -412,12 +504,11 @@
|
||||
|
||||
if (aiPlayer.connected_robots.len)
|
||||
var/borg_num = aiPlayer.connected_robots.len
|
||||
var/robolist = "<br><b>[aiPlayer.real_name]</b>'s minions were: "
|
||||
parts += "<br><b>[aiPlayer.real_name]</b>'s minions were:"
|
||||
for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots)
|
||||
borg_num--
|
||||
if(robo.mind)
|
||||
robolist += "<b>[robo.name]</b>[robo.mind.hide_ckey ? "" : " (Played by: <b>[robo.mind.key]</b>)"] [robo.stat == DEAD ? " <span class='redtext'>(Deactivated)</span>" : ""][borg_num ?", ":""]<br>"
|
||||
parts += "[robolist]"
|
||||
parts += "<b>[robo.name]</b>[robo.mind.hide_ckey ? "" : " (Played by: <b>[robo.mind.key]</b>)"] [robo.stat == DEAD ? " <span class='redtext'>(Deactivated)</span>" : ""][borg_num ?", ":""]"
|
||||
if(!borg_spacer)
|
||||
borg_spacer = TRUE
|
||||
|
||||
@@ -444,6 +535,34 @@
|
||||
parts += G.get_result()
|
||||
return "<div class='panel stationborder'><ul>[parts.Join()]</ul></div>"
|
||||
|
||||
///Generate a report for how much money is on station, as well as the richest crewmember on the station.
|
||||
/datum/controller/subsystem/ticker/proc/market_report()
|
||||
var/list/parts = list()
|
||||
parts += "<span class='header'>Station Economic Summary:</span>"
|
||||
///This is the richest account on station at roundend.
|
||||
var/datum/bank_account/mr_moneybags
|
||||
///This is the station's total wealth at the end of the round.
|
||||
var/station_vault = 0
|
||||
///How many players joined the round.
|
||||
var/total_players = GLOB.joined_player_list.len
|
||||
var/list/typecache_bank = typecacheof(list(/datum/bank_account/department, /datum/bank_account/remote))
|
||||
for(var/i in SSeconomy.generated_accounts)
|
||||
var/datum/bank_account/current_acc = SSeconomy.generated_accounts[i]
|
||||
if(typecache_bank[current_acc.type])
|
||||
continue
|
||||
station_vault += current_acc.account_balance
|
||||
if(!mr_moneybags || mr_moneybags.account_balance < current_acc.account_balance)
|
||||
mr_moneybags = current_acc
|
||||
parts += "<div class='panel stationborder'>There were [station_vault] credits collected by crew this shift.<br>"
|
||||
if(total_players > 0)
|
||||
parts += "An average of [station_vault/total_players] credits were collected.<br>"
|
||||
// log_econ("Roundend credit total: [station_vault] credits. Average Credits: [station_vault/total_players]")
|
||||
if(mr_moneybags)
|
||||
parts += "The most affluent crew member at shift end was <b>[mr_moneybags.account_holder] with [mr_moneybags.account_balance]</b> cr!</div>"
|
||||
else
|
||||
parts += "Somehow, nobody made any money this shift! This'll result in some budget cuts...</div>"
|
||||
return parts
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/medal_report()
|
||||
if(GLOB.commendations.len)
|
||||
var/list/parts = list()
|
||||
@@ -453,16 +572,40 @@
|
||||
return "<div class='panel stationborder'>[parts.Join("<br>")]</div>"
|
||||
return ""
|
||||
|
||||
///Generate a report for all players who made it out alive with a hardcore random character and prints their final score
|
||||
/datum/controller/subsystem/ticker/proc/hardcore_random_report()
|
||||
. = list()
|
||||
var/list/hardcores = list()
|
||||
for(var/i in GLOB.player_list)
|
||||
if(!ishuman(i))
|
||||
continue
|
||||
var/mob/living/carbon/human/human_player = i
|
||||
if(!human_player.hardcore_survival_score || !human_player.onCentCom() || human_player.stat == DEAD) ///gotta escape nerd
|
||||
continue
|
||||
if(!human_player.mind)
|
||||
continue
|
||||
hardcores += human_player
|
||||
if(!length(hardcores))
|
||||
return
|
||||
. += "<div class='panel stationborder'><span class='header'>The following people made it out as a random hardcore character:</span>"
|
||||
. += "<ul class='playerlist'>"
|
||||
for(var/mob/living/carbon/human/human_player in hardcores)
|
||||
. += "<li>[printplayer(human_player.mind)] with a hardcore random score of [round(human_player.hardcore_survival_score)]</li>"
|
||||
. += "</ul></div>"
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/antag_report()
|
||||
var/list/result = list()
|
||||
var/list/all_teams = list()
|
||||
var/list/all_antagonists = list()
|
||||
|
||||
// for(var/datum/team/A in GLOB.antagonist_teams)
|
||||
// all_teams |= A
|
||||
|
||||
for(var/datum/antagonist/A in GLOB.antagonists)
|
||||
if(!A.owner)
|
||||
continue
|
||||
all_teams |= A.get_team()
|
||||
all_antagonists += A
|
||||
all_antagonists |= A
|
||||
|
||||
for(var/datum/team/T in all_teams)
|
||||
result += T.roundend_report()
|
||||
@@ -515,9 +658,9 @@
|
||||
|
||||
/datum/action/report/Trigger()
|
||||
if(owner && GLOB.common_report && SSticker.current_state == GAME_STATE_FINISHED)
|
||||
SSticker.show_roundend_report(owner.client, FALSE)
|
||||
SSticker.show_roundend_report(owner.client)
|
||||
|
||||
/datum/action/report/IsAvailable(silent = FALSE)
|
||||
/datum/action/report/IsAvailable()
|
||||
return 1
|
||||
|
||||
/datum/action/report/Topic(href,href_list)
|
||||
@@ -532,7 +675,9 @@
|
||||
var/jobtext = ""
|
||||
if(ply.assigned_role)
|
||||
jobtext = " the <b>[ply.assigned_role]</b>"
|
||||
var/text = "<b>[ply.hide_ckey ? "<b>[ply.name]</b>[jobtext] " : "[ply.key]</b> was <b>[ply.name]</b>[jobtext] and "]"
|
||||
var/text = (ply.hide_ckey ? \
|
||||
"<b>[ply.key]</b> was <b>[ply.name]</b>[jobtext] and" \
|
||||
: "<b>[ply.name]</b>[jobtext]")
|
||||
if(ply.current)
|
||||
if(ply.current.stat == DEAD)
|
||||
text += " <span class='redtext'>died</span>"
|
||||
@@ -589,11 +734,9 @@
|
||||
var/list/sql_admins = list()
|
||||
for(var/i in GLOB.protected_admins)
|
||||
var/datum/admins/A = GLOB.protected_admins[i]
|
||||
var/sql_ckey = sanitizeSQL(A.target)
|
||||
var/sql_rank = sanitizeSQL(A.rank.name)
|
||||
sql_admins += list(list("ckey" = "'[sql_ckey]'", "rank" = "'[sql_rank]'"))
|
||||
sql_admins += list(list("ckey" = A.target, "rank" = A.rank.name))
|
||||
SSdbcore.MassInsert(format_table_name("admin"), sql_admins, duplicate_key = TRUE)
|
||||
var/datum/DBQuery/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] p INNER JOIN [format_table_name("admin")] a ON p.ckey = a.ckey SET p.lastadminrank = a.rank")
|
||||
var/datum/db_query/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] p INNER JOIN [format_table_name("admin")] a ON p.ckey = a.ckey SET p.lastadminrank = a.rank")
|
||||
query_admin_rank_update.Execute()
|
||||
qdel(query_admin_rank_update)
|
||||
|
||||
@@ -626,15 +769,20 @@
|
||||
flags += "can_edit_flags"
|
||||
if(!flags.len)
|
||||
continue
|
||||
var/sql_rank = sanitizeSQL(R.name)
|
||||
var/flags_to_check = flags.Join(" != [R_EVERYTHING] AND ") + " != [R_EVERYTHING]"
|
||||
var/datum/DBQuery/query_check_everything_ranks = SSdbcore.NewQuery("SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = '[sql_rank]' AND ([flags_to_check])")
|
||||
var/datum/db_query/query_check_everything_ranks = SSdbcore.NewQuery(
|
||||
"SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = :rank AND ([flags_to_check])",
|
||||
list("rank" = R.name)
|
||||
)
|
||||
if(!query_check_everything_ranks.Execute())
|
||||
qdel(query_check_everything_ranks)
|
||||
return
|
||||
if(query_check_everything_ranks.NextRow()) //no row is returned if the rank already has the correct flag value
|
||||
var/flags_to_update = flags.Join(" = [R_EVERYTHING], ") + " = [R_EVERYTHING]"
|
||||
var/datum/DBQuery/query_update_everything_ranks = SSdbcore.NewQuery("UPDATE [format_table_name("admin_ranks")] SET [flags_to_update] WHERE rank = '[sql_rank]'")
|
||||
var/datum/db_query/query_update_everything_ranks = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("admin_ranks")] SET [flags_to_update] WHERE rank = :rank",
|
||||
list("rank" = R.name)
|
||||
)
|
||||
if(!query_update_everything_ranks.Execute())
|
||||
qdel(query_update_everything_ranks)
|
||||
return
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
* SQL sanitization
|
||||
*/
|
||||
|
||||
// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts.
|
||||
/proc/sanitizeSQL(t)
|
||||
return SSdbcore.Quote("[t]")
|
||||
|
||||
/proc/format_table_name(table as text)
|
||||
return CONFIG_GET(string/feedback_tableprefix) + table
|
||||
|
||||
@@ -670,7 +666,7 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
|
||||
if(fexists(log))
|
||||
oldjson = json_decode(file2text(log))
|
||||
oldentries = oldjson["data"]
|
||||
if(!isemptylist(oldentries))
|
||||
if(length(oldentries))
|
||||
for(var/string in accepted)
|
||||
for(var/old in oldentries)
|
||||
if(string == old)
|
||||
@@ -680,7 +676,7 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
|
||||
var/list/finalized = list()
|
||||
finalized = accepted.Copy() + oldentries.Copy() //we keep old and unreferenced phrases near the bottom for culling
|
||||
listclearnulls(finalized)
|
||||
if(!isemptylist(finalized) && length(finalized) > storemax)
|
||||
if(length(finalized) > storemax)
|
||||
finalized.Cut(storemax + 1)
|
||||
fdel(log)
|
||||
|
||||
|
||||
+28
-43
@@ -1247,28 +1247,40 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
|
||||
|
||||
#define FOR_DVIEW_END GLOB.dview_mob.loc = null
|
||||
|
||||
//can a window be here, or is there a window blocking it?
|
||||
/proc/valid_window_location(turf/T, dir_to_check)
|
||||
if(!T)
|
||||
/**
|
||||
* Checks whether the target turf is in a valid state to accept a directional window
|
||||
* or other directional pseudo-dense object such as railings.
|
||||
*
|
||||
* Returns FALSE if the target turf cannot accept a directional window or railing.
|
||||
* Returns TRUE otherwise.
|
||||
*
|
||||
* Arguments:
|
||||
* * dest_turf - The destination turf to check for existing windows and railings
|
||||
* * test_dir - The prospective dir of some atom you'd like to put on this turf.
|
||||
* * is_fulltile - Whether the thing you're attempting to move to this turf takes up the entire tile or whether it supports multiple movable atoms on its tile.
|
||||
*/
|
||||
/proc/valid_window_location(turf/dest_turf, test_dir, is_fulltile = FALSE)
|
||||
if(!dest_turf)
|
||||
return FALSE
|
||||
for(var/obj/O in T)
|
||||
if(istype(O, /obj/machinery/door/window) && (O.dir == dir_to_check || dir_to_check == FULLTILE_WINDOW_DIR))
|
||||
return FALSE
|
||||
if(istype(O, /obj/structure/windoor_assembly))
|
||||
var/obj/structure/windoor_assembly/W = O
|
||||
if(W.ini_dir == dir_to_check || dir_to_check == FULLTILE_WINDOW_DIR)
|
||||
for(var/obj/turf_content in dest_turf)
|
||||
if(istype(turf_content, /obj/machinery/door/window))
|
||||
if((turf_content.dir == test_dir) || is_fulltile)
|
||||
return FALSE
|
||||
if(istype(O, /obj/structure/window))
|
||||
var/obj/structure/window/W = O
|
||||
if(W.ini_dir == dir_to_check || W.ini_dir == FULLTILE_WINDOW_DIR || dir_to_check == FULLTILE_WINDOW_DIR)
|
||||
if(istype(turf_content, /obj/structure/windoor_assembly))
|
||||
var/obj/structure/windoor_assembly/windoor_assembly = turf_content
|
||||
if(windoor_assembly.dir == test_dir || is_fulltile)
|
||||
return FALSE
|
||||
if(istype(O, /obj/structure/railing))
|
||||
var/obj/structure/railing/rail = O
|
||||
if(rail.ini_dir == dir_to_check || rail.ini_dir == FULLTILE_WINDOW_DIR || dir_to_check == FULLTILE_WINDOW_DIR)
|
||||
if(istype(turf_content, /obj/structure/window))
|
||||
var/obj/structure/window/window_structure = turf_content
|
||||
if(window_structure.dir == test_dir || window_structure.fulltile || is_fulltile)
|
||||
return FALSE
|
||||
if(istype(turf_content, /obj/structure/railing))
|
||||
var/obj/structure/railing/rail = turf_content
|
||||
if(rail.dir == test_dir || is_fulltile)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/proc/pass()
|
||||
/proc/pass(...)
|
||||
return
|
||||
|
||||
/proc/get_mob_or_brainmob(occupant)
|
||||
@@ -1579,33 +1591,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
|
||||
for(var/i in 1 to items_list[each_item])
|
||||
new each_item(where_to)
|
||||
|
||||
//sends a message to chat
|
||||
//config_setting should be one of the following
|
||||
//null - noop
|
||||
//empty string - use TgsTargetBroadcast with admin_only = FALSE
|
||||
//other string - use TgsChatBroadcast with the tag that matches config_setting, only works with TGS4, if using TGS3 the above method is used
|
||||
/proc/send2chat(message, config_setting)
|
||||
if(config_setting == null)
|
||||
return
|
||||
|
||||
UNTIL(GLOB.tgs_initialized)
|
||||
if(!world.TgsAvailable())
|
||||
return
|
||||
|
||||
var/datum/tgs_version/version = world.TgsVersion()
|
||||
if(config_setting == "" || version.suite == 3)
|
||||
world.TgsTargetedChatBroadcast(message, FALSE)
|
||||
return
|
||||
|
||||
var/list/channels_to_use = list()
|
||||
for(var/I in world.TgsChatChannelInfo())
|
||||
var/datum/tgs_chat_channel/channel = I
|
||||
if(channel.tag == config_setting)
|
||||
channels_to_use += channel
|
||||
|
||||
if(channels_to_use.len)
|
||||
world.TgsChatBroadcast()
|
||||
|
||||
//Checks to see if either the victim has a garlic necklace or garlic in their blood
|
||||
/proc/blood_sucking_checks(var/mob/living/carbon/target, check_neck, check_blood)
|
||||
//Bypass this if the target isnt carbon.
|
||||
|
||||
Reference in New Issue
Block a user