diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index d7595e1a29de3..22ffe69db8892 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -99,7 +99,16 @@ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) if(!ui) - ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 600) + ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800) + + // adding a template with the key "mapContent" enables the map ui functionality + ui.add_template("mapContent", "crew_monitor_map_content.tmpl") + // adding a template with the key "mapHeader" replaces the map header content + ui.add_template("mapHeader", "crew_monitor_map_header.tmpl") + + // we want to show the map by default + ui.set_show_map(1) + ui.set_initial_data(data) ui.open() diff --git a/code/modules/nano/nanomapgen.dm b/code/modules/nano/nanomapgen.dm new file mode 100644 index 0000000000000..9b674966d0aec --- /dev/null +++ b/code/modules/nano/nanomapgen.dm @@ -0,0 +1,80 @@ +// This file is a modified version of https://raw2.github.com/Baystation12/OldCode-BS12/master/code/TakePicture.dm + +#define NANOMAP_ICON_SIZE 4 +#define NANOMAP_MAX_ICON_DIMENSION 1024 + +#define NANOMAP_TILES_PER_IMAGE (NANOMAP_MAX_ICON_DIMENSION / NANOMAP_ICON_SIZE) + +#define NANOMAP_TERMINALERR 5 +#define NANOMAP_INPROGRESS 2 +#define NANOMAP_BADOUTPUT 2 +#define NANOMAP_SUCCESS 1 +#define NANOMAP_WATCHDOGSUCCESS 4 +#define NANOMAP_WATCHDOGTERMINATE 3 + + +//Call these procs to dump your world to a series of image files (!!) +//NOTE: Does not explicitly support non 32x32 icons or stuff with large pixel_* values, so don't blame me if it doesn't work perfectly + +/mob/verb/nanomapgen_DumpImage() + set category = "Admin" + set name = "Generate NanoUI Map" + + if(!src.client.holder) + src << "Only administrators may use this command." + return + nanomapgen_DumpTile() + +/mob/proc/nanomapgen_DumpTile(var/startX = 1, var/startY = 1, var/currentZ = 1, var/endX = -1, var/endY = -1) + + if (endX < 0 || endX > world.maxx) + endX = world.maxx + + if (endY < 0 || endY > world.maxy) + endY = world.maxy + + if (startX > endX) + world.log << "NanoMapGen: ERROR: startX ([startX]) cannot be greater than endX ([endX])" + sleep(3) + return NANOMAP_TERMINALERR + + if (startY > endX) + world.log << "NanoMapGen: ERROR: startY ([startY]) cannot be greater than endY ([endY])" + sleep(3) + return NANOMAP_TERMINALERR + + var/icon/Tile = icon(file("nano/mapbase1024.png")) + if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION) + world.log << "NanoMapGen: ERROR: BASE IMAGE DIMENSIONS ARE NOT [NANOMAP_MAX_ICON_DIMENSION]x[NANOMAP_MAX_ICON_DIMENSION]" + sleep(3) + return NANOMAP_TERMINALERR + + world.log << "NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])" + + var/count = 0; + for(var/WorldX = startX, WorldX <= endX, WorldX++) + for(var/WorldY = startY, WorldY <= endY, WorldY++) + + var/atom/Turf = locate(WorldX, WorldY, currentZ) + + var/icon/TurfIcon = new(Turf.icon, Turf.icon_state, Turf.dir, 1, 0) + TurfIcon.Scale(NANOMAP_ICON_SIZE, NANOMAP_ICON_SIZE) + + Tile.Blend(TurfIcon, ICON_OVERLAY, ((WorldX - 1) * NANOMAP_ICON_SIZE), ((WorldY - 1) * NANOMAP_ICON_SIZE)) + + count++ + + if (count % 1024 == 0) + world.log << "NanoMapGen: [count] tiles done" + sleep(5) + + world.log << "NanoMapGen: sending nanoMap.png to client" + + usr << browse(Tile, "window=picture;file=nanoMap.png;display=0") + + world.log << "NanoMapGen: Done." + + if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION) + return NANOMAP_BADOUTPUT + + return NANOMAP_SUCCESS \ No newline at end of file diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index 3620053db4d37..ec1c5f9ac4e1a 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -40,8 +40,14 @@ nanoui is used to open and update nano browser uis var/templates[0] // the layout key for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing) var/layout_key = "default" + // this sets whether to re-render the ui layout with each update (default 0, turning on will break the map ui if it's in use) + var/auto_update_layout = 0 + // this sets whether to re-render the ui content with each update (default 1) + var/auto_update_content = 1 // the default state to use for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing) var/state_key = "default" + // show the map ui, this is used by the default layout + var/show_map = 0 // initial data, containing the full data structure, must be sent to the ui (the data structure cannot be extended later on) var/list/initial_data[0] // set to 1 to update the ui automatically every master_controller tick @@ -166,8 +172,8 @@ nanoui is used to open and update nano browser uis * * @return nothing */ -/datum/nanoui/proc/set_auto_update(state = 1) - is_auto_updating = state +/datum/nanoui/proc/set_auto_update(nstate = 1) + is_auto_updating = nstate /** * Set the initial data for the ui. This is vital as the data structure set here cannot be changed when pushing new updates. @@ -190,6 +196,9 @@ nanoui is used to open and update nano browser uis "srcObject" = list("name" = src_object.name), "stateKey" = state_key, "status" = status, + "autoUpdateLayout" = auto_update_layout, + "autoUpdateContent" = auto_update_content, + "showMap" = show_map, "user" = list("name" = user.name) ) return config_data @@ -269,6 +278,26 @@ nanoui is used to open and update nano browser uis */ /datum/nanoui/proc/set_layout_key(nlayout_key) layout_key = lowertext(nlayout_key) + + /** + * Set the ui to update the layout (re-render it) on each update, turning this on will break the map ui (if it's being used) + * + * @param state int (bool) Set update to 1 or 0 (true/false) (default 0) + * + * @return nothing + */ +/datum/nanoui/proc/set_auto_update_layout(nstate) + auto_update_layout = nstate + + /** + * Set the ui to update the main content (re-render it) on each update + * + * @param state int (bool) Set update to 1 or 0 (true/false) (default 1) + * + * @return nothing + */ +/datum/nanoui/proc/set_auto_update_content(nstate) + auto_update_content = nstate /** * Set the state key for use in the frontend Javascript @@ -278,7 +307,17 @@ nanoui is used to open and update nano browser uis * @return nothing */ /datum/nanoui/proc/set_state_key(nstate_key) - state_key = nstate_key + state_key = nstate_key + + /** + * Toggle showing the map ui + * + * @param nstate_key boolean 1 to show map, 0 to hide (default is 0) + * + * @return nothing + */ +/datum/nanoui/proc/set_show_map(nstate) + show_map = nstate /** * Set whether or not to use the "old" on close logic (mainly unset_machine()) @@ -416,8 +455,14 @@ nanoui is used to open and update nano browser uis update_status(0) // update the status if (status != STATUS_INTERACTIVE || user != usr) // If UI is not interactive or usr calling Topic is not the UI user return + + // This is used to toggle the nano map ui + var/show_map_updated = 0 + if(href_list["showMap"]) + set_show_map(text2num(href_list["showMap"])) + show_map_updated = 1 - if (src_object && src_object.Topic(href, href_list)) + if ((src_object && src_object.Topic(href, href_list)) || show_map_updated) nanomanager.update_uis(src_object) // update all UIs attached to src_object /** diff --git a/nano/css/layout_default.css b/nano/css/layout_default.css index df5ce06e77cbf..50902230eb3a3 100644 --- a/nano/css/layout_default.css +++ b/nano/css/layout_default.css @@ -12,7 +12,7 @@ body { height: 30px; } -#uiTitle { +#uiTitleText { position: absolute; top: 6px; left: 44px; @@ -45,6 +45,57 @@ body { height: 24px; } +#uiMapWrapper { + clear: both; + padding: 8px; +} + +#uiMapHeader { + position: relative; + clear: both; + padding: 8px; +} + +#uiMapContainer { + position: relative; + width: 100%; + height: 600px; + overflow: hidden; + border: 1px solid #40628a; + background: url(nanomapBackground.png); +} + +#uiMap { + position: absolute; + top: 50%; + left: 50%; + margin: -1024px 0 0 -1024px; + width: 2048px; + height: 2048px; +} + +#uiMapImage { + position: absolute; + top: 0px; + left: 0px; + width: 2048px; + height: 2048px; +} + +#uiMapContent { + position: absolute; + top: 0px; + left: 0px; + width: 2048px; + height: 2048px; +} + +#uiMapFooter { + position: relative; + clear: both; + padding: 8px; +} + #uiContent { clear: both; padding: 8px; diff --git a/nano/css/shared.css b/nano/css/shared.css index ec9ff3f93d03e..2cbeda5c6dcae 100644 --- a/nano/css/shared.css +++ b/nano/css/shared.css @@ -73,6 +73,10 @@ a.white:hover { background: #40628a; } +.hidden { + display: none; +} + .linkOn, a.linkOn:link, a.linkOn:visited, a.linkOn:active, a.linkOn:hover, .selected, a.selected:link, a.selected:visited, a.selected:active, a.selected:hover { color: #ffffff; background: #2f943c; diff --git a/nano/images/nanomap.png b/nano/images/nanomap.png new file mode 100644 index 0000000000000..d6a635769f653 Binary files /dev/null and b/nano/images/nanomap.png differ diff --git a/nano/images/nanomapBackground.png b/nano/images/nanomapBackground.png new file mode 100644 index 0000000000000..f3ead0161961f Binary files /dev/null and b/nano/images/nanomapBackground.png differ diff --git a/nano/js/nano_state.js b/nano/js/nano_state.js index bca2abd6ac9eb..c230eb3920120 100644 --- a/nano/js/nano_state.js +++ b/nano/js/nano_state.js @@ -13,6 +13,9 @@ function NanoStateClass() { } NanoStateClass.prototype.key = null; +NanoStateClass.prototype.layoutRendered = false; +NanoStateClass.prototype.contentRendered = false; +NanoStateClass.prototype.mapInitialised = false; NanoStateClass.prototype.isCurrent = function () { return NanoStateManager.getCurrentState() == this; @@ -45,8 +48,47 @@ NanoStateClass.prototype.onUpdate = function (data) { try { - $("#uiLayout").html(NanoTemplate.parse('layout', data)); // render the 'mail' template to the #mainTemplate div - $("#uiContent").html(NanoTemplate.parse('main', data)); // render the 'mail' template to the #mainTemplate div + if (!this.layoutRendered || (data['config'].hasOwnProperty('autoUpdateLayout') && data['config']['autoUpdateLayout'])) + { + $("#uiLayout").html(NanoTemplate.parse('layout', data)); // render the 'mail' template to the #mainTemplate div + this.layoutRendered = true; + } + if (!this.contentRendered || (data['config'].hasOwnProperty('autoUpdateContent') && data['config']['autoUpdateContent'])) + { + $("#uiContent").html(NanoTemplate.parse('main', data)); // render the 'mail' template to the #mainTemplate div + this.contentRendered = true; + } + if (NanoTemplate.templateExists('mapContent')) + { + if (!this.mapInitialised) + { + // Add drag functionality to the map ui + $('#uiMap').drags({handle : '#uiMapImage'}); + + this.mapInitialised = true; + } + + $("#uiMapContent").html(NanoTemplate.parse('mapContent', data)); // render the 'mapContent' template to the #uiMapContent div + + if (data['config'].hasOwnProperty('showMap') && data['config']['showMap']) + { + $('#uiContent').addClass('hidden'); + $('#uiMapWrapper').removeClass('hidden'); + } + else + { + $('#uiMapWrapper').addClass('hidden'); + $('#uiContent').removeClass('hidden'); + } + } + if (NanoTemplate.templateExists('mapHeader')) + { + $("#uiMapHeader").html(NanoTemplate.parse('mapHeader', data)); // render the 'mapHeader' template to the #uiMapHeader div + } + if (NanoTemplate.templateExists('mapFooter')) + { + $("#uiMapFooter").html(NanoTemplate.parse('mapFooter', data)); // render the 'mapFooter' template to the #uiMapFooter div + } } catch(error) { diff --git a/nano/js/nano_template.js b/nano/js/nano_template.js index bdb1eeeb5f1c6..f8f5d2659485a 100644 --- a/nano/js/nano_template.js +++ b/nano/js/nano_template.js @@ -88,6 +88,9 @@ var NanoTemplate = function () { addTemplate: function (key, templateString) { _templates[key] = templateString; }, + templateExists: function (key) { + return _templates.hasOwnProperty(key); + }, parse: function (templateKey, data) { if (!_compiledTemplates.hasOwnProperty(templateKey) || !_compiledTemplates[templateKey]) { if (!_templates.hasOwnProperty(templateKey)) { diff --git a/nano/js/nano_utility.js b/nano/js/nano_utility.js index 40cd827d132f9..eb3f5b0ffc71d 100644 --- a/nano/js/nano_utility.js +++ b/nano/js/nano_utility.js @@ -145,4 +145,46 @@ Function.prototype.inheritsFrom = function (parentClassOrObject) { this.prototype.constructor = this; this.prototype.parent = parentClassOrObject.prototype; return this; -}; \ No newline at end of file +}; + +(function($) { + $.fn.drags = function(opt) { + + opt = $.extend({handle:"",cursor:"move"}, opt); + + if(opt.handle === "") { + var $el = this; + } else { + var $el = this.find(opt.handle); + } + + return $el.css('cursor', opt.cursor).on("mousedown", function(e) { + if(opt.handle === "") { + var $drag = $(this).addClass('draggable'); + } else { + var $drag = $(this).addClass('active-handle').parent().addClass('draggable'); + } + var z_idx = $drag.css('z-index'), + drg_h = $drag.outerHeight(), + drg_w = $drag.outerWidth(), + pos_y = $drag.offset().top + drg_h - e.pageY, + pos_x = $drag.offset().left + drg_w - e.pageX; + $drag.css('z-index', 1000).parents().on("mousemove", function(e) { + $('.draggable').offset({ + top:e.pageY + pos_y - drg_h, + left:e.pageX + pos_x - drg_w + }).on("mouseup", function() { + $(this).removeClass('draggable').css('z-index', z_idx); + }); + }); + e.preventDefault(); // disable selection + }).on("mouseup", function() { + if(opt.handle === "") { + $(this).removeClass('draggable'); + } else { + $(this).removeClass('active-handle').parent().removeClass('draggable'); + } + }); + + } +})(jQuery); \ No newline at end of file diff --git a/nano/mapbase1024.png b/nano/mapbase1024.png new file mode 100644 index 0000000000000..a927a71b6e7b3 Binary files /dev/null and b/nano/mapbase1024.png differ diff --git a/nano/templates/crew_monitor.tmpl b/nano/templates/crew_monitor.tmpl index f789f6052c137..db3e5673b475a 100644 --- a/nano/templates/crew_monitor.tmpl +++ b/nano/templates/crew_monitor.tmpl @@ -1,6 +1,8 @@ +Title: Crew Monitoring Console (Main content) +Used In File(s): \code\game\machinery\computer\crew.dm + --> +{{:helper.link('Show Tracker Map', 'pin-s', {'showMap' : 1})}} {{for data.crewmembers}} {{if value.sensor_type == 1}} diff --git a/nano/templates/crew_monitor_map_content.tmpl b/nano/templates/crew_monitor_map_content.tmpl new file mode 100644 index 0000000000000..2a788364473bc --- /dev/null +++ b/nano/templates/crew_monitor_map_content.tmpl @@ -0,0 +1,9 @@ + +{{for data.crewmembers}} + {{if value.sensor_type == 3}} +
+ {{/if}} +{{/for}} diff --git a/nano/templates/crew_monitor_map_header.tmpl b/nano/templates/crew_monitor_map_header.tmpl new file mode 100644 index 0000000000000..2793e66c27b56 --- /dev/null +++ b/nano/templates/crew_monitor_map_header.tmpl @@ -0,0 +1,5 @@ + +{{:helper.link('Show Detail List', 'script', {'showMap' : 0})}} \ No newline at end of file diff --git a/nano/templates/layout_default.tmpl b/nano/templates/layout_default.tmpl index fed78460879ab..174aafab4601f 100644 --- a/nano/templates/layout_default.tmpl +++ b/nano/templates/layout_default.tmpl @@ -1,4 +1,22 @@ -
{{:config.title}}
+
{{:config.title}}
+
Initiating...
\ No newline at end of file