VV Refactor

Ports /tg/'s VV refactor. Most of this is just cleaning up and making it
easier on the coders, but everyone else benefits too:

 - Mass mod no longer freezes the server (CHECK_TICK <3)
 - Attempting to change any of the step_* values or anything that will
 otherwise disable pixel movement will now prompt you and ask if you are
 sure you want to continue
 - You can actually enter |text| again in a box, rather than being
 forced to always use the multi-line input
 - Cookies! VV saves your search and scroll position for a given UID,
 rather convenient for debugging
 - The refresh button on /clients works finally
 - You can view lists in detail by clicking on them like any other
 reference, and edit them directly that way. This also means there is no
 hard limit on how many variables you can see in a list before it's
 truncated- it will just be hidden until you view the list in VV
 directly.
 - You can shuffle lists and clear duplicate/null entries.
 - Mildly reordered the stuff in the dropdown, inconvenient but
 necessary.
This commit is contained in:
tigercat2000
2017-09-14 14:08:38 -07:00
parent eb69af1321
commit 60e287c019
18 changed files with 1520 additions and 1128 deletions
+538 -345
View File
@@ -1,441 +1,517 @@
// reference: /client/proc/modify_variables(var/atom/O, var/param_var_name = null, var/autodetect_class = 0)
/datum
var/var_edited = 0 //Warrenty void if seal is broken
var/var_edited = FALSE //Warranty void if seal is broken
/datum/proc/on_varedit(modified_var) //called whenever a var is edited
var_edited = 1
/datum/proc/can_vv_get(var_name)
return TRUE
/client/proc/can_vv_get(var_name)
return TRUE
/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited
switch(var_name)
if("vars")
return FALSE
if("var_edited")
return FALSE
var_edited = TRUE
vars[var_name] = var_value
/client/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited
switch(var_name)
if("vars")
return FALSE
if("var_edited")
return FALSE
var_edited = TRUE
vars[var_name] = var_value
/datum/proc/vv_get_var(var_name)
switch(var_name)
if("vars")
return debug_variable(var_name, list(), 0, src)
return debug_variable(var_name, vars[var_name], 0, src)
/client/proc/vv_get_var(var_name)
switch(var_name)
if("vars")
return debug_variable(var_name, list(), 0, src)
return debug_variable(var_name, vars[var_name], 0, src)
//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down
//add seperaters by doing . += "---"
/datum/proc/vv_get_dropdown()
. = list()
. += "---"
.["Call Proc"] = "?_src_=vars;proc_call=[UID()]"
.["Mark Object"] = "?_src_=vars;mark_object=[UID()]"
.["Jump to Object"] = "?_src_=vars;jump_to=[UID()]"
.["Delete"] = "?_src_=vars;delete=[UID()]"
. += "---"
/client/proc/vv_get_dropdown()
. = list()
. += "---"
.["Call Proc"] = "?_src_=vars;proc_call=[UID()]"
.["Mark Object"] = "?_src_=vars;mark_object=[UID()]"
.["Delete"] = "?_src_=vars;delete=[UID()]"
. += "---"
/client/proc/debug_variables(datum/D in world)
set category = "Debug"
set name = "View Variables"
//set src in world
var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round.
if(!is_admin(usr))
to_chat(usr, "<span class='warning'>You need to be an administrator to access this.</span>")
return
if(!D)
return
var/islist = islist(D)
var/isclient = isclient(D)
if(!islist && !isclient && !istype(D))
return
var/title = ""
var/list/body = list()
var/icon/sprite
var/hash
var/refid
if(!D) return
if(istype(D, /atom))
var/atom/A = D
title = "[A.name] (\ref[A]) = [A.type]"
#ifdef VARSICON
if(A.icon)
body += debug_variable("icon", new/icon(A.icon, A.icon_state, A.dir), 0)
#endif
var/sprite
if(istype(D,/atom))
var/atom/AT = D
if(AT.icon && AT.icon_state)
sprite = 1
title = "[D] (\ref[D]) = [D.type]"
body += {"<script type="text/javascript">
function updateSearch(){
var filter_text = document.getElementById('filter');
var filter = filter_text.value.toLowerCase();
if(event.keyCode == 13){ //Enter / return
var vars_ol = document.getElementById('vars');
var lis = vars_ol.getElementsByTagName("li");
for( var i = 0; i < lis.length; ++i )
{
try{
var li = lis\[i\];
if( li.style.backgroundColor == "#ffee88" )
{
alist = lis\[i\].getElementsByTagName("a")
if(alist.length > 0){
location.href=alist\[0\].href;
}
}
}catch(err) { }
}
return
}
if(event.keyCode == 38){ //Up arrow
var vars_ol = document.getElementById('vars');
var lis = vars_ol.getElementsByTagName("li");
for( var i = 0; i < lis.length; ++i )
{
try{
var li = lis\[i\];
if( li.style.backgroundColor == "#ffee88" )
{
if( (i-1) >= 0){
var li_new = lis\[i-1\];
li.style.backgroundColor = "white";
li_new.style.backgroundColor = "#ffee88";
return
}
}
}catch(err) { }
}
return
}
if(event.keyCode == 40){ //Down arrow
var vars_ol = document.getElementById('vars');
var lis = vars_ol.getElementsByTagName("li");
for( var i = 0; i < lis.length; ++i )
{
try{
var li = lis\[i\];
if( li.style.backgroundColor == "#ffee88" )
{
if( (i+1) < lis.length){
var li_new = lis\[i+1\];
li.style.backgroundColor = "white";
li_new.style.backgroundColor = "#ffee88";
return
}
}
}catch(err) { }
}
return
}
//This part here resets everything to how it was at the start so the filter is applied to the complete list. Screw efficiency, it's client-side anyway and it only looks through 200 or so variables at maximum anyway (mobs).
if(complete_list != null && complete_list != ""){
var vars_ol1 = document.getElementById("vars");
vars_ol1.innerHTML = complete_list
}
if(filter.value == ""){
return;
}else{
var vars_ol = document.getElementById('vars');
var lis = vars_ol.getElementsByTagName("li");
for( var i = 0; i < lis.length; ++i )
{
try{
var li = lis\[i\];
if( li.innerText.toLowerCase().indexOf(filter) == -1 )
{
vars_ol.removeChild(li);
i--;
}
}catch(err) { }
}
}
var lis_new = vars_ol.getElementsByTagName("li");
for( var j = 0; j < lis_new.length; ++j )
{
var li1 = lis\[j\];
if(j == 0){
li1.style.backgroundColor = "#ffee88";
}else{
li1.style.backgroundColor = "white";
}
}
}
function selectTextField(){
var filter_text = document.getElementById('filter');
filter_text.focus();
filter_text.select();
}
function loadPage(list) {
if(list.options\[list.selectedIndex\].value == ""){
return;
}
location.href=list.options\[list.selectedIndex\].value;
}
</script> "}
body += "<body onload='selectTextField(); updateSearch()' onkeyup='updateSearch()'>"
body += "<div align='center'><table width='100%'><tr><td width='50%'>"
if(sprite)
body += "<table align='center' width='100%'><tr><td>[bicon(D, use_class=0)]</td><td>"
if(!islist)
refid = "[D.UID()]"
else
body += "<table align='center' width='100%'><tr><td>"
refid = "\ref[D]"
body += "<div align='center'>"
var/type = /list
if(!islist)
type = D.type
if(istype(D,/atom))
if(isatom(D))
var/atom/A = D
if(A.icon && A.icon_state)
sprite = new /icon(A.icon, A.icon_state)
hash = md5(A.icon)
hash = md5(hash + A.icon_state)
usr << browse_rsc(sprite, "vv[hash].png")
var/sprite_text
if(sprite)
sprite_text = "<img src='vv[hash].png'></td><td>"
var/list/atomsnowflake = list()
if(isatom(D))
var/atom/A = D
if(isliving(A))
body += "<a href='?_src_=vars;rename=[D.UID()]'><b>[D]</b></a>"
if(A.dir)
body += "<br><font size='1'><a href='?_src_=vars;rotatedatum=[D.UID()];rotatedir=left'><<</a> <a href='?_src_=vars;datumedit=[D.UID()];varnameedit=dir'>[dir2text(A.dir)]</a> <a href='?_src_=vars;rotatedatum=[D.UID()];rotatedir=right'>>></a></font>"
var/mob/living/M = A
body += "<br><font size='1'><a href='?_src_=vars;datumedit=[D.UID()];varnameedit=ckey'>[M.ckey ? M.ckey : "No ckey"]</a> / <a href='?_src_=vars;datumedit=[D.UID()];varnameedit=real_name'>[M.real_name ? M.real_name : "No real name"]</a></font>"
body += {"
<br><font size='1'>
BRUTE:<font size='1'><a href='?_src_=vars;mobToDamage=[D.UID()];adjustDamage=brute'>[M.getBruteLoss()]</a>
FIRE:<font size='1'><a href='?_src_=vars;mobToDamage=[D.UID()];adjustDamage=fire'>[M.getFireLoss()]</a>
TOXIN:<font size='1'><a href='?_src_=vars;mobToDamage=[D.UID()];adjustDamage=toxin'>[M.getToxLoss()]</a>
OXY:<font size='1'><a href='?_src_=vars;mobToDamage=[D.UID()];adjustDamage=oxygen'>[M.getOxyLoss()]</a>
CLONE:<font size='1'><a href='?_src_=vars;mobToDamage=[D.UID()];adjustDamage=clone'>[M.getCloneLoss()]</a>
BRAIN:<font size='1'><a href='?_src_=vars;mobToDamage=[D.UID()];adjustDamage=brain'>[M.getBrainLoss()]</a>
</font>
var/mob/living/L = A
atomsnowflake += "<a href='?_src_=vars;rename=[L.UID()]'><b>[L]</b></a>"
if(L.dir)
atomsnowflake += "<br><font size='1'><a href='?_src_=vars;rotatedatum=[L.UID()];rotatedir=left'><<</a> <a href='?_src_=vars;datumedit=[L.UID()];varnameedit=dir'>[dir2text(L.dir)]</a> <a href='?_src_=vars;rotatedatum=[L.UID()];rotatedir=right'>>></a></font>"
atomsnowflake += {"
<br><font size='1'><a href='?_src_=vars;datumedit=[L.UID()];varnameedit=ckey'>[L.ckey ? L.ckey : "No ckey"]</a> / <a href='?_src_=vars;datumedit=[L.UID()];varnameedit=real_name'>[L.real_name ? L.real_name : "No real name"]</a></font>
<br><font size='1'>
BRUTE:<font size='1'><a href='?_src_=vars;mobToDamage=[L.UID()];adjustDamage=brute'>[L.getBruteLoss()]</a>
FIRE:<font size='1'><a href='?_src_=vars;mobToDamage=[L.UID()];adjustDamage=fire'>[L.getFireLoss()]</a>
TOXIN:<font size='1'><a href='?_src_=vars;mobToDamage=[L.UID()];adjustDamage=toxin'>[L.getToxLoss()]</a>
OXY:<font size='1'><a href='?_src_=vars;mobToDamage=[L.UID()];adjustDamage=oxygen'>[L.getOxyLoss()]</a>
CLONE:<font size='1'><a href='?_src_=vars;mobToDamage=[L.UID()];adjustDamage=clone'>[L.getCloneLoss()]</a>
BRAIN:<font size='1'><a href='?_src_=vars;mobToDamage=[L.UID()];adjustDamage=brain'>[L.getBrainLoss()]</a>
STAMINA:<font size='1'><a href='?_src_=vars;mobToDamage=[L.UID()];adjustDamage=stamina'>[L.getStaminaLoss()]</a>
</font>
"}
else
body += "<a href='?_src_=vars;datumedit=[D.UID()];varnameedit=name'><b>[D]</b></a>"
atomsnowflake += "<a href='?_src_=vars;datumedit=[A.UID()];varnameedit=name'><b>[A]</b></a>"
if(A.dir)
body += "<br><font size='1'><a href='?_src_=vars;rotatedatum=[D.UID()];rotatedir=left'><<</a> <a href='?_src_=vars;datumedit=[D.UID()];varnameedit=dir'>[dir2text(A.dir)]</a> <a href='?_src_=vars;rotatedatum=[D.UID()];rotatedir=right'>>></a></font>"
atomsnowflake += "<br><font size='1'><a href='?_src_=vars;rotatedatum=[A.UID()];rotatedir=left'><<</a> <a href='?_src_=vars;datumedit=[A.UID()];varnameedit=dir'>[dir2text(A.dir)]</a> <a href='?_src_=vars;rotatedatum=[D.UID()];rotatedir=right'>>></a></font>"
else
body += "<b>[D]</b>"
atomsnowflake += "<b>[D]</b>"
body += "</div>"
body += "</tr></td></table>"
var/formatted_type = text("[D.type]")
var/formatted_type = "[type]"
if(length(formatted_type) > 25)
var/middle_point = length(formatted_type) / 2
var/splitpoint = findtext(formatted_type,"/",middle_point)
var/splitpoint = findtext(formatted_type, "/", middle_point)
if(splitpoint)
formatted_type = "[copytext(formatted_type,1,splitpoint)]<br>[copytext(formatted_type,splitpoint)]"
formatted_type = "[copytext(formatted_type, 1, splitpoint)]<br>[copytext(formatted_type, splitpoint)]"
else
formatted_type = "Type too long" //No suitable splitpoint (/) found.
body += "<div align='center'><b><font size='1'>[formatted_type]</font></b>"
if(src.holder && src.holder.marked_datum && src.holder.marked_datum == D)
body += "<br><font size='1' color='red'><b>Marked Object</b></font>"
var/marked
if(holder.marked_datum && holder.marked_datum == D)
marked = "<br><font size='1' color='red'><b>Marked Object</b></font>"
if(D.var_edited)
body += "<br><font size='1' color='red'><b>Var Edited</b></font>"
if(istype(D, /atom))
var/varedited_line = ""
if(isatom(D))
var/atom/A = D
if(A.admin_spawned)
body += "<br><font size='1' color='red'><b>Admin Spawned</b></font>"
body += "</div>"
body += "</div></td>"
body += "<td width='50%'><div align='center'><a href='?_src_=vars;datumrefresh=[D.UID()]'>Refresh</a>"
body += {" <form>
<select name="file" size="1"
onchange="loadPage(this.form.elements\[0\])"
target="_parent._top"
onmouseclick="this.focus()"
style="background-color:#ffffff">
"}
body += {" <option value>Select option</option>
<option value> </option>
"}
varedited_line += "<br><font size='1' color='red'><b>Admin Spawned</b></font>"
body += "<option value='?_src_=vars;mark_object=[D.UID()]'>Mark Object</option>"
body += "<option value='?_src_=vars;proc_call=[D.UID()]'>Call Proc</option>"
body += "<option value='?_src_=vars;jump_to=[D.UID()]'>Jump to Object</option>"
if(ismob(D))
body += "<option value='?_src_=vars;mob_player_panel=[D.UID()]'>Show player panel</option>"
if(!islist && D.var_edited)
varedited_line += "<br><font size='1' color='red'><b>Var Edited</b></font>"
body += "<option value>---</option>"
if(ismob(D))
body += "<option value='?_src_=vars;give_spell=[D.UID()]'>Give Spell</option>"
body += "<option value='?_src_=vars;give_disease=[D.UID()]'>Give Disease</option>"
body += "<option value='?_src_=vars;godmode=[D.UID()]'>Toggle Godmode</option>"
body += "<option value='?_src_=vars;build_mode=[D.UID()]'>Toggle Build Mode</option>"
var/dropdownoptions = list()
if(islist)
dropdownoptions = list(
"---",
"Add Item" = "?_src_=vars;listadd=[refid]",
"Remove Nulls" = "?_src_=vars;listnulls=[refid]",
"Remove Dupes" = "?_src_=vars;listdupes=[refid]",
"Set len" = "?_src_=vars;listlen=[refid]",
"Shuffle" = "?_src_=vars;listshuffle=[refid]"
)
else
dropdownoptions = D.vv_get_dropdown()
body += "<option value='?_src_=vars;make_skeleton=[D.UID()]'>Make 2spooky</option>"
body += "<option value='?_src_=vars;direct_control=[D.UID()]'>Assume Direct Control</option>"
body += "<option value='?_src_=vars;offer_control=[D.UID()]'>Offer Control to Ghosts</option>"
body += "<option value='?_src_=vars;drop_everything=[D.UID()]'>Drop Everything</option>"
var/list/dropdownoptions_html = list()
for(var/name in dropdownoptions)
var/link = dropdownoptions[name]
if(link)
dropdownoptions_html += "<option value='[link]'>[name]</option>"
else
dropdownoptions_html += "<option value>[name]</option>"
body += "<option value='?_src_=vars;regenerateicons=[D.UID()]'>Regenerate Icons</option>"
body += "<option value='?_src_=vars;addlanguage=[D.UID()]'>Add Language</option>"
body += "<option value='?_src_=vars;remlanguage=[D.UID()]'>Remove Language</option>"
body += "<option value='?_src_=vars;addorgan=[D.UID()]'>Add Organ</option>"
body += "<option value='?_src_=vars;remorgan=[D.UID()]'>Remove Organ</option>"
body += "<option value='?_src_=vars;fix_nano=[D.UID()]'>Fix NanoUI</option>"
body += "<option value='?_src_=vars;addverb=[D.UID()]'>Add Verb</option>"
body += "<option value='?_src_=vars;remverb=[D.UID()]'>Remove Verb</option>"
if(ishuman(D))
body += "<option value>---</option>"
body += "<option value='?_src_=vars;setspecies=[D.UID()]'>Set Species</option>"
body += "<option value='?_src_=vars;makeai=[D.UID()]'>Make AI</option>"
body += "<option value='?_src_=vars;makemask=[D.UID()]'>Make Mask of Nar'sie</option>"
body += "<option value='?_src_=vars;makerobot=[D.UID()]'>Make cyborg</option>"
body += "<option value='?_src_=vars;makemonkey=[D.UID()]'>Make monkey</option>"
body += "<option value='?_src_=vars;makealien=[D.UID()]'>Make alien</option>"
body += "<option value='?_src_=vars;makeslime=[D.UID()]'>Make slime</option>"
body += "<option value='?_src_=vars;makesuper=[D.UID()]'>Make superhero</option>"
body += "<option value>---</option>"
body += "<option value='?_src_=vars;gib=[D.UID()]'>Gib</option>"
if(isobj(D))
body += "<option value='?_src_=vars;delall=[D.UID()]'>Delete all of type</option>"
if(isobj(D) || ismob(D) || isturf(D))
body += "<option value='?_src_=vars;addreagent=[D.UID()]'>Add reagent</option>"
body += "<option value='?_src_=vars;explode=[D.UID()]'>Trigger explosion</option>"
body += "<option value='?_src_=vars;emp=[D.UID()]'>Trigger EM pulse</option>"
body += "</select></form>"
body += "</div></td></tr></table></div><hr>"
body += "<font size='1'><b>E</b> - Edit, tries to determine the variable type by itself.<br>"
body += "<b>C</b> - Change, asks you for the var type first.<br>"
body += "<b>M</b> - Mass modify: changes this variable for all objects of this type.</font><br>"
body += "<hr><table width='100%'><tr><td width='20%'><div align='center'><b>Search:</b></div></td><td width='80%'><input type='text' id='filter' name='filter_text' value='' style='width:100%;'></td></tr></table><hr>"
body += "<ol id='vars'>"
var/list/names = list()
for(var/V in D.vars)
names += V
if(!islist)
for(var/V in D.vars)
names += V
names = sortList(names)
for(var/V in names)
body += debug_variable(V, D.vars[V], 0, D)
sleep(1) // Without a sleep here, VV sometimes disconnects clients
body += "</ol>"
var/html = "<html><head>"
if(title)
html += "<title>[title]</title>"
html += {"<style>
body
{
font-family: Verdana, sans-serif;
font-size: 9pt;
}
.value
{
font-family: "Courier New", monospace;
font-size: 8pt;
}
</style>"}
html += "</head><body>"
html += body.Join("")
var/list/variable_html = list()
if(islist)
var/list/L = D
for(var/i in 1 to L.len)
var/key = L[i]
var/value
if(IS_NORMAL_LIST(L) && !isnum(key))
value = L[key]
variable_html += debug_variable(i, value, 0, D)
else
names = sortList(names)
for(var/V in names)
if(D.can_vv_get(V))
variable_html += D.vv_get_var(V)
html += {"
var/html = {"
<html>
<head>
<title>[title]</title>
<style>
body {
font-family: Verdana, sans-serif;
font-size: 9pt;
}
.value {
font-family: "Courier New", monospace;
font-size: 8pt;
}
</style>
</head>
<body onload='selectTextField(); updateSearch()' onkeydown='return checkreload()' onkeyup='updateSearch()'>
<script type="text/javascript">
function checkreload() {
if(event.keyCode == 116){ //F5 (to refresh properly)
document.getElementById("refresh_link").click();
event.preventDefault ? event.preventDefault() : (event.returnValue = false)
return false;
}
return true;
}
function updateSearch(){
var filter_text = document.getElementById('filter');
var filter = filter_text.value.toLowerCase();
if(event.keyCode == 13){ //Enter / return
var vars_ol = document.getElementById('vars');
var lis = vars_ol.getElementsByTagName("li");
for ( var i = 0; i < lis.length; ++i )
{
try{
var li = lis\[i\];
if ( li.style.backgroundColor == "#ffee88" )
{
alist = lis\[i\].getElementsByTagName("a")
if(alist.length > 0){
location.href=alist\[0\].href;
}
}
}catch(err) { }
}
return
}
if(event.keyCode == 38){ //Up arrow
var vars_ol = document.getElementById('vars');
var lis = vars_ol.getElementsByTagName("li");
for ( var i = 0; i < lis.length; ++i )
{
try{
var li = lis\[i\];
if ( li.style.backgroundColor == "#ffee88" )
{
if( (i-1) >= 0){
var li_new = lis\[i-1\];
li.style.backgroundColor = "white";
li_new.style.backgroundColor = "#ffee88";
return
}
}
}catch(err) { }
}
return
}
if(event.keyCode == 40){ //Down arrow
var vars_ol = document.getElementById('vars');
var lis = vars_ol.getElementsByTagName("li");
for ( var i = 0; i < lis.length; ++i )
{
try{
var li = lis\[i\];
if ( li.style.backgroundColor == "#ffee88" )
{
if( (i+1) < lis.length){
var li_new = lis\[i+1\];
li.style.backgroundColor = "white";
li_new.style.backgroundColor = "#ffee88";
return
}
}
}catch(err) { }
}
return
}
//This part here resets everything to how it was at the start so the filter is applied to the complete list. Screw efficiency, it's client-side anyway and it only looks through 200 or so variables at maximum anyway (mobs).
if(complete_list != null && complete_list != ""){
var vars_ol1 = document.getElementById("vars");
vars_ol1.innerHTML = complete_list
}
document.cookie="[refid][cookieoffset]search="+encodeURIComponent(filter);
if(filter == ""){
return;
}else{
var vars_ol = document.getElementById('vars');
var lis = vars_ol.getElementsByTagName("li");
for ( var i = 0; i < lis.length; ++i )
{
try{
var li = lis\[i\];
if ( li.innerText.toLowerCase().indexOf(filter) == -1 )
{
vars_ol.removeChild(li);
i--;
}
}catch(err) { }
}
}
var lis_new = vars_ol.getElementsByTagName("li");
for ( var j = 0; j < lis_new.length; ++j )
{
var li1 = lis\[j\];
if (j == 0){
li1.style.backgroundColor = "#ffee88";
}else{
li1.style.backgroundColor = "white";
}
}
}
function selectTextField() {
var filter_text = document.getElementById('filter');
filter_text.focus();
filter_text.select();
var lastsearch = getCookie("[refid][cookieoffset]search");
if (lastsearch) {
filter_text.value = lastsearch;
updateSearch();
}
}
function loadPage(list) {
if(list.options\[list.selectedIndex\].value == ""){
return;
}
location.href=list.options\[list.selectedIndex\].value;
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca\[i\];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return "";
}
</script>
<div align='center'>
<table width='100%'>
<tr>
<td width='50%'>
<table align='center' width='100%'>
<tr>
<td>
[sprite_text]
<div align='center'>
[atomsnowflake.Join()]
</div>
</td>
</tr>
</table>
<div align='center'>
<b><font size='1'>[formatted_type]</font></b>
[marked]
[varedited_line]
</div>
</td>
<td width='50%'>
<div align='center'>
<a id='refresh_link' href='?_src_=vars;[islist ? "listrefresh=\ref[D]" : "datumrefresh=[D.UID()]"]'>Refresh</a>
<form>
<select name="file" size="1"
onchange="loadPage(this.form.elements\[0\])"
target="_parent._top"
onmouseclick="this.focus()"
style="background-color:#ffffff">
<option value selected>Select option</option>
[dropdownoptions_html.Join()]
</select>
</form>
</div>
</td>
</tr>
</table>
</div>
<hr>
<font size='1'>
<b>E</b> - Edit, tries to determine the variable type by itself.<br>
<b>C</b> - Change, asks you for the var type first.<br>
<b>M</b> - Mass modify: changes this variable for all objects of this type.<br>
</font>
<hr>
<table width='100%'>
<tr>
<td width='20%'>
<div align='center'>
<b>Search:</b>
</div>
</td>
<td width='80%'>
<input type='text' id='filter' name='filter_text' value='' style='width:100%;'>
</td>
</tr>
</table>
<hr>
<ol id='vars'>
[variable_html.Join()]
</ol>
<script type='text/javascript'>
var vars_ol = document.getElementById("vars");
var complete_list = vars_ol.innerHTML;
</script>
</body>
</html>
"}
html += "</body></html>"
usr << browse(html, "window=variables[D.UID()];size=475x650")
return
/client/proc/debug_variable(name, value, level, var/datum/DA = null)
var/list/html = list()
usr << browse(html, "window=variables[refid];size=475x650")
#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing )
/proc/debug_variable(name, value, level, var/datum/DA = null, sanitize = TRUE)
var/header
if(DA)
html += "<li style='backgroundColor:white'>(<a href='?_src_=vars;datumedit=[DA.UID()];varnameedit=[name]'>E</a>) (<a href='?_src_=vars;datumchange=[DA.UID()];varnamechange=[name]'>C</a>) (<a href='?_src_=vars;datummass=[DA.UID()];varnamemass=[name]'>M</a>) "
if(islist(DA))
var/index = name
if(value)
name = DA[name] // name is really the index until this line
else
value = DA[name]
header = "<li style='backgroundColor:white'>(<a href='?_src_=vars;listedit=\ref[DA];index=[index]'>E</a>) (<a href='?_src_=vars;listchange=\ref[DA];index=[index]'>C</a>) (<a href='?_src_=vars;listremove=\ref[DA];index=[index]'>-</a>) "
else
header = "<li style='backgroundColor:white'>(<a href='?_src_=vars;datumedit=[DA.UID()];varnameedit=[name]'>E</a>) (<a href='?_src_=vars;datumchange=[DA.UID()];varnamechange=[name]'>C</a>) (<a href='?_src_=vars;datummass=[DA.UID()];varnamemass=[name]'>M</a>) "
else
html += "<li>"
header = "<li>"
var/item
if(isnull(value))
html += "[name] = <span class='value'>null</span>"
item = "[VV_HTML_ENCODE(name)] = <span class='value'>null</span>"
else if(istext(value))
html += "[name] = <span class='value'>\"[value]\"</span>"
item = "[VV_HTML_ENCODE(name)] = <span class='value'>\"[VV_HTML_ENCODE(value)]\"</span>"
else if(isicon(value))
#ifdef VARSICON
html += "[name] = /icon (<span class='value'>[value]</span>) [bicon(value, use_class=0)]"
item = "[name] = /icon (<span class='value'>[value]</span>) [bicon(value, use_class=0)]"
#else
html += "[name] = /icon (<span class='value'>[value]</span>)"
item = "[name] = /icon (<span class='value'>[value]</span>)"
#endif
else if(istype(value, /image))
var/image/I = value
#ifdef VARSICON
html += "<a href='?_src_=vars;Vars=[I.UID()]'>[name] \ref[value]</a> = /image (<span class='value'>[value]</span>) [bicon(value, use_class=0)]"
item = "<a href='?_src_=vars;Vars=[I.UID()]'>[name] \ref[value]</a> = /image (<span class='value'>[value]</span>) [bicon(value, use_class=0)]"
#else
html += "<a href='?_src_=vars;Vars=[I.UID()]'>[name] \ref[value]</a> = /image (<span class='value'>[value]</span>)"
#endif
item = "<a href='?_src_=vars;Vars=[I.UID()]'>[name] \ref[value]</a> = /image (<span class='value'>[value]</span>)"
#endif
else if(isfile(value))
html += "[name] = <span class='value'>'[value]'</span>"
item = "[VV_HTML_ENCODE(name)] = <span class='value'>'[value]'</span>"
else if(istype(value, /datum))
var/datum/D = value
html += "<a href='?_src_=vars;Vars=[D.UID()]'>[name] \ref[value]</a> = [D.type]"
item = "<a href='?_src_=vars;Vars=[D.UID()]'>[VV_HTML_ENCODE(name)] \ref[value]</a> = [D.type]"
else if(istype(value, /client))
var/client/C = value
html += "<a href='?_src_=vars;Vars=[C.UID()]'>[name] \ref[value]</a> = [C] [C.type]"
item = "<a href='?_src_=vars;Vars=[C.UID()]'>[VV_HTML_ENCODE(name)] \ref[value]</a> = [C] [C.type]"
//
else if(istype(value, /list))
else if(islist(value))
var/list/L = value
html += "[name] = /list ([L.len])"
var/list/items = list()
if(L.len > 0 && !(name == "underlays" || name == "overlays" || name == "vars" || L.len > 500))
// not sure if this is completely right...
if(0) //(L.vars.len > 0)
html += "<ol>"
html += "</ol>"
else
html += "<ul>"
var/index = 1
for(var/entry in L)
if(istext(entry))
html += debug_variable(entry, L[entry], level + 1)
else
html += debug_variable(index, L[index], level + 1)
index++
html += "</ul>"
if(L.len > 0 && !(name == "underlays" || name == "overlays" || name == "vars" || L.len > (IS_NORMAL_LIST(L) ? 50 : 150)))
for(var/i in 1 to L.len)
var/key = L[i]
var/val
if(IS_NORMAL_LIST(L) && !isnum(key))
val = L[key]
if(!val)
val = key
key = i
items += debug_variable(key, val, level + 1, sanitize = sanitize)
item = "<a href='?_src_=vars;VarsList=\ref[L]'>[VV_HTML_ENCODE(name)] = /list ([L.len])</a><ul>[items.Join()]</ul>"
else
item = "<a href='?_src_=vars;VarsList=\ref[L]'>[VV_HTML_ENCODE(name)] = /list ([L.len])</a>"
else
html += "[name] = <span class='value'>[value]</span>"
/*
// Bitfield stuff
if(round(value)==value) // Require integers.
var/idx=0
var/bit=0
var/bv=0
html += "<div class='value binary'>"
for(var/block=0;block<8;block++)
html += " <span class='block'>"
for(var/i=0;i<4;i++)
idx=(block*4)+i
to_chat(bit=1, idx)
bv=value & bit
html += "<a href='?_src_=vars;togbit=[idx];var=[name];subject=[DA.UID()]' title='bit [idx] ([bit])'>[bv?1:0]</a>"
html += "</span>"
html += "</div>"
*/
html += "</li>"
item = "[VV_HTML_ENCODE(name)] = <span class='value'>[VV_HTML_ENCODE(value)]</span>"
return html.Join("")
return "[header][item]</li>"
#undef VV_HTML_ENCODE
/client/proc/view_var_Topic(href, href_list, hsrc)
//This should all be moved over to datum/admins/Topic() or something ~Carn
if(!check_rights(R_ADMIN|R_MOD))
return
if(view_var_Topic_list(href, href_list, hsrc)) // done because you can't use UIDs with lists and I don't want to snowflake into the below check to supress warnings
return
// Correct and warn about any VV topic links that aren't using UIDs
for(var/paramname in href_list)
if(findtext(href_list[paramname], "]_"))
@@ -516,6 +592,7 @@ body
cmd_mass_modify_object_variables(A, href_list["varnamemass"])
else if(href_list["mob_player_panel"])
if(!check_rights(R_ADMIN|R_MOD)) return
@@ -1099,6 +1176,7 @@ body
if("oxygen")L.adjustOxyLoss(amount)
if("brain") L.adjustBrainLoss(amount)
if("clone") L.adjustCloneLoss(amount)
if("stamina") L.adjustStaminaLoss(amount)
else
to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]")
return
@@ -1110,8 +1188,123 @@ body
if(href_list["datumrefresh"])
var/datum/DAT = locateUID(href_list["datumrefresh"])
if(!istype(DAT, /datum))
if(!istype(DAT, /datum) && !isclient(DAT))
return
src.debug_variables(DAT)
return
/client/proc/view_var_Topic_list(href, href_list, hsrc)
if(href_list["VarsList"])
debug_variables(locate(href_list["VarsList"]))
return TRUE
if(href_list["listedit"] && href_list["index"])
var/index = text2num(href_list["index"])
if(!index)
return TRUE
var/list/L = locate(href_list["listedit"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return
mod_list(L, null, "list", "contents", index, autodetect_class = TRUE)
return TRUE
if(href_list["listchange"] && href_list["index"])
var/index = text2num(href_list["index"])
if(!index)
return TRUE
var/list/L = locate(href_list["listchange"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return
mod_list(L, null, "list", "contents", index, autodetect_class = FALSE)
return TRUE
if(href_list["listremove"] && href_list["index"])
var/index = text2num(href_list["index"])
if(!index)
return TRUE
var/list/L = locate(href_list["listremove"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return
var/variable = L[index]
var/prompt = alert("Do you want to remove item number [index] from list?", "Confirm", "Yes", "No")
if(prompt != "Yes")
return
L.Cut(index, index+1)
log_to_dd("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]")
log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]")
message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]")
return TRUE
if(href_list["listadd"])
var/list/L = locate(href_list["listadd"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return TRUE
mod_list_add(L, null, "list", "contents")
return TRUE
if(href_list["listdupes"])
var/list/L = locate(href_list["listdupes"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return TRUE
uniqueList_inplace(L)
log_to_dd("### ListVarEdit by [src]: /list contents: CLEAR DUPES")
log_admin("[key_name(src)] modified list's contents: CLEAR DUPES")
message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES")
return TRUE
if(href_list["listnulls"])
var/list/L = locate(href_list["listnulls"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return TRUE
listclearnulls(L)
log_to_dd("### ListVarEdit by [src]: /list contents: CLEAR NULLS")
log_admin("[key_name(src)] modified list's contents: CLEAR NULLS")
message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS")
return TRUE
if(href_list["listlen"])
var/list/L = locate(href_list["listlen"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return TRUE
var/value = vv_get_value(VV_NUM)
if(value["class"] != VV_NUM)
return TRUE
L.len = value["value"]
log_to_dd("### ListVarEdit by [src]: /list len: [L.len]")
log_admin("[key_name(src)] modified list's len: [L.len]")
message_admins("[key_name_admin(src)] modified list's len: [L.len]")
return TRUE
if(href_list["listshuffle"])
var/list/L = locate(href_list["listshuffle"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return TRUE
shuffle_inplace(L)
log_to_dd("### ListVarEdit by [src]: /list contents: SHUFFLE")
log_admin("[key_name(src)] modified list's contents: SHUFFLE")
message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE")
return TRUE
if(href_list["listrefresh"])
debug_variables(locate(href_list["listrefresh"]))
return TRUE