diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index fa6db81352b..a8653369af1 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -66,6 +66,7 @@ var/datum/subsystem/ticker/ticker timeLeft = config.lobby_countdown * 10 world << "Welcome to the pre-game lobby!" world << "Please, setup your character and select ready. Game will start in [config.lobby_countdown] seconds" + crewmonitor.generateMiniMaps() // start generating minimaps (this is a background process) current_state = GAME_STATE_PREGAME if(GAME_STATE_PREGAME) @@ -200,6 +201,10 @@ var/datum/subsystem/ticker/ticker //Plus it provides an easy way to make cinematics for other events. Just use this as a template /datum/subsystem/ticker/proc/station_explosion_cinematic(var/station_missed=0, var/override = null) if( cinematic ) return //already a cinematic in progress! + + for (var/datum/html_interface/hi in html_interfaces) + hi.closeAll() + auto_toggle_ooc(1) // Turn it on //initialise our cinematic screen object cinematic = new /obj/screen{icon='icons/effects/station_explosion.dmi';icon_state="station_intact";layer=20;mouse_opacity=0;screen_loc="1,0";}(src) diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 688ae0c3b3f..edd4d2ad81b 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -93,6 +93,9 @@ var/datum/atom_hud/data/medical/basic/B = huds[DATA_HUD_MEDICAL_BASIC] B.update_suit_sensors(src) + var/turf/T = get_turf(src) + if (T) crewmonitor.queueUpdate(T.z) + //called when a carbon changes health /mob/living/carbon/proc/med_hud_set_health() var/image/holder = hud_list[HEALTH_HUD] @@ -101,6 +104,9 @@ else holder.icon_state = "hud[RoundHealth(health)]" + var/turf/T = get_turf(src) + if (T) crewmonitor.queueUpdate(T.z) + //called when a carbon changes stat, virus or XENO_HOST /mob/living/carbon/proc/med_hud_set_status() var/image/holder = hud_list[STATUS_HUD] @@ -126,6 +132,9 @@ if(wear_id) holder.icon_state = "hud[ckey(wear_id.GetJobName())]" + var/turf/T = get_turf(src) + if (T) 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)) diff --git a/code/game/machinery/computer/crew.css b/code/game/machinery/computer/crew.css new file mode 100644 index 00000000000..175735be66e --- /dev/null +++ b/code/game/machinery/computer/crew.css @@ -0,0 +1,100 @@ +body +{ + padding-left: 490px; + cursor: default; +} + +#ntbgcenter +{ + background-position: 550px 0px !important; +} + +#minimap +{ + position: fixed; + top: 8px; + left: 8px; + border: 2px inset #888; +} + +#textbased +{ + width: 100%; +} + +#textbased table +{ + min-width: 380px; + width: 100%; + table-layout: fixed; +} + +#textbased td +{ + vertical-align: top; + padding: 2px; +} + +#textbased tbody tr:hover td, #textbased tbody tr.hover td +{ + background-color: #434343; +} + +#textarea:after +{ + content: ""; + clear: both; +} + +a { display: none; } + +.health +{ + width: 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; } + +.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; +} \ No newline at end of file diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index e97ad9e0182..9b82de94c35 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -20,23 +20,15 @@ return crewmonitor.show(user) -/obj/machinery/computer/crew/Topic(href, href_list) - if(..()) return - if (src.z > 6) - usr << "Unable to establish a connection: \black You're too far away from the station!" - return - if( href_list["close"] ) - usr << browse(null, "window=crewcomp") - usr.unset_machine() - return - if(href_list["update"]) - src.updateDialog() - return - var/global/datum/crewmonitor/crewmonitor = new -/datum/crewmonitor/var/list/jobs -/datum/crewmonitor/var/list/interfaces +/datum/crewmonitor + var/list/jobs + var/list/interfaces + var/list/data + var/const/MAX_ICON_DIMENSION = 1024 + var/const/ICON_SIZE = 4 + var/initialized = FALSE /datum/crewmonitor/New() . = ..() @@ -84,6 +76,7 @@ var/global/datum/crewmonitor/crewmonitor = new src.jobs = jobs src.interfaces = list() + src.data = list() /datum/crewmonitor/Destroy() if (src.interfaces) @@ -97,28 +90,53 @@ var/global/datum/crewmonitor/crewmonitor = new if (!z) z = mob.z if (z > 0 && src.interfaces) - if (!src.interfaces["[mob.z]"]) - src.interfaces["[mob.z]"] = new/datum/html_interface/nanotrasen(src, "Crew Monitoring", 900, 600) + var/datum/html_interface/hi - src.update() + if (!src.interfaces["[z]"]) + src.interfaces["[z]"] = new/datum/html_interface/nanotrasen(src, "Crew Monitoring", 900, 540, "") - var/datum/html_interface/hi = src.interfaces["[z]"] + hi = src.interfaces["[z]"] + + hi.updateContent("content", "Switch to mini map Switch to text-based
") + + src.update(z, TRUE) + else + hi = src.interfaces["[z]"] + + hi = src.interfaces["[z]"] hi.show(mob) + src.updateFor(mob, hi, z) -/datum/crewmonitor/proc/update(z) +/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. + var/list/parameters = list(ismob(hclient_or_mob) && isAI(hclient_or_mob) ? "true" : "false") + + hi.callJavaScript("clearAll", parameters, hclient_or_mob) + + for (var/list/L in data) + hi.callJavaScript("add", L, hclient_or_mob) + +/datum/crewmonitor/proc/update(z, ignore_unused = FALSE) if (src.interfaces["[z]"]) var/datum/html_interface/hi = src.interfaces["[z]"] - if (hi.isUsed()) - var/t = "" - var/list/logs = list() + if (ignore_unused || hi.isUsed()) + var/list/results = list() var/obj/item/clothing/under/U var/obj/item/weapon/card/id/I - var/log var/turf/pos - var/style var/ijob - var/damage_report + var/name + var/assignment + var/dam1 + var/dam2 + var/dam3 + var/dam4 + var/area + var/pos_x + var/pos_y + var/life_status for(var/mob/living/carbon/human/H in mob_list) // Check if their z-level is correct and if they are wearing a uniform. @@ -133,61 +151,47 @@ var/global/datum/crewmonitor/crewmonitor = new // 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 - log = "" I = H.wear_id ? H.wear_id.GetID() : null - var/life_status = (H.stat > 1 ? "Deceased" : "Living") - if (I) - style = null + name = I.registered_name + assignment = I.assignment ijob = jobs[I.assignment] - - if (ijob % 10 == 0) style += "font-weight: bold; " // head roles always end in 0 - if (ijob >= 10 && ijob < 20) style += "color: #E74C3C; " // security - if (ijob >= 20 && ijob < 30) style += "color: #3498DB; " // medical - if (ijob >= 30 && ijob < 40) style += "color: #9B59B6; " // science - if (ijob >= 40 && ijob < 50) style += "color: #F1C40F; " // engineering - if (ijob >= 50 && ijob < 60) style += "color: #F39C12; " // cargo - if (ijob >= 200 && ijob < 230) style += "color: #00C100; " // Centcom - - // ijob does not get displayed, nor does it take up space, it's just used for the positioning of an entry - log += "[ijob]" else - log += "80" + name = "Unknown" + assignment = "" + ijob = 80 - damage_report = "" + if (U.sensor_mode >= 1) life_status = (!H.stat ? "true" : "false") + else life_status = null - if (U.sensor_mode > 1) - var/dam1 = round(H.getOxyLoss(),1) - var/dam2 = round(H.getToxLoss(),1) - var/dam3 = round(H.getFireLoss(),1) - var/dam4 = round(H.getBruteLoss(),1) - damage_report = "([dam1]/[dam2]/[dam3]/[dam4])" + if (U.sensor_mode >= 2) + 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 - switch (U.sensor_mode) - if (1) - log += "" + if (U.sensor_mode >= 3) + if (!pos) pos = get_turf(H) + var/area/player_area = get_area(H) - if (2) - log += "" + area = format_text(player_area.name) + pos_x = pos.x + pos_y = pos.y + else + area = null + pos_x = null + pos_y = null - if (3) - if (!pos) pos = get_turf(H) + results[++results.len] = list(name, assignment, ijob, life_status, dam1, dam2, dam3, dam4, area, pos_x, pos_y, H.can_track(null)) - var/area/player_area = get_area(H) - log += "" - - logs.Add(log) - - // TODO: Client-side sorting? - logs = sortList(logs) - - for(log in logs) - t += log - - t += "

Name

Vitals

Position

[I.registered_name] ([I.assignment])
Unknown[life_status]Not Available
[life_status] [damage_report]Not Available
[life_status] [damage_report][format_text(player_area.name)] ([pos.x], [pos.y])
" - - hi.updateContent("content", t) + 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 = "" @@ -197,6 +201,32 @@ var/global/datum/crewmonitor/crewmonitor = new return (hclient.client.mob && hclient.client.mob.stat == 0 && hclient.client.mob.z == text2num(z) && ( isAI(hclient.client.mob) || (locate(/obj/machinery/computer/crew, range(1, hclient.client.mob))) || (locate(/obj/item/device/sensor_device, hclient.client.mob.contents)) ) ) +/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 range(10, tile) + if (!C) C = locate(/obj/machinery/camera) in range(15, tile) + + if (C) + var/turf/current_loc = AI.eyeobj.loc + + spawn(min(30, get_dist(get_turf(C), AI.eyeobj) / 4)) + if (AI && AI.eyeobj && current_loc == AI.eyeobj.loc) + AI.switchCamera(C) + /* /proc/crewscan() var/list/tracked = list() @@ -214,7 +244,148 @@ var/global/datum/crewmonitor/crewmonitor = new . = ..() - if (old_z != src.z) procqueue.queue(crewmonitor, "update", old_z) - procqueue.queue(crewmonitor, "update", src.z) + if (old_z != src.z) crewmonitor.queueUpdate(old_z) + crewmonitor.queueUpdate(src.z) else - return ..() \ No newline at end of file + return ..() + +/datum/crewmonitor/proc/queueUpdate(z) + procqueue.queue(crewmonitor, "update", z) + +/datum/crewmonitor/proc/generateMiniMaps() + spawn + for (var/z = 1 to world.maxz) src.generateMiniMap(z) + + NOTICE("MINIMAP: All minimaps have been generated.") + + for (var/client/C in clients) + src.sendResources(C) + + src.initialized = TRUE + +/datum/crewmonitor/proc/sendResources(client/C) + C << browse_rsc('crew.js', "crewmonitor.js") + C << browse_rsc('crew.css', "crewmonitor.css") + for (var/z = 1 to world.maxz) C << browse_rsc(file("[getMinimapFile(z)].png"), "minimap_[z].png") + +/datum/crewmonitor/proc/getMinimapFile(z) + return "data/minimaps/map_[z]" + +// Activate this to debug tile mismatches in the minimap. +// This will store the full information on each tile and compare it the next time you run the minimap. +// It can be used to find out what's changed since the last iteration. +// Only activate this when you need it - this should never be active on a live server! +// #define MINIMAP_DEBUG + +/datum/crewmonitor/proc/generateMiniMap(z, x1 = 1, y1 = 1, x2 = world.maxx, y2 = world.maxy) + var/result_path = "[src.getMinimapFile(z)].png" + var/hash_path = "[src.getMinimapFile(z)].md5" + var/list/tiles = block(locate(x1, y1, z), locate(x2, y2, z)) + var/hash = "" + var/temp + + #ifdef MINIMAP_DEBUG + var/tiledata_path = "data/minimaps/debug_tiledata_[z].sav" + var/savefile/F = new/savefile(tiledata_path) + #endif + + // Note for future developer: If you have tiles on the map with random or dynamic icons this hash check will fail + // every time. You'll have to modify this code to generate a unique hash for your object. + // Don't forget to modify the minimap generation code to use a default icon (or skip generation altogether). + for (var/turf/tile in tiles) + if (istype(tile.loc, /area/asteroid) || istype(tile.loc, /area/mine/unexplored) || istype(tile, /turf/simulated/mineral) || (istype(tile.loc, /area/space) && istype(tile, /turf/simulated/floor/plating/asteroid))) + temp = "/area/asteroid" + else if (istype(tile.loc, /area/mine) && istype(tile, /turf/simulated/floor/plating/asteroid)) + temp = "/area/mine/explored" + else if (tile.type == /area/start && tile.type == /turf/space || istype(tile, /turf/space/transit)) + temp = "/turf/space" + else if (tile.type == /turf/simulated/floor/plating/abductor) + temp = "/turf/simulated/floor/plating/abductor" + else + temp = "[tile.icon][tile.icon_state][tile.dir]" + + #ifdef MINIMAP_DEBUG + if (F["/[tile.y]/[tile.x]"] && F["/[tile.y]/[tile.x]"] != temp) + CRASH("Mismatch: [tile.type] at [tile.x],[tile.y],[tile.z] ([tile.icon], [tile.icon_state], [tile.dir])") + else + F["/[tile.y]/[tile.x]"] << temp + #endif + + hash = md5("[hash][temp]") + + if (fexists(result_path)) + if (!fexists(hash_path) || trim(file2text(hash_path)) != hash) + fdel(result_path) + fdel(hash_path) + + if (!fexists(result_path)) + ASSERT(x1 > 0) + ASSERT(y1 > 0) + ASSERT(x2 <= world.maxx) + ASSERT(y2 <= world.maxy) + + var/icon/map_icon = new/icon('html/mapbase1024.png') + + // map_icon is fine and contains only 1 direction at this point. + + ASSERT(map_icon.Width() == MAX_ICON_DIMENSION && map_icon.Height() == MAX_ICON_DIMENSION) + + NOTICE("MINIMAP: Generating minimap for z-level [z].") + + var/i = 0 + var/icon/turf_icon + var/old_icon + var/old_icon_state + var/old_dir + var/new_icon + var/new_icon_state + var/new_dir + + for (var/turf/tile in tiles) + if (tile.type != /area/start && tile.type != /turf/space && !istype(tile, /turf/space/transit)) + if (istype(tile.loc, /area/asteroid) || istype(tile.loc, /area/mine/unexplored) || istype(tile, /turf/simulated/mineral) || (istype(tile.loc, /area/space) && istype(tile, /turf/simulated/floor/plating/asteroid))) + new_icon = 'icons/turf/mining.dmi' + new_icon_state = "rock" + new_dir = 2 + else if (istype(tile.loc, /area/mine) && istype(tile, /turf/simulated/floor/plating/asteroid)) + new_icon = 'icons/turf/floors.dmi' + new_icon_state = "asteroid" + new_dir = 2 + else if (tile.type == /turf/simulated/floor/plating/abductor) + new_icon = 'icons/turf/floors.dmi' + new_icon_state = "alienpod1" + new_dir = 2 + else + new_icon = tile.icon + new_icon_state = tile.icon_state + new_dir = tile.dir + + if (new_icon != old_icon || new_icon_state != old_icon_state || new_dir != old_dir) + old_icon = new_icon + old_icon_state = new_icon_state + old_dir = new_dir + + turf_icon = new/icon(new_icon, new_icon_state, new_dir, 1, 0) + turf_icon.Scale(ICON_SIZE, ICON_SIZE) + + map_icon.Blend(turf_icon, ICON_OVERLAY, ((tile.x - 1) * ICON_SIZE), ((tile.y - 1) * ICON_SIZE)) + + if ((++i) % 512 == 0) sleep(1) // deliberate delay to avoid lag spikes + + if ((i % 1024) == 0) NOTICE("MINIMAP: Generated [i] of [tiles.len] tiles.") + else + sleep(-1) // avoid sleeping if possible: prioritize pending procs + + NOTICE("MINIMAP: Generated [tiles.len] of [tiles.len] tiles.") + + // BYOND BUG: map_icon now contains 4 directions? Create a new icon with only a single state. + var/icon/result_icon = new/icon() + + result_icon.Insert(map_icon, "", SOUTH, 1, 0) + + fcopy(result_icon, result_path) + text2file(hash, hash_path) + +#ifdef MINIMAP_DEBUG +#undef MINIMAP_DEBUG +#endif diff --git a/code/game/machinery/computer/crew.js b/code/game/machinery/computer/crew.js new file mode 100644 index 00000000000..bfc75c9583d --- /dev/null +++ b/code/game/machinery/computer/crew.js @@ -0,0 +1,196 @@ +var isAI = null; +var scale_x; +var scale_y; + +function disableSelection(){ return false; }; + +$(window).on("onUpdateContent", function() +{ + $("#textbased").html("

Name

 

Position

"); + + $("#minimap").append(""); + + $("body")[0].onselectstart = disableSelection; + + var width = $("#minimap").width(); + + scale_x = width / (maxx * tile_size); + scale_y = width / (maxy * tile_size); // height is assumed to be the same + + $("#minimap").on("click", function(e) + { + var x = ((((e.clientX - 8) / scale_x) / tile_size) + 1).toFixed(0); + var y = ((maxy - (((e.clientY - 8) / scale_y) / tile_size)) + 1).toFixed(0); + + window.location.href = "byond://?src=" + hSrc + "&action=select_position&x=" + x + "&y=" + y; + }); +}); + +var updateMap = true; + +function switchTo(i) +{ + if (i == 1) + { + $("#minimap").hide(); + $("#textbased").show(); + } + else + { + $("#textbased").hide(); + $("#minimap").show(); + } +} + +function clearAll(ai) +{ + if (isAI === null) { isAI = (ai == "true"); } + $("#textbased-tbody").empty(); + $("#minimap .dot").remove(); +} + +function isHead(ijob) +{ + return (ijob % 10 == 0); // head roles always end in 0 +} + +function getColor(ijob) +{ + 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 +} + +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 ? "Deceased" : "Living"); } + + var healthHTML = ""; + + if (dam1 != "" || dam2 != "" || dam3 != "" || dam4 != "") + { + var avg_dam = parseInt(dam1) + parseInt(dam2) + parseInt(dam3) + parseInt(dam4); + var i; + + if (isAI) { i = -1; } + else + { + 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 = "
(" + dam1 + "/" + dam2 + "/" + dam3 + "/" + dam4 + ")
"; + } + else + { + healthHTML = "
Not Available
"; + } + + var trElem = $("").attr("data-ijob", ijob); + var tdElem; + var spanElem; + + tdElem = $(""); + + var italics = false; + + if (name.length >= 7 && name.substring(0, 3) == "") + { + name = name.substring(3, name.length - 4); + italics = true; + } + + spanElem = $("").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($("").text(" (" + assignment + ")")); } + + trElem.append(tdElem); + + tdElem = $(""); + tdElem.html(healthHTML); + + trElem.append(tdElem); + + tdElem = $(""); + + if (area && pos_x && pos_y) { tdElem.append($("
").text(area).addClass("tt").append($("
").append($("").text("(" + pos_x + ", " + pos_y + ")")))); } + else { tdElem.text("Not Available"); } + + trElem.append(tdElem); + + $("#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 = (translate(x - 1, scale_x) - 1.5).toFixed(0); + var ty = (translate(y - 1, scale_y) + 3).toFixed(0); + + if (!color) { color = "#FFFFFF"; } + + var dotElem = $("
"); + + $("#minimap").append(dotElem); + + function enable() + { + dotElem.css({ "border-color": "#FFFFFF", "z-index": 9999, "width": "6px", "height": "6px", "margin-top": "-1px", "margin-left": "-2px" }); + } + + function disable() + { + dotElem.css({ "border-color": "transparent", "z-index": ijob, "width": "3px", "height": "3px", "margin-top": "0px", "margin-left": "0px", "filter": "" }); + } + + function click(e) + { + e.preventDefault(); + e.stopPropagation(); + + window.location.href = "byond://?src=" + hSrc + "&action=select_person&name=" + encodeURIComponent(name); + } + + trElem.on("mouseover", enable).on("mouseout", disable).on("click", click); + + dotElem.on("mouseover", function() + { + trElem.addClass("hover"); + enable(); + }).on("mouseout", function() + { + trElem.removeClass("hover"); + disable(); + }).on("click", click); + } +} + +function translate(n, scale) +{ + return (n * tile_size) * scale; +} \ No newline at end of file diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index f1024432765..366f113a114 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -280,6 +280,11 @@ var/next_external_rsc = 0 hi = new type(null) hi.sendResources(src) + // Preload the crew monitor. This needs to be done due to BYOND bug http://www.byond.com/forum/?post=1487244 + spawn + if (crewmonitor && crewmonitor.initialized) + crewmonitor.sendResources(src) + //Send nanoui files to client SSnano.send_resources(src) getFiles( diff --git a/code/modules/html_interface/html_interface.dm b/code/modules/html_interface/html_interface.dm index b9787123f21..2e3359e80c4 100644 --- a/code/modules/html_interface/html_interface.dm +++ b/code/modules/html_interface/html_interface.dm @@ -54,6 +54,10 @@ Hides the HTML interface from the provided client. This will close the browser w Returns TRUE if the interface is being used (has an active client) or FALSE if not. + hi.closeAll() + +Closes the interface on all clients. + ** Additional notes ** When working with byond:// links make sure to reference the HTML interface object and NOT the original object. Topic() will still be called on @@ -74,6 +78,8 @@ mob/verb/test() */ +/var/list/html_interfaces = new/list() + /datum/html_interface // The atom we should report to. var/atom/ref @@ -100,6 +106,8 @@ mob/verb/test() var/height /datum/html_interface/New(atom/ref, title, width = 700, height = 480, head = "") + html_interfaces.Add(src) + . = ..() src.ref = ref @@ -109,9 +117,9 @@ mob/verb/test() src.head = head /datum/html_interface/Destroy() - if (src.clients) - for (var/client in src.clients) - src.hide(src.clients[client]) + src.closeAll() + + html_interfaces.Remove(src) return ..() @@ -160,7 +168,19 @@ mob/verb/test() if (istype(hclient)) if (hclient.is_loaded) hclient.client << output(list2params(list(jscript)), "browser_\ref[src].browser:eval") else - for (var/client in src.clients) src.executeJavaScript(jscript, src.clients[client]) + for (var/client in src.clients) if(src.clients[client]) src.executeJavaScript(jscript, src.clients[client]) + +/datum/html_interface/proc/callJavaScript(func, list/arguments, datum/html_interface_client/hclient = null) + if (!arguments) arguments = new/list() + + if (hclient) + hclient = getClient(hclient) + + if (istype(hclient)) + if (hclient.is_loaded) + hclient.client << output(list2params(arguments), "browser_\ref[src].browser:[func]") + else + for (var/client in src.clients) if (src.clients[client]) src.callJavaScript(func, arguments, src.clients[client]) /datum/html_interface/proc/updateLayout(layout) src.layout = layout @@ -201,6 +221,8 @@ mob/verb/test() hclient.client << output(replacetextEx(replacetextEx(file2text('html_interface.html'), "\[hsrc\]", "\ref[src]"), "", "[head]"), "browser_\ref[src].browser") winshow(hclient.client, "browser_\ref[src]", TRUE) + while (hclient.client && hclient.active && !hclient.is_loaded) sleep(2) + /datum/html_interface/proc/hide(datum/html_interface_client/hclient) hclient = getClient(hclient) @@ -244,12 +266,22 @@ mob/verb/test() /datum/html_interface/proc/isUsed() if (src.clients && src.clients.len > 0) var/datum/html_interface_client/hclient + for (var/key in clients) - hclient = clients[key] - if (hclient.active) return TRUE + hclient = _getClient(clients[key]) + + if (hclient) + if (hclient.active) return TRUE + else + clients.Remove(key) return FALSE +/datum/html_interface/proc/closeAll() + if (src.clients) + for (var/client in src.clients) + src.hide(src.clients[client]) + /* * Danger Zone */ /datum/html_interface/proc/_getClient(datum/html_interface_client/hclient) @@ -269,8 +301,8 @@ mob/verb/test() else return null -/datum/html_interface/proc/_renderTitle(datum/html_interface_client/hclient, ignore_cache = FALSE) - if (hclient && hclient.is_loaded) +/datum/html_interface/proc/_renderTitle(datum/html_interface_client/hclient, ignore_cache = FALSE, ignore_loaded = FALSE) + if (hclient && (ignore_loaded || hclient.is_loaded)) // Only render if we have new content. if (ignore_cache || src.title != hclient.title) @@ -280,8 +312,8 @@ mob/verb/test() hclient.client << output(list2params(list(title)), "browser_\ref[src].browser:setTitle") -/datum/html_interface/proc/_renderLayout(datum/html_interface_client/hclient) - if (hclient && hclient.is_loaded) +/datum/html_interface/proc/_renderLayout(datum/html_interface_client/hclient, ignore_loaded = FALSE) + if (hclient && (ignore_loaded || hclient.is_loaded)) var/html = src.layout // Only render if we have new content. @@ -290,10 +322,10 @@ mob/verb/test() hclient.client << output(list2params(list(html)), "browser_\ref[src].browser:updateLayout") - for (var/id in src.content_elements) src._renderContent(id, hclient) + for (var/id in src.content_elements) src._renderContent(id, hclient, ignore_loaded = ignore_loaded) -/datum/html_interface/proc/_renderContent(id, datum/html_interface_client/hclient, ignore_cache = FALSE) - if (hclient && hclient.is_loaded) +/datum/html_interface/proc/_renderContent(id, datum/html_interface_client/hclient, ignore_cache = FALSE, ignore_loaded = FALSE) + if (hclient && (ignore_loaded || hclient.is_loaded)) var/html = src.content_elements[id] // Only render if we have new content. @@ -312,10 +344,11 @@ mob/verb/test() if ("onload") hclient.layout = null hclient.content_elements.len = 0 - hclient.is_loaded = TRUE - src._renderTitle(hclient, TRUE) - src._renderLayout(hclient) + src._renderTitle(hclient, TRUE, TRUE) + src._renderLayout(hclient, TRUE) + + hclient.is_loaded = TRUE if ("onclose") src.hide(hclient) diff --git a/code/modules/html_interface/nanotrasen/nanotrasen.css b/code/modules/html_interface/nanotrasen/nanotrasen.css index 2b5a2cc418c..702a381c077 100644 --- a/code/modules/html_interface/nanotrasen/nanotrasen.css +++ b/code/modules/html_interface/nanotrasen/nanotrasen.css @@ -11,6 +11,12 @@ body line-height: 170%; /* NullQuery: 170% of what? */ } +/* Fix for IE8 */ +body +{ + background-color: #1F1F1F\9 !important; +} + #ntbgcenter { position: absolute; diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index bc689b62098..01d5e9bf93e 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -849,7 +849,7 @@ Sorry Giacom. Please don't be mad :( return 0 if(T.z >= ZLEVEL_SPACEMAX) return 0 - if(src == user) + if(user != null && src == user) return 0 if(invisibility || alpha == 0)//cloaked return 0 diff --git a/html/mapbase1024.png b/html/mapbase1024.png new file mode 100644 index 00000000000..a927a71b6e7 Binary files /dev/null and b/html/mapbase1024.png differ