html_interface removal and crew monitoring console refactor.

This commit is contained in:
AnturK
2018-02-15 21:31:21 +01:00
committed by CitadelStationBot
parent 1cda9ef2b4
commit 35b8e410be
41 changed files with 268 additions and 2416 deletions
-12
View File
@@ -143,10 +143,6 @@
var/datum/atom_hud/data/human/medical/basic/B = GLOB.huds[DATA_HUD_MEDICAL_BASIC]
B.update_suit_sensors(src)
var/turf/T = get_turf(src)
if (T)
GLOB.crewmonitor.queueUpdate(T.z)
//called when a living mob changes health
/mob/living/proc/med_hud_set_health()
var/image/holder = hud_list[HEALTH_HUD]
@@ -158,10 +154,6 @@
/mob/living/carbon/med_hud_set_health()
..()
var/turf/T = get_turf(src)
if(T)
GLOB.crewmonitor.queueUpdate(T.z)
//called when a carbon changes stat, virus or XENO_HOST
/mob/living/proc/med_hud_set_status()
var/image/holder = hud_list[STATUS_HUD]
@@ -216,10 +208,6 @@
holder.icon_state = "hud[ckey(wear_id.GetJobName())]"
sec_hud_set_security_status()
var/turf/T = get_turf(src)
if (T)
GLOB.crewmonitor.queueUpdate(T.z)
/mob/living/carbon/human/proc/sec_hud_set_implants()
var/image/holder
for(var/i in list(IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD))
-15
View File
@@ -456,21 +456,6 @@ Class Procs:
/obj/machinery/proc/on_deconstruction()
return
// Hook for html_interface module to prevent updates to clients who don't have this as their active machine.
/obj/machinery/proc/hiIsValidClient(datum/html_interface_client/hclient, datum/html_interface/hi)
if (hclient.client.mob && (hclient.client.mob.stat == 0 || IsAdminGhost(hclient.client.mob)))
if (isAI(hclient.client.mob) || IsAdminGhost(hclient.client.mob))
return TRUE
else
return hclient.client.mob.machine == src && Adjacent(hclient.client.mob)
else
return FALSE
// Hook for html_interface module to unset the active machine when the window is closed by the player.
/obj/machinery/proc/hiOnHide(datum/html_interface_client/hclient)
if (hclient.client.mob && hclient.client.mob.machine == src)
hclient.client.mob.unset_machine()
/obj/machinery/proc/can_be_overridden()
. = 1
+5 -1
View File
@@ -20,13 +20,14 @@
switchCamera(cameras[camera])
/datum/trackable
var/initialized = FALSE
var/list/names = list()
var/list/namecounts = list()
var/list/humans = list()
var/list/others = list()
/mob/living/silicon/ai/proc/trackable_mobs()
track.initialized = TRUE
track.names.Cut()
track.namecounts.Cut()
track.humans.Cut()
@@ -63,6 +64,9 @@
if(!target_name)
return
if(!track.initialized)
trackable_mobs()
var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name])
ai_actual_track(target)
-154
View File
@@ -1,154 +0,0 @@
body
{
padding-left: 53%;
cursor: default;
}
#ntbgcenter
{
background-position: 550px 0px !important;
}
#minimap
{
position: fixed;
top: 8px;
left: 8px;
border: 2px inset #888;
overflow: hidden;
min-width: 480px;
min-height: 480px;
width: 53%;
height: 100%;
max-width: 480px;
max-height: 480px;
}
#textbased
{
width: 100%;
padding-left: 20px;
}
#textbased table
{
min-width: 380px;
width: 100%;
table-layout: fixed;
}
#textbased td
{
vertical-align: top;
padding: 2px;
}
#textbased tbody td
{
transition: .2s all;
}
#textbased tbody tr:hover td, #textbased tbody tr.hover td
{
background-color: #515151;
}
#textarea:after
{
content: "";
clear: both;
}
.health
{
width: 16px;
height: 16px;
background-color: #FFF;
border: 1px solid #434343;
position: relative;
top: 2px;
}
.health-5 { background-color: #17d568; }
.health-4 { background-color: #2ecc71; }
.health-3 { background-color: #e67e22; }
.health-2 { background-color: #ed5100; }
.health-1 { background-color: #e74c3c; }
.health-0 { background-color: #ed2814; }
.health > div
{
margin-left: 20px;
margin-top: -4px;
}
.tt
{
position: relative;
display: inline-block;
height: 16px;
}
.tt > div
{
display: none;
}
.tt:hover > div
{
position: absolute;
bottom: -30px;
left: 50%;
margin-left: -64px;
display: block;
width: 128px;
height: 24px;
border: 1px solid #313131;
background-color: #434343;
padding: 4px;
z-index: 999;
text-align: center;
}
.tt > div > span
{
position: relative;
top: -2px;
}
.dot
{
position: absolute;
width: 3px;
height: 3px;
margin-top: 0px;
margin-left: 0px;
border: 1px solid transparent;
}
.dot.active
{
z-index: 9999 !important;
width: 8px;
height: 8px;
margin-top: -2px;
margin-left: -3px;
}
.zoom
{
position: fixed;
left: 53%;
top: 10px;
display: block;
width: 24px;
height: 24px;
text-align: center;
margin-left: 20px !important;
}
.zoom.in
{
margin-left: -3px !important;
}
+103 -177
View File
@@ -1,3 +1,5 @@
#define SENSORS_UPDATE_PERIOD 100 //How often the sensor data updates.
/obj/machinery/computer/crew
name = "crew monitoring console"
desc = "Used to monitor active health sensors built into most of the crew's uniforms."
@@ -16,21 +18,22 @@
/obj/machinery/computer/crew/attack_ai(mob/user)
if(stat & (BROKEN|NOPOWER))
return
GLOB.crewmonitor.show(user)
GLOB.crewmonitor.show(user,src)
/obj/machinery/computer/crew/attack_hand(mob/user)
if(..())
return
if(stat & (BROKEN|NOPOWER))
return
GLOB.crewmonitor.show(user)
GLOB.crewmonitor.show(user,src)
GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
/datum/crewmonitor
var/list/ui_sources = list() //List of user -> ui source
var/list/jobs
var/list/interfaces
var/list/data
var/list/data_by_z = list()
var/list/last_update = list()
/datum/crewmonitor/New()
. = ..()
@@ -77,196 +80,119 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
jobs["Assistant"] = 999 //Unknowns/custom jobs should appear after civilians, and before assistants
src.jobs = jobs
src.interfaces = list()
src.data = list()
register_asset("crewmonitor.js",'crew.js')
register_asset("crewmonitor.css",'crew.css')
/datum/crewmonitor/Destroy()
if (src.interfaces)
for (var/datum/html_interface/hi in interfaces)
qdel(hi)
src.interfaces = null
return ..()
/datum/crewmonitor/proc/show(mob/mob, z)
if (mob.client)
sendResources(mob.client)
if (!z)
z = mob.z
/datum/crewmonitor/ui_interact(mob/user, ui_key = "crew", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if (!ui)
ui = new(user, src, ui_key, "crew", "crew monitor", 800, 600 , master_ui, state)
ui.open()
if (z > 0 && src.interfaces)
var/datum/html_interface/hi
/datum/crewmonitor/proc/show(mob/M, source)
ui_sources[M] = source
ui_interact(M)
if (!src.interfaces["[z]"])
src.interfaces["[z]"] = new/datum/html_interface/nanotrasen(src, "Crew Monitoring", 900, 540, "<link rel=\"stylesheet\" type=\"text/css\" href=\"crewmonitor.css\" /><script type=\"text/javascript\">var z = [z]; var tile_size = [world.icon_size]; var maxx = [world.maxx]; var maxy = [world.maxy];</script><script type=\"text/javascript\" src=\"crewmonitor.js\"></script>")
/datum/crewmonitor/ui_host(mob/user)
return ui_sources[user]
hi = src.interfaces["[z]"]
/datum/crewmonitor/ui_data(mob/user)
var/z = user.z
if(!z)
var/turf/T = get_turf(user)
z = T.z
var/list/zdata = update_data(z)
. = list()
.["sensors"] = zdata
.["link_allowed"] = isAI(user)
hi.updateContent("content", "<div id=\"minimap\"><a href=\"javascript:zoomIn();\" class=\"zoom in\">+</a><a href=\"javascript:zoomOut();\" class=\"zoom\">-</a></div><div id=\"textbased\"></div>")
/datum/crewmonitor/proc/update_data(z)
if(data_by_z["[z]"] && last_update["[z]"] && world.time <= last_update["[z]"] + SENSORS_UPDATE_PERIOD)
return data_by_z["[z]"]
src.update(z, TRUE)
else
hi = src.interfaces["[z]"]
src.update(z,TRUE)
var/list/results = list()
var/obj/item/clothing/under/U
var/obj/item/card/id/I
var/turf/pos
var/ijob
var/name
var/assignment
var/oxydam
var/toxdam
var/burndam
var/brutedam
var/area
var/pos_x
var/pos_y
var/life_status
// Debugging purposes
mob << browse_rsc(file("code/game/machinery/computer/crew.js"), "crew.js")
mob << browse_rsc(file("code/game/machinery/computer/crew.css"), "crew.css")
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
// Check if their z-level is correct and if they are wearing a uniform.
// Accept H.z==0 as well in case the mob is inside an object.
if ((H.z == 0 || H.z == z) && istype(H.w_uniform, /obj/item/clothing/under))
U = H.w_uniform
hi = src.interfaces["[z]"]
hi.show(mob)
src.updateFor(mob, hi, z)
// Are the suit sensors on?
if ((U.has_sensor > 0) && U.sensor_mode)
pos = H.z == 0 || U.sensor_mode == SENSOR_COORDS ? get_turf(H) : null
/datum/crewmonitor/proc/updateFor(hclient_or_mob, datum/html_interface/hi, z)
// This check will succeed if updateFor is called after showing to the player, but will fail
// on regular updates. Since we only really need this once we don't care if it fails.
hi.callJavaScript("clearAll", null, hclient_or_mob)
// Special case: If the mob is inside an object confirm the z-level on turf level.
if (H.z == 0 && (!pos || pos.z != z))
continue
for (var/list/L in data)
hi.callJavaScript("add", L, hclient_or_mob)
I = H.wear_id ? H.wear_id.GetID() : null
hi.callJavaScript("onAfterUpdate", null, hclient_or_mob)
if (I)
name = I.registered_name
assignment = I.assignment
ijob = jobs[I.assignment]
else
name = "Unknown"
assignment = ""
ijob = 80
/datum/crewmonitor/proc/update(z, ignore_unused = FALSE)
if (src.interfaces["[z]"])
var/datum/html_interface/hi = src.interfaces["[z]"]
if (U.sensor_mode >= SENSOR_LIVING)
life_status = (!H.stat ? TRUE : FALSE)
else
life_status = null
if (ignore_unused || hi.isUsed())
var/list/results = list()
var/obj/item/clothing/under/U
var/obj/item/card/id/I
var/turf/pos
var/ijob
var/name
var/assignment
var/dam1
var/dam2
var/dam3
var/dam4
var/area
var/pos_x
var/pos_y
var/life_status
if (U.sensor_mode >= SENSOR_VITALS)
oxydam = round(H.getOxyLoss(),1)
toxdam = round(H.getToxLoss(),1)
burndam = round(H.getFireLoss(),1)
brutedam = round(H.getBruteLoss(),1)
else
oxydam = null
toxdam = null
burndam = null
brutedam = null
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
// Check if their z-level is correct and if they are wearing a uniform.
// Accept H.z==0 as well in case the mob is inside an object.
if ((H.z == 0 || H.z == z) && istype(H.w_uniform, /obj/item/clothing/under))
U = H.w_uniform
if (U.sensor_mode >= SENSOR_COORDS)
if (!pos)
pos = get_turf(H)
area = get_area_name(H, TRUE)
pos_x = pos.x
pos_y = pos.y
else
area = null
pos_x = null
pos_y = null
// Are the suit sensors on?
if ((U.has_sensor > 0) && U.sensor_mode)
pos = H.z == 0 || U.sensor_mode == SENSOR_COORDS ? get_turf(H) : null
results[++results.len] = list("name" = name, "assignment" = assignment, "ijob" = ijob, "life_status" = life_status, "oxydam" = oxydam, "toxdam" = toxdam, "burndam" = burndam, "brutedam" = brutedam, "area" = area, "pos_x" = pos_x, "pos_y" = pos_y, "can_track" = H.can_track(null))
data_by_z["[z]"] = results
last_update["[z]"] = world.time
return results
// Special case: If the mob is inside an object confirm the z-level on turf level.
if (H.z == 0 && (!pos || pos.z != z))
continue
/datum/crewmonitor/ui_act(action,params)
var/mob/living/silicon/ai/AI = usr
if(!istype(AI))
return
switch (action)
if ("select_person")
AI.ai_camera_track(params["name"])
I = H.wear_id ? H.wear_id.GetID() : null
if (I)
name = I.registered_name
assignment = I.assignment
ijob = jobs[I.assignment]
else
name = "<i>Unknown</i>"
assignment = ""
ijob = 80
if (U.sensor_mode >= SENSOR_LIVING)
life_status = (!H.stat ? "true" : "false")
else
life_status = null
if (U.sensor_mode >= SENSOR_VITALS)
dam1 = round(H.getOxyLoss(),1)
dam2 = round(H.getToxLoss(),1)
dam3 = round(H.getFireLoss(),1)
dam4 = round(H.getBruteLoss(),1)
else
dam1 = null
dam2 = null
dam3 = null
dam4 = null
if (U.sensor_mode >= SENSOR_COORDS)
if (!pos)
pos = get_turf(H)
area = get_area_name(H, TRUE)
pos_x = pos.x
pos_y = pos.y
else
area = null
pos_x = null
pos_y = null
results[++results.len] = list(name, assignment, ijob, life_status, dam1, dam2, dam3, dam4, area, pos_x, pos_y, H.can_track(null))
src.data = results
src.updateFor(null, hi, z) // updates for everyone
/datum/crewmonitor/proc/hiIsValidClient(datum/html_interface_client/hclient, datum/html_interface/hi)
var/z = ""
for (z in src.interfaces)
if (src.interfaces[z] == hi)
break
if(hclient.client.mob && IsAdminGhost(hclient.client.mob))
return TRUE
if (hclient.client.mob && hclient.client.mob.stat == 0 && hclient.client.mob.z == text2num(z))
if (isAI(hclient.client.mob))
return TRUE
else if (iscyborg(hclient.client.mob))
return (locate(/obj/machinery/computer/crew, range(world.view, hclient.client.mob))) || (locate(/obj/item/device/sensor_device, hclient.client.mob.contents))
else
return (locate(/obj/machinery/computer/crew, range(1, hclient.client.mob))) || (locate(/obj/item/device/sensor_device, hclient.client.mob.contents))
else
return FALSE
/datum/crewmonitor/Topic(href, href_list[], datum/html_interface_client/hclient)
if (istype(hclient))
if (hclient && hclient.client && hclient.client.mob && isAI(hclient.client.mob))
var/mob/living/silicon/ai/AI = hclient.client.mob
switch (href_list["action"])
if ("select_person")
AI.ai_camera_track(href_list["name"])
if ("select_position")
var/x = text2num(href_list["x"])
var/y = text2num(href_list["y"])
var/turf/tile = locate(x, y, AI.z)
var/obj/machinery/camera/C = locate(/obj/machinery/camera) in range(5, tile)
if (!C)
C = locate(/obj/machinery/camera) in urange(10, tile)
if (!C)
C = locate(/obj/machinery/camera) in urange(15, tile)
if (C)
addtimer(CALLBACK(src, .proc/update_ai, AI, C, AI.eyeobj.loc), min(30, get_dist(get_turf(C), AI.eyeobj) / 4))
/datum/crewmonitor/proc/update_ai(mob/living/silicon/ai/AI, obj/machinery/camera/C, turf/current_loc)
if (AI && AI.eyeobj && current_loc == AI.eyeobj.loc)
AI.switchCamera(C)
/mob/living/carbon/human/Move()
var/old_z = src.z
. = ..()
if (src.w_uniform)
if (old_z != src.z)
GLOB.crewmonitor.queueUpdate(old_z)
GLOB.crewmonitor.queueUpdate(src.z)
/datum/crewmonitor/proc/queueUpdate(z)
addtimer(CALLBACK(src, .proc/update, z), 5, TIMER_UNIQUE)
/datum/crewmonitor/proc/sendResources(var/client/client)
send_asset(client, "crewmonitor.js")
send_asset(client, "crewmonitor.css")
SSminimap.send(client)
#undef SENSORS_UPDATE_PERIOD
-588
View File
@@ -1,588 +0,0 @@
/*!
* jQuery scrollintoview() plugin and :scrollable selector filter
*
* Version 1.8 (14 Jul 2011)
* Requires jQuery 1.4 or newer
*/
(function ($) {
var converter = {
vertical: { x: false, y: true },
horizontal: { x: true, y: false },
both: { x: true, y: true },
x: { x: true, y: false },
y: { x: false, y: true }
};
var settings = {
duration: "fast",
direction: "both"
};
var rootrx = /^(?:html)$/i;
// gets border dimensions
var borders = function (domElement, styles) {
styles = styles || (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(domElement, null) : domElement.currentStyle);
var px = document.defaultView && document.defaultView.getComputedStyle ? true : false;
var b = {
top: (parseFloat(px ? styles.borderTopWidth : $.css(domElement, "borderTopWidth")) || 0),
left: (parseFloat(px ? styles.borderLeftWidth : $.css(domElement, "borderLeftWidth")) || 0),
bottom: (parseFloat(px ? styles.borderBottomWidth : $.css(domElement, "borderBottomWidth")) || 0),
right: (parseFloat(px ? styles.borderRightWidth : $.css(domElement, "borderRightWidth")) || 0)
};
return {
top: b.top,
left: b.left,
bottom: b.bottom,
right: b.right,
vertical: b.top + b.bottom,
horizontal: b.left + b.right
};
};
var dimensions = function ($element) {
var win = $(window);
var isRoot = rootrx.test($element[0].nodeName);
return {
border: isRoot ? { top: 0, left: 0, bottom: 0, right: 0} : borders($element[0]),
scroll: {
top: (isRoot ? win : $element).scrollTop(),
left: (isRoot ? win : $element).scrollLeft()
},
scrollbar: {
right: isRoot ? 0 : $element.innerWidth() - $element[0].clientWidth,
bottom: isRoot ? 0 : $element.innerHeight() - $element[0].clientHeight
},
rect: (function () {
var r = $element[0].getBoundingClientRect();
return {
top: isRoot ? 0 : r.top,
left: isRoot ? 0 : r.left,
bottom: isRoot ? $element[0].clientHeight : r.bottom,
right: isRoot ? $element[0].clientWidth : r.right
};
})()
};
};
$.fn.extend({
scrollintoview: function (options) {
/// <summary>Scrolls the first element in the set into view by scrolling its closest scrollable parent.</summary>
/// <param name="options" type="Object">Additional options that can configure scrolling:
/// duration (default: "fast") - jQuery animation speed (can be a duration string or number of milliseconds)
/// direction (default: "both") - select possible scrollings ("vertical" or "y", "horizontal" or "x", "both")
/// complete (default: none) - a function to call when scrolling completes (called in context of the DOM element being scrolled)
/// </param>
/// <return type="jQuery">Returns the same jQuery set that this function was run on.</return>
options = $.extend({}, settings, options);
options.direction = converter[typeof (options.direction) === "string" && options.direction.toLowerCase()] || converter.both;
var dirStr = "";
if (options.direction.x === true) dirStr = "horizontal";
if (options.direction.y === true) dirStr = dirStr ? "both" : "vertical";
var el = this.eq(0);
var scroller = el.closest(":scrollable(" + dirStr + ")");
// check if there's anything to scroll in the first place
if (scroller.length > 0)
{
scroller = scroller.eq(0);
var dim = {
e: dimensions(el),
s: dimensions(scroller)
};
var rel = {
top: dim.e.rect.top - (dim.s.rect.top + dim.s.border.top),
bottom: dim.s.rect.bottom - dim.s.border.bottom - dim.s.scrollbar.bottom - dim.e.rect.bottom,
left: dim.e.rect.left - (dim.s.rect.left + dim.s.border.left),
right: dim.s.rect.right - dim.s.border.right - dim.s.scrollbar.right - dim.e.rect.right
};
var animOptions = {};
// vertical scroll
if (options.direction.y === true)
{
if (rel.top < 0)
{
animOptions.scrollTop = dim.s.scroll.top + rel.top;
}
else if (rel.top > 0 && rel.bottom < 0)
{
animOptions.scrollTop = dim.s.scroll.top + Math.min(rel.top, -rel.bottom);
}
}
// horizontal scroll
if (options.direction.x === true)
{
if (rel.left < 0)
{
animOptions.scrollLeft = dim.s.scroll.left + rel.left;
}
else if (rel.left > 0 && rel.right < 0)
{
animOptions.scrollLeft = dim.s.scroll.left + Math.min(rel.left, -rel.right);
}
}
// scroll if needed
if (!$.isEmptyObject(animOptions))
{
if (rootrx.test(scroller[0].nodeName))
{
scroller = $("html,body");
}
scroller
.animate(animOptions, options.duration)
.eq(0) // we want function to be called just once (ref. "html,body")
.queue(function (next) {
$.isFunction(options.complete) && options.complete.call(scroller[0]);
next();
});
}
else
{
// when there's nothing to scroll, just call the "complete" function
$.isFunction(options.complete) && options.complete.call(scroller[0]);
}
}
// return set back
return this;
}
});
var scrollValue = {
auto: true,
scroll: true,
visible: false,
hidden: false
};
$.extend($.expr[":"], {
scrollable: function (element, index, meta, stack) {
var direction = converter[typeof (meta[3]) === "string" && meta[3].toLowerCase()] || converter.both;
var styles = (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(element, null) : element.currentStyle);
var overflow = {
x: scrollValue[styles.overflowX.toLowerCase()] || false,
y: scrollValue[styles.overflowY.toLowerCase()] || false,
isRoot: rootrx.test(element.nodeName)
};
// check if completely unscrollable (exclude HTML element because it's special)
if (!overflow.x && !overflow.y && !overflow.isRoot)
{
return false;
}
var size = {
height: {
scroll: element.scrollHeight,
client: element.clientHeight
},
width: {
scroll: element.scrollWidth,
client: element.clientWidth
},
// check overflow.x/y because iPad (and possibly other tablets) don't dislay scrollbars
scrollableX: function () {
return (overflow.x || overflow.isRoot) && this.width.scroll > this.width.client;
},
scrollableY: function () {
return (overflow.y || overflow.isRoot) && this.height.scroll > this.height.client;
}
};
return direction.y && size.scrollableY() || direction.x && size.scrollableX();
}
});
})(jQuery);
/*!
* Crew manifest script
*/
var minimap_height = 480;
var scale_x;
var scale_y;
var zoom_factor = null;
var minimap_mousedown = false;
var minimap_mousedown_scrollLeft;
var minimap_mousedown_scrollTop;
var minimap_mousedown_clientX;
var minimap_mousedown_clientY;
var minimap_mousedown_counter = 0;
function disableSelection(){ return false; };
$(window).on("onUpdateContent", function()
{
$("#textbased").html("<table><colgroup><col /><col style=\"width: 24px;\" class=\"colhealth\" /><col style=\"width: 180px;\" /></colgroup><thead><tr><td><h3>Name</h3></td><td><h3>&nbsp;</h3></td><td><h3>Position</h3></td></tr></thead><tbody id=\"textbased-tbody\"></tbody></table>");
$("#minimap").append("<img src=\"minimap_" + z + ".png\" id=\"map\" style=\"width: auto; height: " + minimap_height + "px;\" />");
$("body")[0].onselectstart = disableSelection;
$("#minimap").on("click", function(e)
{
if (!$(e.target).is(".zoom,.dot"))
{
var x = ((((e.clientX + this.scrollLeft - 8) / scale_x) / tile_size) + 1).toFixed(0);
var y = ((maxy - (((e.clientY + this.scrollTop - 8) / scale_y) / tile_size)) + 1).toFixed(0);
window.location.href = "byond://?src=" + hSrc + "&action=select_position&x=" + x + "&y=" + y;
}
}).on("mousedown", function(e)
{
minimap_mousedown_scrollLeft = this.scrollLeft;
minimap_mousedown_scrollTop = this.scrollTop;
minimap_mousedown_clientX = e.clientX;
minimap_mousedown_clientY = e.clientY;
var c = ++minimap_mousedown_counter;
setTimeout(function()
{
if (c == minimap_mousedown_counter)
{
minimap_mousedown = true;
$("#minimap").css("cursor", "move");
}
}, 100);
});
$(document).on("mousemove", function(e)
{
if (minimap_mousedown)
{
var offsetX = minimap_mousedown_clientX - e.clientX;
var offsetY = minimap_mousedown_clientY - e.clientY;
var minimap = document.getElementById("minimap");
minimap.scrollLeft = minimap_mousedown_scrollLeft + offsetX;
minimap.scrollTop = minimap_mousedown_scrollTop + offsetY;
}
}).on("mouseup", function()
{
++minimap_mousedown_counter;
if (minimap_mousedown)
{
document.body.focus();
minimap_mousedown = false;
$("#minimap").css("cursor", "");
}
});
$(window).on("resize", onResize);
scaleMinimap(1.00);
});
function zoomIn()
{
scaleMinimap(Math.min(6.00, zoom_factor + 1.00));
}
function zoomOut()
{
scaleMinimap(Math.max(1.00, zoom_factor - 1.00));
}
function scaleMinimap(factor)
{
var $minimap = $("#minimap");
if (factor != zoom_factor)
{
zoom_factor = factor;
var old_map_width = $minimap.width();
var old_map_height = $minimap.height();
var old_canvas_size = $("#minimap > img").height(); // height is assumed to be the same
var new_canvas_size = minimap_height * factor; // ditto
var old_scrollLeft = $minimap[0].scrollLeft;
var old_scrollTop = $minimap[0].scrollTop;
var old_factor = old_canvas_size / minimap_height;
var diff_factor = factor - old_factor;
var old_centerX = ((old_map_width / 2) * diff_factor) + old_scrollLeft;
var old_centerY = ((old_map_height / 2) * diff_factor) + old_scrollTop;
$("#minimap > img").css("height", new_canvas_size + "px");
$minimap.css("max-width", new_canvas_size + "px");
var new_map_width = $minimap.width();
var new_map_height = $minimap.height();
var new_centerX = (new_map_width / 2) + old_centerX;
var new_centerY = (new_map_height / 2) + old_centerY;
var scrollLeft = new_centerX - (new_map_width / 2);
var scrollTop = new_centerY - (new_map_height / 2);
scale_x = new_canvas_size / (maxx * tile_size);
scale_y = new_canvas_size / (maxy * tile_size);
onResize();
$minimap[0].scrollLeft = scrollLeft;
$minimap[0].scrollTop = scrollTop;
$(".dot").each(function()
{
var $this = $(this);
var tx = translateX(parseInt($this.attr("data-x")));
var ty = translateY(parseInt($this.attr("data-y")));
// Workaround for IE bug where it doesn't modify the positions.
setTimeout(function(){ $this.css({ "top": ty + "px", "left": tx + "px" });}, 0);
});
}
}
function onResize()
{
if (zoom_factor == 1.00)
{
$(".zoom").css("left", "442px");
$("#minimap").css("max-height", Math.min($(window).height() - 16, 480) + "px");
}
else
{
$(".zoom").css("left", ($("#minimap").width() - 34) + "px");
$("#minimap").css("max-height", Math.min($(window).height() - 16, $("#minimap > img").height()) + "px");
}
if (expandHealth())
{
$(".colhealth").css("width", "150px");
$(".health").removeClass("tt");
}
else
{
$(".colhealth").css("width", "24px");
$(".health").addClass("tt");
}
$("body").css("padding-left", Math.min($(window).width() - 400, $("#minimap").width() - 10) + "px");
}
function expandHealth()
{
return $("#textbased").width() > 510;
}
var updateMap = true;
function switchTo(i)
{
if (i == 1)
{
$("#minimap").hide();
$("#textbased").show();
}
else
{
$("#textbased").hide();
$("#minimap").show();
}
}
var orig_scrollTop = 0;
function clearAll()
{
orig_scrollTop = $(window).scrollTop();
$("#textbased-tbody").empty();
$("#minimap .dot").remove();
}
function onAfterUpdate()
{
$(window).scrollTop(orig_scrollTop);
}
function isHead(ijob)
{
return (ijob % 10 == 0); // head roles always end in 0
}
function getColor(ijob)
{
if (ijob == 0) { return "#C06616"; } // captain
else if (ijob >= 10 && ijob < 20) { return "#E74C3C"; } // security
else if (ijob >= 20 && ijob < 30) { return "#3498DB"; } // medical
else if (ijob >= 30 && ijob < 40) { return "#9B59B6"; } // science
else if (ijob >= 40 && ijob < 50) { return "#F1C40F"; } // engineering
else if (ijob >= 50 && ijob < 60) { return "#F39C12"; } // cargo
else if (ijob >= 200 && ijob < 230) { return "#00C100"; } // CentCom
else { return "#C38312"; } // other / unknown
}
function add(name, assignment, ijob, life_status, dam1, dam2, dam3, dam4, area, pos_x, pos_y, in_range)
{
try { ijob = parseInt(ijob); }
catch (ex) { ijob = 0; }
var ls = "";
if (life_status === null) { ls = (life_status ? "<span class=\"bad\">Deceased</span>" : "<span class=\"good\">Living</span>"); }
var healthHTML = "";
if (dam1 != "" || dam2 != "" || dam3 != "" || dam4 != "")
{
var avg_dam = parseInt(dam1) + parseInt(dam2) + parseInt(dam3) + parseInt(dam4);
var i;
if (avg_dam <= 0) { i = 5; }
else if (avg_dam <= 25) { i = 4; }
else if (avg_dam <= 50) { i = 3; }
else if (avg_dam <= 75) { i = 2; }
else { i = 0; }
healthHTML = "<div class=\"health health-" + i + (expandHealth() ? "" : " tt") + "\"><div><span>(<font color=\"#3498db\">" + dam1 + "</font>/<font color=\"#2ecc71\">" + dam2 + "</font>/<font color=\"#e67e22\">" + dam3 + "</font>/<font color=\"#e74c3c\">" + dam4 + "</font>)</span></div></div>";
}
else
{
healthHTML = "<div class=\"health health-" + (life_status == "" ? -1 : (life_status == "true" ? 4 : 0)) + (expandHealth() ? "" : " tt") + "\"><div><span>Not Available</span></div></div>";
}
var trElem = $("<tr></tr>").attr("data-ijob", ijob);
var tdElem;
var spanElem;
tdElem = $("<td></td>");
var italics = false;
if (name.length >= 7 && name.substring(0, 3) == "<i>")
{
name = name.substring(3, name.length - 4);
italics = true;
}
spanElem = $("<span></span>").text(name);
if (italics)
{
spanElem.css("font-style", "italic");
}
if (isHead(ijob)) { spanElem.css("font-weight", "bold"); }
var color = getColor(ijob);
if (color) { spanElem.css("color", color); }
tdElem.append(spanElem);
if (assignment) { tdElem.append($("<span></span>").text(" (" + assignment + ")")); }
trElem.append(tdElem);
tdElem = $("<td style=\"vertical-align: top; cursor: default;\"></td>");
tdElem.html(healthHTML);
trElem.append(tdElem);
tdElem = $("<td style=\"cursor: default;\"></td>");
if (area && pos_x && pos_y)
{
tdElem.append($("<div></div>").text(area).addClass("tt").append($("<div></div>").append($("<span></span>").text("(" + pos_x + ", " + pos_y + ")"))));
tdElem.css("cursor", "pointer").on("click", function()
{
window.clipboardData.setData("Text", pos_x + ", " + pos_y);
});
}
else { tdElem.text("Not Available"); }
trElem.append(tdElem);
var item = $("#textbased-tbody > tr").filter(function(){ return parseInt($(this).attr("data-ijob")) >= ijob; }).eq(0);
if (item.length > 0) { trElem.insertBefore(item); }
else { $("#textbased-tbody").append(trElem); }
if (updateMap && pos_x && pos_y && (in_range == "1"))
{
var x = parseInt(pos_x);
var y = maxy - parseInt(pos_y);
var tx = translateX(x);
var ty = translateY(y);
var dotElem = $("<div class=\"dot\" style=\"top: " + ty + "px; left: " + tx + "px; background-color: " + color + "; z-index: " + ijob + ";\" data-x=\"" + x + "\" data-y=\"" + y + "\"></div>");
$("#minimap").append(dotElem);
var counter = 0;
function enable()
{
++counter;
dotElem.addClass("active").css({ "border-color": color });
}
function disable()
{
++counter;
dotElem.removeClass("active").css({ "border-color": "transparent" });
}
function click(e)
{
e.preventDefault();
e.stopPropagation();
window.location.href = "byond://?src=" + hSrc + "&action=select_person&name=" + encodeURIComponent(name);
}
trElem.on("mouseover", function()
{
enable();
if (zoom_factor > 1.00)
{
var c = counter;
setTimeout(function()
{
if (c == counter)
{
var minimap = document.getElementById("minimap");
var half = $(minimap).height() / 2;
var offset = $(dotElem).offset();
minimap.scrollLeft = offset.left + minimap.scrollLeft - half;
minimap.scrollTop = offset.top + minimap.scrollTop - half;
}
}, 100);
}
}).on("mouseout", disable).on("click", click);
dotElem.on("mouseover", function()
{
trElem.addClass("hover");
enable();
trElem.scrollintoview();
}).on("mouseout", function()
{
trElem.removeClass("hover");
disable();
}).on("click", click);
}
}
function translateX(n) { return (translate(n - 1.5, scale_x) ).toFixed(0); }
function translateY(n) { return (translate(n + 0.75, scale_y) ).toFixed(0); }
function translate(n, scale)
{
return (n * tile_size) * scale;
}
@@ -7,4 +7,4 @@
slot_flags = SLOT_BELT
/obj/item/device/sensor_device/attack_self(mob/user)
GLOB.crewmonitor.show(user) //Proc already exists, just had to call it
GLOB.crewmonitor.show(user,src) //Proc already exists, just had to call it