Merge remote-tracking branch 'refs/remotes/ParadiseSS13/master' into wizloadouts

This commit is contained in:
datlo
2020-03-22 12:32:54 +01:00
68 changed files with 600 additions and 149 deletions
+1 -1
View File
@@ -31,7 +31,7 @@
/datum/proc/vv_get_var(var_name)
switch(var_name)
if("attack_log", "debug_log")
if("attack_log_old", "debug_log")
return debug_variable(var_name, vars[var_name], 0, src, sanitize = FALSE)
if("vars")
return debug_variable(var_name, list(), 0, src)
+37
View File
@@ -0,0 +1,37 @@
/datum/log_record
var/log_type // Type of log
var/raw_time // When did this happen?
var/what // What happened
var/who // Who did it
var/target // Who/what was targeted (can be a string)
var/turf/where // Where did it happen
/datum/log_record/New(_log_type, _who, _what, _target, _where, _raw_time)
log_type = _log_type
who = get_subject_text(_who)
what = _what
target = get_subject_text(_target)
if(!_where)
_where = get_turf(_who)
where = _where
if(!_raw_time)
_raw_time = world.time
raw_time = _raw_time
/datum/log_record/proc/get_subject_text(subject)
if(ismob(subject) || isclient(subject) || istype(subject, /datum/mind))
return key_name_admin(subject)
if(isatom(subject))
var/atom/A = subject
return A.name
if(istype(subject, /datum))
var/datum/D = subject
return D.type
return subject
/proc/compare_log_record(datum/log_record/A, datum/log_record/B)
var/time_diff = A.raw_time - B.raw_time
if(!time_diff) // Same time
return cmp_text_asc(A.log_type, B.log_type)
return time_diff
+232
View File
@@ -0,0 +1,232 @@
#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG)
/datum/log_viewer
var/time_from = 0
var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up
var/list/selected_mobs = list() // The mobs in question
var/list/selected_log_types = list() // The log types being searched for
var/list/log_records = list() // Found and sorted records
/datum/log_viewer/proc/clear_all()
selected_mobs.Cut()
selected_log_types.Cut()
time_from = initial(time_from)
time_to = initial(time_to)
log_records.Cut()
return
/datum/log_viewer/proc/search()
log_records.Cut() // Empty the old results
var/list/invalid_mobs = list()
for(var/i in selected_mobs)
var/mob/M = i
if(!M || QDELETED(M))
invalid_mobs |= M
continue
for(var/log_type in selected_log_types)
var/list/logs = M.logs[log_type]
var/len_logs = length(logs)
if(len_logs)
var/start_index = get_earliest_log_index(logs)
if(!start_index) // No log found that matches the starting time criteria
continue
var/end_index = get_latest_log_index(logs)
if(!end_index) // No log found that matches the end time criteria
continue
log_records.Add(logs.Copy(start_index, end_index + 1))
if(invalid_mobs.len)
to_chat(usr, "<span class='warning'>The search criteria contained invalid mobs. They have been removed from the criteria.</span>")
for(var/i in invalid_mobs)
selected_mobs -= i // Cleanup
log_records = sortTim(log_records, /proc/compare_log_record)
/** Binary search like implementation to find the earliest log
* Returns the index of the earliest log using the time_from value for the given list of logs.
* It will return 0 if no log after time_from is found
*/
/datum/log_viewer/proc/get_earliest_log_index(list/logs)
if(!time_from)
return 1
var/start = 1
var/end = length(logs)
var/mid
do
mid = round_down((end + start) / 2)
var/datum/log_record/L = logs[mid]
if(L.raw_time >= time_from)
end = mid
else
start = mid
while(end - start > 1)
var/datum/log_record/L = logs[end]
if(L.raw_time >= time_from) // Check if there is atleast one valid log
return end
return 0
/** Binary search like implementation to find the latest log
* Returns the index of the latest log using the time_to value (1 second is added to prevent rounding weirdness) for the given list of logs.
* It will return 0 if no log before time_to + 10 is found
*/
/datum/log_viewer/proc/get_latest_log_index(list/logs)
if(world.time < time_to)
return length(logs)
var/end = length(logs)
var/start = 1
var/mid
var/max_time = time_to + 10
do
mid = round((end + start) / 2 + 0.5)
var/datum/log_record/L = logs[mid]
if(L.raw_time >= max_time)
end = mid
else
start = mid
while(end - start > 1)
var/datum/log_record/L = logs[start]
if(L.raw_time < max_time) // Check if there is atleast one valid log
return start
return 0
/datum/log_viewer/proc/add_mob(mob/user, mob/M)
if(!M || !user)
return
selected_mobs |= M
show_ui(user)
/datum/log_viewer/proc/show_ui(mob/user)
var/all_log_types = ALL_LOGS
var/trStyleTop = "border-top:2px solid; border-bottom:2px solid; padding-top: 5px; padding-bottom: 5px;"
var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;"
var/dat
dat += "<head><style>.adminticket{border:2px solid} td{border:1px solid grey;} th{border:1px solid grey;} span{float:left;width:150px;}</style></head>"
dat += "<div style='height:15vh'>"
dat += "<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>"
dat += "<span>Mobs being used:</span>"
for(var/i in selected_mobs)
var/mob/M = i
dat += "<a href='?src=[UID()];remove_mob=\ref[M]'>[M.name]</a>"
dat += "<a href='?src=[UID()];add_mob=1'>Add Mob</a>"
dat += "<a href='?src=[UID()];clear_mobs=1'>Clear All Mobs</a>"
dat += "<BR>"
dat += "<span>Log Types:</span>"
for(var/i in all_log_types)
var/log_type = i
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)]"
else
text = log_type
dat += "<a href='?src=[UID()];toggle_log_type=[log_type]' style='[style]'>[text]</a>"
dat += "<BR>"
dat += "<a href='?src=[UID()];clear_all=1'>Clear All Settings</a>"
dat += "<a href='?src=[UID()];search=1'>Search</a>"
dat += "</div>"
// Search results
var/tdStyleTime = "width:80px; text-align:center;"
var/tdStyleType = "width:80px; text-align:center;"
var/tdStyleWho = "width:300px; text-align:center;"
var/tdStyleWhere = "width:150px; text-align:center;"
dat += "<div style='overflow-y: auto; max-height:76vh;'>"
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)
var/datum/log_record/L = i
var/time = gameTimestamp(wtime = L.raw_time - 9.99) // The time rounds up for some reason. Will result in weird filtering results
dat +="<tr style='[trStyle]'><td style='[tdStyleTime]'>[time]</td><td style='[tdStyleType]background: [get_logtype_color(L.log_type)]'>[L.log_type]</td>\
<td style='[tdStyleWho]'>[L.who]</td><td style='background: [get_logtype_color(L.log_type)];'>[L.what]</td>\
<td style='[tdStyleWho]'>[L.target]</td><td style='[tdStyleWhere]'>[ADMIN_COORDJMP(L.where)]</td></tr>"
dat += "</table>"
dat += "</div>"
var/datum/browser/popup = new(user, "Log viewer", "Log viewer", 1400, 600)
popup.set_content(dat)
popup.open()
/datum/log_viewer/Topic(href, href_list)
if(href_list["start_time"])
var/input = input(usr, "hh:mm:ss", "Start time", "00:00:00") as text|null
if(!input)
return
var/res = timeStampToNum(input)
if(res < 0)
to_chat(usr, "<span class='warning'>'[input]' is an invalid input value.</span>")
return
time_from = res
show_ui(usr)
return
if(href_list["end_time"])
var/input = input(usr, "hh:mm:ss", "End time", "04:00:00") as text|null
if(!input)
return
var/res = timeStampToNum(input)
if(res < 0)
to_chat(usr, "<span class='warning'>'[input]' is an invalid input value.</span>")
return
time_to = res
show_ui(usr)
return
if(href_list["search"])
search(usr)
show_ui(usr)
return
if(href_list["clear_all"])
clear_all(usr)
show_ui(usr)
return
if(href_list["clear_mobs"])
selected_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["remove_mob"])
var/mob/M = locate(href_list["remove_mob"])
if(M)
selected_mobs -= M
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)
selected_log_types -= log_type
else
selected_log_types += log_type
show_ui(usr)
return
/datum/log_viewer/proc/get_logtype_color(log_type)
switch(log_type)
if(ATTACK_LOG)
return "darkred"
if(DEFENSE_LOG)
return "chocolate"
if(CONVERSION_LOG)
return "indigo"
if(SAY_LOG)
return "teal"
if(EMOTE_LOG)
return "deepskyblue"
if(MISC_LOG)
return "gray"
return "slategray"
+8
View File
@@ -105,6 +105,14 @@
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
+1 -1
View File
@@ -241,7 +241,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
before_cast(targets)
invocation()
if(user && user.ckey)
user.create_attack_log("<font color='red'>[key_name(user)] cast the spell [name].</font>")
add_attack_logs(user, null, "cast the spell [name]")
spawn(0)
if(charge_type == "recharge" && recharge)
start_recharge()
+2 -1
View File
@@ -141,6 +141,7 @@
else
user.mind.AddSpell(S)
to_chat(user, "<span class='notice'>You flip through the pages. Your understanding of the boundaries of reality increases. You can cast [spellname]!</span>")
user.create_log(MISC_LOG, "learned the spell [spellname] ([S])")
user.create_attack_log("<font color='orange'>[key_name(user)] learned the spell [spellname] ([S]).</font>")
onlearned(user)
@@ -164,4 +165,4 @@
/obj/item/spellbook/oneuse/mime/greaterwall
spell = /obj/effect/proc_holder/spell/targeted/forcewall/mime
spellname = "Invisible Greater Wall"
desc = "It contains illustrations of the great walls of human history."
desc = "It contains illustrations of the great walls of human history."