mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
In preparation of pixel movement, I want to refactor our slowdown system to something more modular, and something that doesn't require /quite/ as many proccalls/calculations a tick. The way this works is intended to only have things recalculate when it's necessary, rather than calling it every move. However, I've left movement_delay() in, as without completely redoing a lot of code it's not /quite/ ready at this point to tear it out completely, but I'm hoping everything can be transitioned over to this system later.
575 lines
21 KiB
Plaintext
575 lines
21 KiB
Plaintext
#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.
|
|
|
|
/datum/controller/subsystem/ticker/proc/gather_roundend_feedback()
|
|
gather_antag_data()
|
|
record_nuke_disk_location()
|
|
var/json_file = file("[GLOB.log_directory]/round_end_data.json")
|
|
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/list/area/shuttle_areas
|
|
if(SSshuttle && SSshuttle.emergency)
|
|
shuttle_areas = SSshuttle.emergency.shuttle_areas
|
|
for(var/mob/m in GLOB.mob_list)
|
|
var/escaped
|
|
var/category
|
|
var/list/mob_data = list()
|
|
if(isnewplayer(m))
|
|
continue
|
|
if(m.mind)
|
|
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(ishuman(L))
|
|
var/mob/living/carbon/human/H = L
|
|
category = "humans"
|
|
mob_data += list("job" = H.mind.assigned_role, "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))
|
|
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)
|
|
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
|
|
var/datum/station_state/end_state = new /datum/station_state()
|
|
end_state.count()
|
|
var/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"))
|
|
SSblackbox.record_feedback("nested tally", "round_end_stats", GLOB.joined_player_list.len - num_survivors, list("players", "dead"))
|
|
. = list()
|
|
.[POPCOUNT_SURVIVORS] = num_survivors
|
|
.[POPCOUNT_ESCAPEES] = num_escapees
|
|
.[POPCOUNT_SHUTTLE_ESCAPEES] = num_shuttle_escapees
|
|
.["station_integrity"] = station_integrity
|
|
|
|
/datum/controller/subsystem/ticker/proc/gather_antag_data()
|
|
var/team_gid = 1
|
|
var/list/team_ids = list()
|
|
|
|
for(var/datum/antagonist/A in GLOB.antagonists)
|
|
if(!A.owner)
|
|
continue
|
|
|
|
var/list/antag_info = list()
|
|
antag_info["key"] = A.owner.key
|
|
antag_info["name"] = A.owner.name
|
|
antag_info["antagonist_type"] = A.type
|
|
antag_info["antagonist_name"] = A.name //For auto and custom roles
|
|
antag_info["objectives"] = list()
|
|
antag_info["team"] = list()
|
|
var/datum/team/T = A.get_team()
|
|
if(T)
|
|
antag_info["team"]["type"] = T.type
|
|
antag_info["team"]["name"] = T.name
|
|
if(!team_ids[T])
|
|
team_ids[T] = team_gid++
|
|
antag_info["team"]["id"] = team_ids[T]
|
|
|
|
if(A.objectives.len)
|
|
for(var/datum/objective/O in A.objectives)
|
|
var/result = O.check_completion() ? "SUCCESS" : "FAIL"
|
|
antag_info["objectives"] += list(list("objective_type"=O.type,"text"=O.explanation_text,"result"=result))
|
|
SSblackbox.record_feedback("associative", "antagonists", 1, antag_info)
|
|
|
|
/datum/controller/subsystem/ticker/proc/record_nuke_disk_location()
|
|
var/obj/item/disk/nuclear/N = locate() in GLOB.poi_list
|
|
if(N)
|
|
var/list/data = list()
|
|
var/turf/T = get_turf(N)
|
|
if(T)
|
|
data["x"] = T.x
|
|
data["y"] = T.y
|
|
data["z"] = T.z
|
|
var/atom/outer = get_atom_on_turf(N,/mob/living)
|
|
if(outer != N)
|
|
if(isliving(outer))
|
|
var/mob/living/L = outer
|
|
data["holder"] = L.real_name
|
|
else
|
|
data["holder"] = outer.name
|
|
|
|
SSblackbox.record_feedback("associative", "roundend_nukedisk", 1 , data)
|
|
|
|
/datum/controller/subsystem/ticker/proc/gather_newscaster()
|
|
var/json_file = file("[GLOB.log_directory]/newscaster.json")
|
|
var/list/file_data = list()
|
|
var/pos = 1
|
|
for(var/V in GLOB.news_network.network_channels)
|
|
var/datum/newscaster/feed_channel/channel = V
|
|
if(!istype(channel))
|
|
stack_trace("Non-channel in newscaster channel list")
|
|
continue
|
|
file_data["[pos]"] = list("channel name" = "[channel.channel_name]", "author" = "[channel.author]", "censored" = channel.censored ? 1 : 0, "author censored" = channel.authorCensor ? 1 : 0, "messages" = list())
|
|
for(var/M in channel.messages)
|
|
var/datum/newscaster/feed_message/message = M
|
|
if(!istype(message))
|
|
stack_trace("Non-message in newscaster channel messages list")
|
|
continue
|
|
var/list/comment_data = list()
|
|
for(var/C in message.comments)
|
|
var/datum/newscaster/feed_comment/comment = C
|
|
if(!istype(comment))
|
|
stack_trace("Non-message in newscaster message comments list")
|
|
continue
|
|
comment_data += list(list("author" = "[comment.author]", "time stamp" = "[comment.time_stamp]", "body" = "[comment.body]"))
|
|
file_data["[pos]"]["messages"] += list(list("author" = "[message.author]", "time stamp" = "[message.time_stamp]", "censored" = message.bodyCensor ? 1 : 0, "author censored" = message.authorCensor ? 1 : 0, "photo file" = "[message.photo_file]", "photo caption" = "[message.caption]", "body" = "[message.body]", "comments" = comment_data))
|
|
pos++
|
|
if(GLOB.news_network.wanted_issue.active)
|
|
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))
|
|
|
|
/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>")
|
|
if(LAZYLEN(GLOB.round_end_notifiees))
|
|
send2irc("Notice", "[GLOB.round_end_notifiees.Join(", ")] the round has ended.")
|
|
|
|
for(var/I in round_end_events)
|
|
var/datum/callback/cb = I
|
|
cb.InvokeAsync()
|
|
LAZYCLEARLIST(round_end_events)
|
|
|
|
for(var/client/C in GLOB.clients)
|
|
if(!C.credits)
|
|
C.RollCredits()
|
|
C.playtitlemusic(40)
|
|
|
|
var/popcount = gather_roundend_feedback()
|
|
display_report(popcount)
|
|
|
|
CHECK_TICK
|
|
|
|
// Add AntagHUD to everyone, see who was really evil the whole time!
|
|
for(var/datum/atom_hud/antag/H in GLOB.huds)
|
|
for(var/m in GLOB.player_list)
|
|
var/mob/M = m
|
|
H.add_hud_to(M)
|
|
|
|
CHECK_TICK
|
|
|
|
//Set news report and mode result
|
|
mode.set_round_result()
|
|
|
|
send2irc("Server", "Round just ended.")
|
|
|
|
if(length(CONFIG_GET(keyed_list/cross_server)))
|
|
send_news_report()
|
|
|
|
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()
|
|
//Look into all mobs in world, dead or alive
|
|
for(var/datum/antagonist/A in GLOB.antagonists)
|
|
if(!A.owner)
|
|
continue
|
|
if(!(A.name in total_antagonists))
|
|
total_antagonists[A.name] = list()
|
|
total_antagonists[A.name] += "[key_name(A.owner)]"
|
|
|
|
CHECK_TICK
|
|
|
|
//Now print them all into the log!
|
|
log_game("Antagonists at round end were...")
|
|
for(var/antag_name in total_antagonists)
|
|
var/list/L = total_antagonists[antag_name]
|
|
log_game("[antag_name]s :[L.Join(", ")].")
|
|
|
|
CHECK_TICK
|
|
SSdbcore.SetRoundEnd()
|
|
//Collects persistence features
|
|
if(mode.allow_persistence_save)
|
|
SSpersistence.CollectData()
|
|
|
|
//stop collecting feedback during grifftime
|
|
SSblackbox.Seal()
|
|
|
|
sleep(50)
|
|
ready_for_reboot = TRUE
|
|
standard_reboot()
|
|
|
|
/datum/controller/subsystem/ticker/proc/standard_reboot()
|
|
if(ready_for_reboot)
|
|
if(mode.station_was_nuked)
|
|
Reboot("Station destroyed by Nuclear Device.", "nuke")
|
|
else
|
|
Reboot("Round ended.", "proper completion")
|
|
else
|
|
CRASH("Attempted standard reboot without ticker roundend completion")
|
|
|
|
//Common part of the report
|
|
/datum/controller/subsystem/ticker/proc/build_roundend_report()
|
|
var/list/parts = list()
|
|
|
|
//Gamemode specific things. Should be empty most of the time.
|
|
parts += mode.special_report()
|
|
|
|
CHECK_TICK
|
|
|
|
//AI laws
|
|
parts += law_report()
|
|
|
|
CHECK_TICK
|
|
|
|
//Antagonists
|
|
parts += antag_report()
|
|
|
|
CHECK_TICK
|
|
//Medals
|
|
parts += medal_report()
|
|
//Station Goals
|
|
parts += goal_report()
|
|
|
|
listclearnulls(parts)
|
|
|
|
return parts.Join()
|
|
|
|
/datum/controller/subsystem/ticker/proc/survivor_report(popcount)
|
|
var/list/parts = list()
|
|
var/station_evacuated = EMERGENCY_ESCAPED_OR_ENDGAMED
|
|
|
|
if(GLOB.round_id)
|
|
var/statspage = CONFIG_GET(string/roundstatsurl)
|
|
var/info = statspage ? "<a href='?action=openLink&link=[url_encode(statspage)][GLOB.round_id]'>[GLOB.round_id]</a>" : GLOB.round_id
|
|
parts += "[GLOB.TAB]Round ID: <b>[info]</b>"
|
|
parts += "[GLOB.TAB]Shift Duration: <B>[DisplayTimeText(world.time - SSticker.round_start_time)]</B>"
|
|
parts += "[GLOB.TAB]Station Integrity: <B>[mode.station_was_nuked ? "<span class='redtext'>Destroyed</span>" : "[popcount["station_integrity"]]%"]</B>"
|
|
var/total_players = GLOB.joined_player_list.len
|
|
if(total_players)
|
|
parts+= "[GLOB.TAB]Total Population: <B>[total_players]</B>"
|
|
if(station_evacuated)
|
|
parts += "<BR>[GLOB.TAB]Evacuation Rate: <B>[popcount[POPCOUNT_ESCAPEES]] ([PERCENT(popcount[POPCOUNT_ESCAPEES]/total_players)]%)</B>"
|
|
parts += "[GLOB.TAB](on emergency shuttle): <B>[popcount[POPCOUNT_SHUTTLE_ESCAPEES]] ([PERCENT(popcount[POPCOUNT_SHUTTLE_ESCAPEES]/total_players)]%)</B>"
|
|
parts += "[GLOB.TAB]Survival Rate: <B>[popcount[POPCOUNT_SURVIVORS]] ([PERCENT(popcount[POPCOUNT_SURVIVORS]/total_players)]%)</B>"
|
|
if(SSblackbox.first_death)
|
|
var/list/ded = SSblackbox.first_death
|
|
if(ded.len)
|
|
parts += "[GLOB.TAB]First Death: <b>[ded["name"]], [ded["role"]], at [ded["area"]]. Damage taken: [ded["damage"]].[ded["last_words"] ? " Their last words were: \"[ded["last_words"]]\"" : ""]</b>"
|
|
//ignore this comment, it fixes the broken sytax parsing caused by the " above
|
|
else
|
|
parts += "[GLOB.TAB]<i>Nobody died this shift!</i>"
|
|
return parts.Join("<br>")
|
|
|
|
/client/proc/roundend_report_file()
|
|
return "data/roundend_reports/[ckey].html"
|
|
|
|
/datum/controller/subsystem/ticker/proc/show_roundend_report(client/C, previous = FALSE)
|
|
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)
|
|
var/list/report_parts = list(personal_report(C), GLOB.common_report)
|
|
content = report_parts.Join()
|
|
C.verbs -= /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')
|
|
roundend_report.open(0)
|
|
|
|
/datum/controller/subsystem/ticker/proc/personal_report(client/C, popcount)
|
|
var/list/parts = list()
|
|
var/mob/M = C.mob
|
|
if(M.mind && !isnewplayer(M))
|
|
if(M.stat != DEAD && !isbrain(M))
|
|
if(EMERGENCY_ESCAPED_OR_ENDGAMED)
|
|
if(!M.onCentCom() && !M.onSyndieBase())
|
|
parts += "<div class='panel stationborder'>"
|
|
parts += "<span class='marooned'>You managed to survive, but were marooned on [station_name()]...</span>"
|
|
else
|
|
parts += "<div class='panel greenborder'>"
|
|
parts += "<span class='greentext'>You managed to survive the events on [station_name()] as [M.real_name].</span>"
|
|
else
|
|
parts += "<div class='panel greenborder'>"
|
|
parts += "<span class='greentext'>You managed to survive the events on [station_name()] as [M.real_name].</span>"
|
|
|
|
else
|
|
parts += "<div class='panel redborder'>"
|
|
parts += "<span class='redtext'>You did not survive the events on [station_name()]...</span>"
|
|
else
|
|
parts += "<div class='panel stationborder'>"
|
|
parts += "<br>"
|
|
parts += GLOB.survivor_report
|
|
parts += "</div>"
|
|
|
|
return parts.Join()
|
|
|
|
/datum/controller/subsystem/ticker/proc/display_report(popcount)
|
|
GLOB.common_report = build_roundend_report()
|
|
GLOB.survivor_report = survivor_report(popcount)
|
|
for(var/client/C in GLOB.clients)
|
|
show_roundend_report(C, FALSE)
|
|
give_show_report_button(C)
|
|
CHECK_TICK
|
|
|
|
/datum/controller/subsystem/ticker/proc/law_report()
|
|
var/list/parts = list()
|
|
var/borg_spacer = FALSE //inserts an extra linebreak to seperate AIs from independent borgs, and then multiple independent borgs.
|
|
//Silicon laws report
|
|
for (var/i in GLOB.ai_list)
|
|
var/mob/living/silicon/ai/aiPlayer = i
|
|
if(aiPlayer.mind)
|
|
parts += "<b>[aiPlayer.name]</b> (Played by: <b>[aiPlayer.mind.key]</b>)'s laws [aiPlayer.stat != DEAD ? "at the end of the round" : "when it was <span class='redtext'>deactivated</span>"] were:"
|
|
parts += aiPlayer.laws.get_law_list(include_zeroth=TRUE)
|
|
|
|
parts += "<b>Total law changes: [aiPlayer.law_change_counter]</b>"
|
|
|
|
if (aiPlayer.connected_robots.len)
|
|
var/borg_num = aiPlayer.connected_robots.len
|
|
var/robolist = "<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> (Played by: <b>[robo.mind.key]</b>)[robo.stat == DEAD ? " <span class='redtext'>(Deactivated)</span>" : ""][borg_num ?", ":""]<br>"
|
|
parts += "[robolist]"
|
|
if(!borg_spacer)
|
|
borg_spacer = TRUE
|
|
|
|
for (var/mob/living/silicon/robot/robo in GLOB.silicon_mobs)
|
|
if (!robo.connected_ai && robo.mind)
|
|
parts += "[borg_spacer?"<br>":""]<b>[robo.name]</b> (Played by: <b>[robo.mind.key]</b>) [(robo.stat != DEAD)? "<span class='greentext'>survived</span> as an AI-less borg!" : "was <span class='redtext'>unable to survive</span> the rigors of being a cyborg without an AI."] Its laws were:"
|
|
|
|
if(robo) //How the hell do we lose robo between here and the world messages directly above this?
|
|
parts += robo.laws.get_law_list(include_zeroth=TRUE)
|
|
|
|
if(!borg_spacer)
|
|
borg_spacer = TRUE
|
|
|
|
if(parts.len)
|
|
return "<div class='panel stationborder'>[parts.Join("<br>")]</div>"
|
|
else
|
|
return ""
|
|
|
|
/datum/controller/subsystem/ticker/proc/goal_report()
|
|
var/list/parts = list()
|
|
if(mode.station_goals.len)
|
|
for(var/V in mode.station_goals)
|
|
var/datum/station_goal/G = V
|
|
parts += G.get_result()
|
|
return "<div class='panel stationborder'><ul>[parts.Join()]</ul></div>"
|
|
|
|
/datum/controller/subsystem/ticker/proc/medal_report()
|
|
if(GLOB.commendations.len)
|
|
var/list/parts = list()
|
|
parts += "<span class='header'>Medal Commendations:</span>"
|
|
for (var/com in GLOB.commendations)
|
|
parts += com
|
|
return "<div class='panel stationborder'>[parts.Join("<br>")]</div>"
|
|
return ""
|
|
|
|
/datum/controller/subsystem/ticker/proc/antag_report()
|
|
var/list/result = list()
|
|
var/list/all_teams = list()
|
|
var/list/all_antagonists = list()
|
|
|
|
for(var/datum/antagonist/A in GLOB.antagonists)
|
|
if(!A.owner)
|
|
continue
|
|
all_teams |= A.get_team()
|
|
all_antagonists += A
|
|
|
|
for(var/datum/team/T in all_teams)
|
|
result += T.roundend_report()
|
|
for(var/datum/antagonist/X in all_antagonists)
|
|
if(X.get_team() == T)
|
|
all_antagonists -= X
|
|
result += " "//newline between teams
|
|
CHECK_TICK
|
|
|
|
var/currrent_category
|
|
var/datum/antagonist/previous_category
|
|
|
|
sortTim(all_antagonists, /proc/cmp_antag_category)
|
|
|
|
for(var/datum/antagonist/A in all_antagonists)
|
|
if(!A.show_in_roundend)
|
|
continue
|
|
if(A.roundend_category != currrent_category)
|
|
if(previous_category)
|
|
result += previous_category.roundend_report_footer()
|
|
result += "</div>"
|
|
result += "<div class='panel redborder'>"
|
|
result += A.roundend_report_header()
|
|
currrent_category = A.roundend_category
|
|
previous_category = A
|
|
result += A.roundend_report()
|
|
result += "<br><br>"
|
|
CHECK_TICK
|
|
|
|
if(all_antagonists.len)
|
|
var/datum/antagonist/last = all_antagonists[all_antagonists.len]
|
|
result += last.roundend_report_footer()
|
|
result += "</div>"
|
|
|
|
return result.Join()
|
|
|
|
/proc/cmp_antag_category(datum/antagonist/A,datum/antagonist/B)
|
|
return sorttext(B.roundend_category,A.roundend_category)
|
|
|
|
|
|
/datum/controller/subsystem/ticker/proc/give_show_report_button(client/C)
|
|
var/datum/action/report/R = new
|
|
C.player_details.player_actions += R
|
|
R.Grant(C.mob)
|
|
to_chat(C,"<a href='?src=[REF(R)];report=1'>Show roundend report again</a>")
|
|
|
|
/datum/action/report
|
|
name = "Show roundend report"
|
|
button_icon_state = "round_end"
|
|
|
|
/datum/action/report/Trigger()
|
|
if(owner && GLOB.common_report && SSticker.current_state == GAME_STATE_FINISHED)
|
|
SSticker.show_roundend_report(owner.client, FALSE)
|
|
|
|
/datum/action/report/IsAvailable()
|
|
return 1
|
|
|
|
/datum/action/report/Topic(href,href_list)
|
|
if(usr != owner)
|
|
return
|
|
if(href_list["report"])
|
|
Trigger()
|
|
return
|
|
|
|
|
|
/proc/printplayer(datum/mind/ply, fleecheck)
|
|
var/jobtext = ""
|
|
if(ply.assigned_role)
|
|
jobtext = " the <b>[ply.assigned_role]</b>"
|
|
var/text = "<b>[ply.key]</b> was <b>[ply.name]</b>[jobtext] and"
|
|
if(ply.current)
|
|
if(ply.current.stat == DEAD)
|
|
text += " <span class='redtext'>died</span>"
|
|
else
|
|
text += " <span class='greentext'>survived</span>"
|
|
if(fleecheck)
|
|
var/turf/T = get_turf(ply.current)
|
|
if(!T || !is_station_level(T.z))
|
|
text += " while <span class='redtext'>fleeing the station</span>"
|
|
if(ply.current.real_name != ply.name)
|
|
text += " as <b>[ply.current.real_name]</b>"
|
|
else
|
|
text += " <span class='redtext'>had their body destroyed</span>"
|
|
return text
|
|
|
|
/proc/printplayerlist(list/players,fleecheck)
|
|
var/list/parts = list()
|
|
|
|
parts += "<ul class='playerlist'>"
|
|
for(var/datum/mind/M in players)
|
|
parts += "<li>[printplayer(M,fleecheck)]</li>"
|
|
parts += "</ul>"
|
|
return parts.Join()
|
|
|
|
|
|
/proc/printobjectives(datum/mind/ply)
|
|
var/list/objective_parts = list()
|
|
var/count = 1
|
|
for(var/datum/objective/objective in ply.objectives)
|
|
if(objective.check_completion())
|
|
objective_parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</span>"
|
|
else
|
|
objective_parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
|
count++
|
|
return objective_parts.Join("<br>")
|
|
|
|
/datum/controller/subsystem/ticker/proc/save_admin_data()
|
|
if(CONFIG_GET(flag/admin_legacy_system)) //we're already using legacy system so there's nothing to save
|
|
return
|
|
else if(load_admins(TRUE)) //returns true if there was a database failure and the backup was loaded from
|
|
return
|
|
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")
|
|
query_admin_rank_update.Execute()
|
|
qdel(query_admin_rank_update)
|
|
|
|
//json format backup file generation stored per server
|
|
var/json_file = file("data/admins_backup.json")
|
|
var/list/file_data = list("ranks" = list(), "admins" = list())
|
|
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
|
file_data["ranks"]["[R.name]"] = list()
|
|
file_data["ranks"]["[R.name]"]["include rights"] = R.include_rights
|
|
file_data["ranks"]["[R.name]"]["exclude rights"] = R.exclude_rights
|
|
file_data["ranks"]["[R.name]"]["can edit rights"] = R.can_edit_rights
|
|
for(var/i in GLOB.admin_datums+GLOB.deadmins)
|
|
var/datum/admins/A = GLOB.admin_datums[i]
|
|
if(!A)
|
|
A = GLOB.deadmins[i]
|
|
if (!A)
|
|
continue
|
|
file_data["admins"]["[i]"] = A.rank.name
|
|
fdel(json_file)
|
|
WRITE_FILE(json_file, json_encode(file_data))
|
|
|
|
/datum/controller/subsystem/ticker/proc/update_everything_flag_in_db()
|
|
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
|
var/list/flags = list()
|
|
if(R.include_rights == R_EVERYTHING)
|
|
flags += "flags"
|
|
if(R.exclude_rights == R_EVERYTHING)
|
|
flags += "exclude_flags"
|
|
if(R.can_edit_rights == R_EVERYTHING)
|
|
flags += "can_edit_flags"
|
|
if(!flags.len)
|
|
continue
|
|
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 = '[R.name]' AND ([flags_to_check])")
|
|
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 = '[R.name]'")
|
|
if(!query_update_everything_ranks.Execute())
|
|
qdel(query_update_everything_ranks)
|
|
return
|
|
qdel(query_update_everything_ranks)
|
|
qdel(query_check_everything_ranks)
|