diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm index 4e9d7fd6ad..edc57f9a4b 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -153,6 +153,9 @@ What is the naming convention for planes or layers? #define CRIT_LAYER 18.3 //Client UI HUD stuff +#define PLANE_HOLOMAP_FRAME 92 +#define PLANE_HOLOMAP 93 // Holomap rendered here +#define PLANE_HOLOMAP_ICONS 94 // Holomap markers and bobbins #define PLANE_PLAYER_HUD 95 //The character's UI is on this plane #define LAYER_HUD_UNDER 1 //Under the HUD items #define LAYER_HUD_BASE 2 //The HUD items themselves diff --git a/code/__defines/holomap.dm b/code/__defines/holomap.dm index 0b93e03ae0..b7dc0305ad 100644 --- a/code/__defines/holomap.dm +++ b/code/__defines/holomap.dm @@ -8,6 +8,45 @@ #define HOLOMAP_ICON_SIZE 480 // Pixel width & height of the holomap icon. Used for auto-centering etc. #define ui_holomap "CENTER-7, CENTER-7" // Screen location of the holomap "hud" +//Holomap filters +#define HOLOMAP_FILTER_DEATHSQUAD 1 +#define HOLOMAP_FILTER_ERT 2 +#define HOLOMAP_FILTER_NUKEOPS 4 +#define HOLOMAP_FILTER_ELITESYNDICATE 8 +#define HOLOMAP_FILTER_VOX 16 +#define HOLOMAP_FILTER_STATIONMAP 32 +#define HOLOMAP_FILTER_STATIONMAP_STRATEGIC 64//features markers over the captain's office, the armory, the SMES +#define HOLOMAP_FILTER_CULT 128//bloodstone locators + +#define HOLOMAP_EXTRA_STATIONMAP "stationmapformatted" +#define HOLOMAP_EXTRA_STATIONMAP_STRATEGIC "stationmapstrategic" +#define HOLOMAP_EXTRA_STATIONMAPAREAS "stationareas" +#define HOLOMAP_EXTRA_STATIONMAPSMALL_NORTH "stationmapsmallnorth" +#define HOLOMAP_EXTRA_STATIONMAPSMALL_SOUTH "stationmapsmallsouth" +#define HOLOMAP_EXTRA_STATIONMAPSMALL_EAST "stationmapsmalleast" +#define HOLOMAP_EXTRA_STATIONMAPSMALL_WEST "stationmapsmallwest" +#define HOLOMAP_EXTRA_CULTMAP "cultmap" + +#define HOLOMAP_MARKER_SMES "smes" +#define HOLOMAP_MARKER_DISK "diskspawn" +#define HOLOMAP_MARKER_SKIPJACK "skipjack" +#define HOLOMAP_MARKER_SYNDISHUTTLE "syndishuttle" +#define HOLOMAP_MARKER_BLOODSTONE "bloodstone" +#define HOLOMAP_MARKER_BLOODSTONE_BROKEN "bloodstone-broken" +#define HOLOMAP_MARKER_BLOODSTONE_ANCHOR "bloodstone-narsie" +#define HOLOMAP_MARKER_CULT_ALTAR "altar" +#define HOLOMAP_MARKER_CULT_FORGE "forge" +#define HOLOMAP_MARKER_CULT_SPIRE "spire" +#define HOLOMAP_MARKER_CULT_ENTRANCE "path_entrance" +#define HOLOMAP_MARKER_CULT_EXIT "path_exit" +#define HOLOMAP_MARKER_CULT_RUNE "rune" + +#define HOLOMAP_DRAW_NORMAL 0 +#define HOLOMAP_DRAW_FULL 1 +#define HOLOMAP_DRAW_EMPTY 2 +#define HOLOMAP_DRAW_PATH 3 +#define HOLOMAP_DRAW_HALLWAY 4 + // Holomap colors #define HOLOMAP_OBSTACLE "#FFFFFFDD" // Color of walls and barriers #define HOLOMAP_PATH "#66666699" // Color of floors diff --git a/code/__defines/map.dm b/code/__defines/map.dm index 0638ee77ef..a3a27dab9e 100644 --- a/code/__defines/map.dm +++ b/code/__defines/map.dm @@ -8,6 +8,7 @@ #define MAP_LEVEL_CONSOLES 0x040 // Z-levels available to various consoles, such as the crew monitor (when that gets coded in). Defaults to station_levels if unset. #define MAP_LEVEL_XENOARCH_EXEMPT 0x080 // Z-levels exempt from xenoarch digsite generation. #define MAP_LEVEL_PERSIST 0x100 // Z-levels where SSpersistence should persist between rounds +#define MAP_LEVEL_MAPPABLE 0x200 // Z-levels where mapping units will work fully // Misc map defines. #define SUBMAP_MAP_EDGE_PAD 15 // Automatically created submaps are forbidden from being this close to the main map's edge. \ No newline at end of file diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index f293a54444..358db89db4 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -35,6 +35,11 @@ GLOBAL_LIST_EMPTY(respawn_timers) var/global/list/poster_designs = list() var/global/list/NT_poster_designs = list() +// Holomaps +var/global/list/holomap_markers = list() +var/global/list/mapping_units = list() +var/global/list/mapping_beacons = list() + //Preferences stuff //Hairstyles var/global/list/hair_styles_list = list() //stores /datum/sprite_accessory/hair indexed by name diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 4bd324d7cf..56d79f5836 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -87,12 +87,6 @@ var/list/global_huds = list( science = setup_overlay("science_hud") material = setup_overlay("material_hud") - // The holomap screen object is actually totally invisible. - // Station maps work by setting it as an images location before sending to client, not - // actually changing the icon or icon state of the screen object itself! - // Why do they work this way? I don't know really, that is how /vg designed them, but since they DO - // work this way, we can take advantage of their immutability by making them part of - // the global_hud (something we have and /vg doesn't) instead of an instance per mob. holomap = new /obj/screen() holomap.name = "holomap" holomap.icon = null @@ -188,7 +182,7 @@ var/list/global_huds = list( var/icon/ui_style var/ui_color var/ui_alpha - + var/list/minihuds = list() datum/hud/New(mob/owner) @@ -198,6 +192,7 @@ datum/hud/New(mob/owner) /datum/hud/Destroy() . = ..() + qdel_null(minihuds) grab_intent = null hurt_intent = null disarm_intent = null @@ -216,7 +211,6 @@ datum/hud/New(mob/owner) hotkeybuttons = null // item_action_list = null // ? mymob = null - qdel_null(minihuds) /datum/hud/proc/hidden_inventory_update() if(!mymob) return @@ -307,7 +301,7 @@ datum/hud/New(mob/owner) /datum/hud/proc/instantiate() if(!ismob(mymob)) return 0 - + mymob.create_mob_hud(src) persistant_inventory_update() diff --git a/code/_onclick/hud/minihud.dm b/code/_onclick/hud/minihud.dm new file mode 100644 index 0000000000..1b8cd69caf --- /dev/null +++ b/code/_onclick/hud/minihud.dm @@ -0,0 +1,36 @@ +/datum/mini_hud + var/datum/hud/main_hud + var/list/screenobjs + var/needs_processing = FALSE + +/datum/mini_hud/New(var/datum/hud/other) + apply_to_hud(other) + if(needs_processing) + START_PROCESSING(SSprocessing, src) + +/datum/mini_hud/Destroy() + unapply_to_hud() + if(needs_processing) + STOP_PROCESSING(SSprocessing, src) + QDEL_LIST_NULL(screenobjs) + return ..() + +// Apply to a real /datum/hud +/datum/mini_hud/proc/apply_to_hud(var/datum/hud/other) + if(main_hud) + unapply_to_hud(main_hud) + main_hud = other + main_hud.apply_minihud(src) + +// Remove from a real /datum/hud +/datum/mini_hud/proc/unapply_to_hud() + main_hud?.remove_minihud(src) + main_hud = null + +// Update the hud +/datum/mini_hud/process() + return PROCESS_KILL // You shouldn't be here! + +// Return a list of screen objects we use +/datum/mini_hud/proc/get_screen_objs(var/mob/M) + return screenobjs.Copy() diff --git a/code/_onclick/hud/minihud_mapper.dm b/code/_onclick/hud/minihud_mapper.dm new file mode 100644 index 0000000000..1b9246d729 --- /dev/null +++ b/code/_onclick/hud/minihud_mapper.dm @@ -0,0 +1,13 @@ +// Specific types +/datum/mini_hud/mapper + var/obj/item/device/mapping_unit/owner + +/datum/mini_hud/mapper/New(var/datum/hud/other, owner) + src.owner = owner + screenobjs = list(new /obj/screen/movable/mapper_holder(null, owner)) + ..() + +/datum/mini_hud/mapper/Destroy() + owner?.hud_item = null + owner?.hud_datum = null + return ..() \ No newline at end of file diff --git a/code/_onclick/hud/rigmech.dm b/code/_onclick/hud/minihud_rigmech.dm similarity index 86% rename from code/_onclick/hud/rigmech.dm rename to code/_onclick/hud/minihud_rigmech.dm index 91632e1604..e54a733f80 100644 --- a/code/_onclick/hud/rigmech.dm +++ b/code/_onclick/hud/minihud_rigmech.dm @@ -1,40 +1,3 @@ -/datum/mini_hud - var/datum/hud/main_hud - var/list/screenobjs = list() - var/list/types_to_instantiate - var/needs_processing = FALSE - -/datum/mini_hud/New(var/datum/hud/other) - apply_to_hud(other) - if(needs_processing) - START_PROCESSING(SSprocessing, src) - -/datum/mini_hud/Destroy() - main_hud?.remove_minihud(src) - main_hud = null - if(needs_processing) - STOP_PROCESSING(SSprocessing, src) - return ..() - -// Apply to a real /datum/hud -/datum/mini_hud/proc/apply_to_hud(var/datum/hud/other) - if(main_hud) - unapply_to_hud(main_hud) - main_hud = other - main_hud.apply_minihud(src) - -// Remove from a real /datum/hud -/datum/mini_hud/proc/unapply_to_hud(var/datum/hud/other) - main_hud.remove_minihud(src) - -// Update the hud -/datum/mini_hud/process() - return PROCESS_KILL // You shouldn't be here! - -// Return a list of screen objects we use -/datum/mini_hud/proc/get_screen_objs(var/mob/M) - return screenobjs - // Specific types /datum/mini_hud/rig var/obj/item/weapon/rig/owner_rig diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 6a42572c2f..9d6da07f0b 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -655,3 +655,218 @@ holder.screen -= src holder = null return ..() + + +/** + * This object holds all the on-screen elements of the mapping unit. + * It has a decorative frame and onscreen buttons. The map itself is drawn + * using a white mask and multiplying the mask against it to crop it to the + * size of the screen. This is not ideal, as filter() is faster, and has + * alpha masks, but the alpha masks it has can't be animated, so the 'ping' + * mode of this device isn't possible using that technique. + * + * The markers use that technique, though, so at least there's that. + */ +/obj/screen/movable/mapper_holder + name = "gps unit" + icon = null + icon_state = "" + screen_loc = "CENTER,CENTER" + alpha = 255 + appearance_flags = KEEP_TOGETHER + mouse_opacity = 1 + plane = PLANE_HOLOMAP + + var/running = FALSE + + var/obj/screen/mapper/mask_full/mask_full + var/obj/screen/mapper/mask_ping/mask_ping + var/obj/screen/mapper/bg/bg + + var/obj/screen/mapper/frame/frame + var/obj/screen/mapper/powbutton/powbutton + var/obj/screen/mapper/mapbutton/mapbutton + + var/obj/item/device/mapping_unit/owner + var/obj/screen/mapper/extras_holder/extras_holder + +/obj/screen/movable/mapper_holder/Initialize(mapload, newowner) + owner = newowner + + mask_full = new(src) // Full white square mask + mask_ping = new(src) // Animated 'pinging' mask + bg = new(src) // Background color, holds map in vis_contents, uses mult against masks + + frame = new(src) // Decorative frame + powbutton = new(src) // Clickable button + mapbutton = new(src) // Clickable button + + frame.icon_state = initial(frame.icon_state)+owner.hud_frame_hint + + /** + * The vis_contents layout is: this(frame,extras_holder,mask(bg(map))) + * bg is set to BLEND_MULTIPLY against the mask to crop it. + */ + + mask_full.vis_contents.Add(bg) + mask_ping.vis_contents.Add(bg) + frame.vis_contents.Add(powbutton,mapbutton) + vis_contents.Add(frame) + + +/obj/screen/movable/mapper_holder/Destroy() + qdel_null(mask_full) + qdel_null(mask_ping) + qdel_null(bg) + + qdel_null(frame) + qdel_null(powbutton) + qdel_null(mapbutton) + + extras_holder = null + owner = null + return ..() + +/obj/screen/movable/mapper_holder/proc/update(var/obj/screen/mapper/map, var/obj/screen/mapper/extras_holder/extras, ping = FALSE) + if(!running) + running = TRUE + if(ping) + vis_contents.Add(mask_ping) + else + vis_contents.Add(mask_full) + + bg.vis_contents.Cut() + bg.vis_contents.Add(map) + + if(extras && !extras_holder) + extras_holder = extras + vis_contents += extras_holder + if(!extras && extras_holder) + vis_contents -= extras_holder + extras_holder = null + +/obj/screen/movable/mapper_holder/proc/powerClick() + if(running) + off() + else + on() + +/obj/screen/movable/mapper_holder/proc/mapClick() + if(owner) + if(running) + off() + owner.pinging = !owner.pinging + on() + +/obj/screen/movable/mapper_holder/proc/off(var/inform = TRUE) + frame.cut_overlay("powlight") + bg.vis_contents.Cut() + vis_contents.Remove(mask_ping, mask_full, extras_holder) + extras_holder = null + running = FALSE + if(inform) + owner.stop_updates() + +/obj/screen/movable/mapper_holder/proc/on(var/inform = TRUE) + frame.add_overlay("powlight") + if(inform) + owner.start_updates() + +// Prototype +/obj/screen/mapper + plane = PLANE_HOLOMAP + mouse_opacity = 0 + var/obj/screen/movable/mapper_holder/parent + +/obj/screen/mapper/New() + ..() + parent = loc + +/obj/screen/mapper/Destroy() + parent = null + return ..() + +// Holds the actual map image +/obj/screen/mapper/map + var/offset_x = 32 + var/offset_y = 32 + +// I really wish I could use filters for this instead of this multiplication-masking technique +// but alpha filters can't be animated, which means I can't use them for the 'sonar ping' mode. +// If filters start supporting animated icons in the future (for the alpha mask filter), +// you should definitely replace these with that technique instead. +/obj/screen/mapper/mask_full + icon = 'icons/effects/64x64.dmi' + icon_state = "mapper_mask" + +/obj/screen/mapper/mask_ping + icon = 'icons/effects/64x64.dmi' + icon_state = "mapper_ping" + +/obj/screen/mapper/bg + icon = 'icons/effects/64x64.dmi' + icon_state = "mapper_bg" + + blend_mode = BLEND_MULTIPLY + appearance_flags = KEEP_TOGETHER + +// Frame/deco components +/obj/screen/mapper/frame + icon = 'icons/effects/gpshud.dmi' + icon_state = "frame" + plane = PLANE_HOLOMAP_FRAME + pixel_x = -18 + pixel_y = -29 + mouse_opacity = 1 + vis_flags = VIS_INHERIT_ID + +/obj/screen/mapper/powbutton + icon = 'icons/effects/gpshud.dmi' + icon_state = "powbutton" + plane = PLANE_HOLOMAP_FRAME + mouse_opacity = 1 + +/obj/screen/mapper/powbutton/Click() + if(!usr.checkClickCooldown()) + return TRUE + if(usr.stat || usr.paralysis || usr.stunned || usr.weakened) + return TRUE + if(istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech + return TRUE + parent.powerClick() + flick("powClick",src) + usr << get_sfx("button") + return TRUE + +/obj/screen/mapper/mapbutton + icon = 'icons/effects/gpshud.dmi' + icon_state = "mapbutton" + plane = PLANE_HOLOMAP_FRAME + mouse_opacity = 1 + +/obj/screen/mapper/mapbutton/Click() + if(!usr.checkClickCooldown()) + return TRUE + if(usr.stat || usr.paralysis || usr.stunned || usr.weakened) + return TRUE + if(istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech + return TRUE + parent.mapClick() + flick("mapClick",src) + usr << get_sfx("button") + return TRUE + +// Markers are 16x16, people have apparently settled on centering them on the 8,8 pixel +/obj/screen/mapper/marker + icon = 'icons/holomap_markers.dmi' + plane = PLANE_HOLOMAP_ICONS + + var/offset_x = -8 + var/offset_y = -8 + +// Holds markers in its vis_contents. It uses an alpha filter to crop them to the HUD screen size +/obj/screen/mapper/extras_holder + icon = null + icon_state = null + plane = PLANE_HOLOMAP_ICONS + appearance_flags = KEEP_TOGETHER diff --git a/code/modules/holomap/generate_holomap.dm b/code/modules/holomap/generate_holomap.dm index e4917e0ba0..9978416424 100644 --- a/code/modules/holomap/generate_holomap.dm +++ b/code/modules/holomap/generate_holomap.dm @@ -48,27 +48,23 @@ // Generates the "base" holomap for one z-level, showing only the physical structure of walls and paths. /datum/controller/subsystem/holomaps/proc/generateHoloMinimap(var/zLevel = 1) - // Save these values now to avoid a bazillion array lookups - var/offset_x = HOLOMAP_PIXEL_OFFSET_X(zLevel) - var/offset_y = HOLOMAP_PIXEL_OFFSET_Y(zLevel) - // Sanity checks - Better to generate a helpful error message now than have DrawBox() runtime var/icon/canvas = icon(HOLOMAP_ICON, "blank") - if(world.maxx + offset_x > canvas.Width()) - crash_with("Minimap for z=[zLevel] : world.maxx ([world.maxx]) + holomap_offset_x ([offset_x]) must be <= [canvas.Width()]") - if(world.maxy + offset_y > canvas.Height()) - crash_with("Minimap for z=[zLevel] : world.maxy ([world.maxy]) + holomap_offset_y ([offset_y]) must be <= [canvas.Height()]") + if(world.maxx > canvas.Width()) + crash_with("Minimap for z=[zLevel] : world.maxx ([world.maxx]) must be <= [canvas.Width()]") + if(world.maxy > canvas.Height()) + crash_with("Minimap for z=[zLevel] : world.maxy ([world.maxy]) must be <= [canvas.Height()]") for(var/x = 1 to world.maxx) for(var/y = 1 to world.maxy) var/turf/tile = locate(x, y, zLevel) if(tile && tile.loc:holomapAlwaysDraw()) if(IS_ROCK(tile)) - canvas.DrawBox(HOLOMAP_ROCK, x + offset_x, y + offset_y) + canvas.DrawBox(HOLOMAP_ROCK, x, y) if(IS_OBSTACLE(tile)) - canvas.DrawBox(HOLOMAP_OBSTACLE, x + offset_x, y + offset_y) + canvas.DrawBox(HOLOMAP_OBSTACLE, x, y) else if(IS_PATH(tile)) - canvas.DrawBox(HOLOMAP_PATH, x + offset_x, y + offset_y) + canvas.DrawBox(HOLOMAP_PATH, x, y) // Check sleeping after each row to avoid *completely* destroying the server CHECK_TICK return canvas @@ -78,16 +74,12 @@ // Leshana: I'm guessing this map will get overlayed on top of the base map at runtime? We'll see. // Wait, seems we actually blend the area map on top of it right now! Huh. /datum/controller/subsystem/holomaps/proc/generateStationMinimap(var/zLevel) - // Save these values now to avoid a bazillion array lookups - var/offset_x = HOLOMAP_PIXEL_OFFSET_X(zLevel) - var/offset_y = HOLOMAP_PIXEL_OFFSET_Y(zLevel) - // Sanity checks - Better to generate a helpful error message now than have DrawBox() runtime var/icon/canvas = icon(HOLOMAP_ICON, "blank") - if(world.maxx + offset_x > canvas.Width()) - crash_with("Minimap for z=[zLevel] : world.maxx ([world.maxx]) + holomap_offset_x ([offset_x]) must be <= [canvas.Width()]") - if(world.maxy + offset_y > canvas.Height()) - crash_with("Minimap for z=[zLevel] : world.maxy ([world.maxy]) + holomap_offset_y ([offset_y]) must be <= [canvas.Height()]") + if(world.maxx > canvas.Width()) + crash_with("Minimap for z=[zLevel] : world.maxx ([world.maxx]) must be <= [canvas.Width()]") + if(world.maxy > canvas.Height()) + crash_with("Minimap for z=[zLevel] : world.maxy ([world.maxy]) must be <= [canvas.Height()]") for(var/x = 1 to world.maxx) for(var/y = 1 to world.maxy) @@ -95,7 +87,7 @@ if(tile && tile.loc) var/area/areaToPaint = tile.loc if(areaToPaint.holomap_color) - canvas.DrawBox(areaToPaint.holomap_color, x + offset_x, y + offset_y) + canvas.DrawBox(areaToPaint.holomap_color, x, y) // Save this nice area-colored canvas in case we want to layer it or something I guess extraMiniMaps["[HOLOMAP_EXTRA_STATIONMAPAREAS]_[zLevel]"] = canvas @@ -129,13 +121,17 @@ var/icon/small_map = icon(HOLOMAP_ICON, "blank") // For each zlevel in turn, overlay them on top of each other for(var/zLevel in zlevels) + var/offset_x = HOLOMAP_PIXEL_OFFSET_X(zLevel) || 1 + var/offset_y = HOLOMAP_PIXEL_OFFSET_Y(zLevel) || 1 + var/icon/z_terrain = icon(holoMiniMaps[zLevel]) - z_terrain.Blend(HOLOMAP_HOLOFIER, ICON_MULTIPLY) - big_map.Blend(z_terrain, ICON_OVERLAY) - small_map.Blend(z_terrain, ICON_OVERLAY) + z_terrain.Blend(HOLOMAP_HOLOFIER, ICON_MULTIPLY, offset_x, offset_y) + big_map.Blend(z_terrain, ICON_OVERLAY, offset_x, offset_y) + small_map.Blend(z_terrain, ICON_OVERLAY, offset_x, offset_y) + var/icon/z_areas = extraMiniMaps["[HOLOMAP_EXTRA_STATIONMAPAREAS]_[zLevel]"] - big_map.Blend(z_areas, ICON_OVERLAY) - small_map.Blend(z_areas, ICON_OVERLAY) + big_map.Blend(z_areas, ICON_OVERLAY, offset_x, offset_y) + small_map.Blend(z_areas, ICON_OVERLAY, offset_x, offset_y) // Then scale and rotate to make the actual small map we will use small_map.Scale(WORLD_ICON_SIZE, WORLD_ICON_SIZE) diff --git a/code/modules/holomap/mapper.dm b/code/modules/holomap/mapper.dm new file mode 100644 index 0000000000..a068c752eb --- /dev/null +++ b/code/modules/holomap/mapper.dm @@ -0,0 +1,444 @@ +/obj/item/device/mapping_unit + name = "mapping unit" + desc = "A portable mapping unit, capable of locating other similar units on a map. Also has a short-range sonar mapping system." + icon_state = "mapping_unit" + item_state = null + w_class = ITEMSIZE_SMALL + + //Holomap stuff + var/marker_prefix = "basic" + var/base_prefix = "basic" + var/map_color = null + var/mapper_filter = HOLOMAP_FILTER_STATIONMAP + + var/list/prefix_update_head + var/list/prefix_update_rig + + // These are local because they are different for every holochip. + // The maps and icons are all pixel_x and pixel_y'd so we're in the center. + var/list/map_image_cache = list() + var/list/icon_image_cache = list() + + var/pinging = FALSE + var/updating = FALSE + var/global/icon/mask_icon + var/obj/screen/mapper/extras_holder/extras_holder + var/hud_frame_hint + + var/datum/mini_hud/mapper/hud_datum + var/obj/screen/movable/mapper_holder/hud_item + + var/obj/item/weapon/cell/cell + var/cell_type = /obj/item/weapon/cell/device + var/power_usage = 0.5 // Usage per map scan (doubled for ping mode) + var/uses_power = 1 // If it uses power at all + + var/list/debug_mappers_list + var/list/debug_beacons_list + +/obj/item/device/mapping_unit/deathsquad + name = "deathsquad mapping unit" + icon_state = "mapping_unit_ds" + marker_prefix = "ds" + mapper_filter = HOLOMAP_FILTER_DEATHSQUAD + //map_color = "#0B74B4" + hud_frame_hint = "_ds" + +/obj/item/device/mapping_unit/operative + name = "nuclear operative mapping unit" + icon_state = "mapping_unit_op" + marker_prefix = "op" + mapper_filter = HOLOMAP_FILTER_NUKEOPS + //map_color = "#13B40B" + hud_frame_hint = "_op" + +/obj/item/device/mapping_unit/ert + name = "emergency response team mapping unit" + icon_state = "mapping_unit_ert" + marker_prefix = "ert" + mapper_filter = HOLOMAP_FILTER_ERT + //map_color = "#5FFF28" + hud_frame_hint = "_ert" + + prefix_update_head = list( + "/obj/item/clothing/head/helmet/ert/command" = "ertc", + "/obj/item/clothing/head/helmet/ert/security" = "erts", + "/obj/item/clothing/head/helmet/ert/engineer" = "erte", + "/obj/item/clothing/head/helmet/ert/medical" = "ertm", + ) + + prefix_update_rig = list( + "/obj/item/weapon/rig/ert" = "ertc", + "/obj/item/weapon/rig/ert/security" = "erts", + "/obj/item/weapon/rig/ert/engineer" = "erte", + "/obj/item/weapon/rig/ert/medical" = "ertm" + ) + +/obj/item/device/mapping_unit/Initialize() + . = ..() + base_prefix = marker_prefix + + if(!mask_icon) + mask_icon = icon('icons/effects/64x64.dmi', "mapper_mask") + + extras_holder = new() + + var/obj/screen/mapper/marker/mark = new() + mark.icon = 'icons/effects/64x64.dmi' + mark.icon_state = "mapper_none" + mark.layer = 10 + icon_image_cache["bad"] = mark + + var/obj/screen/mapper/map/tmp = new() + var/icon/canvas = icon(HOLOMAP_ICON, "blank") + canvas.Crop(1,1,world.maxx,world.maxy) + canvas.DrawBox("#A7BE97",1,1,world.maxx,world.maxy) + tmp.icon = icon + map_image_cache["bad"] = tmp + + if(uses_power && cell_type) + cell = new cell_type(src) + + debug_mappers_list = mapping_units + debug_beacons_list = mapping_beacons + +/obj/item/device/mapping_unit/Destroy() + mapping_units -= src + + last_run() + + map_image_cache.Cut() + icon_image_cache.Cut() + qdel_null(extras_holder) + + return ..() + +/obj/item/device/mapping_unit/dropped(mob/dropper) + if(loc != dropper) // Not just a juggle + hide_device() + +/obj/item/device/mapping_unit/attack_self(mob/user) + if(user.stat != CONSCIOUS) + return + + if(!ishuman(user)) + to_chat(user, "Only humanoids can use this device.") + return + + var/mob/living/carbon/human/H = user + + if(!ishuman(loc) || user != loc) + to_chat(H, "This device needs to be on your person.") + + if(hud_datum?.main_hud) + hide_device() + to_chat(H, "You put \the [src] away.") + else + show_device(H) + to_chat(H, "You hold \the [src] where you can see it.") + +/obj/item/device/mapping_unit/attack_hand(mob/user) + if(cell && user.get_inactive_hand() == src) // click with empty off hand + to_chat(user,"You eject \the [cell] from \the [src].") + user.put_in_hands(cell) + cell = null + if(updating) + stop_updates() + else + return ..() + +/obj/item/device/mapping_unit/attackby(obj/W, mob/user) + if(istype(W,cell_type) && !cell) + cell = W + cell.update_icon() //Why doesn't a cell do this already? :| + user.unEquip(cell) + cell.forceMove(src) + to_chat(user,"You insert \the [cell] into \the [src].") + + +/obj/item/device/mapping_unit/proc/first_run(mob/user) + hud_datum = new(user.hud_used, src) + hud_item = hud_datum.screenobjs[1] + +/obj/item/device/mapping_unit/proc/show_device(mob/user) + if(!hud_datum) + first_run(user) + else + hud_datum.apply_to_hud(user.hud_used) + +/obj/item/device/mapping_unit/proc/start_updates() + mapping_units += src + updating = TRUE + START_PROCESSING(SSobj, src) + process() + + + +/obj/item/device/mapping_unit/proc/stop_updates() + mapping_units -= src + STOP_PROCESSING(SSobj, src) + updating = FALSE + if(hud_item) + hud_item.off(FALSE) + +/obj/item/device/mapping_unit/proc/hide_device() + hud_datum.unapply_to_hud() + +/obj/item/device/mapping_unit/proc/last_run() + stop_updates() + if(!QDELETED(hud_datum)) + qdel(hud_datum) + hud_datum = null + hud_item = null + + +/obj/item/device/mapping_unit/process() + if(!updating || (uses_power && !cell)) + stop_updates() + return + + if(uses_power) + var/power_to_use = pinging ? power_usage*2 : power_usage + if(cell.use(power_to_use) != power_to_use) // we weren't able to use our full power_usage amount! + visible_message("\The [src] flickers before going dull.") + stop_updates() + return + + if(!hud_item || !hud_datum) + log_error("Mapping device tried to update with missing hud_item or hud_datum") + stop_updates() + last_run() + return + + update_holomap() + +#define HOLOMAP_ERROR 0 +#define HOLOMAP_YOU 1 +#define HOLOMAP_OTHER 2 +#define HOLOMAP_DEAD 3 + +/obj/item/device/mapping_unit/proc/update_holomap() + var/turf/T = get_turf(src) + if(!T)//nullspace begone! + stop_updates() + return + + var/T_x = T.x // Used many times, just grab it to avoid derefs + var/T_y = T.y + var/T_z = T.z + + var/obj/screen/mapper/map/bgmap + var/list/extras = list() + + var/map_cache_key = "[T_z]" + var/badmap = FALSE + if(!pinging && using_map && !(T_z in using_map.mappable_levels)) + map_cache_key = "bad" + badmap = TRUE + + // Cache miss + if(!(map_cache_key in map_image_cache)) + var/mutable_appearance/map_app = new() + map_app.appearance_flags = PIXEL_SCALE + map_app.plane = PLANE_HOLOMAP + map_app.layer = HUD_LAYER + map_app.color = map_color + + if(!SSholomaps.holoMiniMaps[T_z]) + var/obj/screen/mapper/map/baddo = map_image_cache["bad"] + map_app.icon = icon(baddo.icon) + badmap = TRUE + // SSholomaps did map it and we're allowed to see it + else + map_app.icon = icon(SSholomaps.holoMiniMaps[T.z]) + + // Apply markers + for(var/marker in holomap_markers) + var/datum/holomap_marker/holomarker = holomap_markers[marker] + if(holomarker.z == T_z && holomarker.filter & mapper_filter) + var/image/markerImage = image(holomarker.icon,holomarker.id) + markerImage.plane = FLOAT_PLANE + markerImage.layer = FLOAT_LAYER + markerImage.appearance_flags = RESET_COLOR|PIXEL_SCALE + markerImage.pixel_x = holomarker.x+holomarker.offset_x + markerImage.pixel_y = holomarker.y+holomarker.offset_y + map_app.overlays += markerImage + + var/obj/screen/mapper/map/tmp = new() + tmp.appearance = map_app + map_image_cache[map_cache_key] = tmp + + bgmap = map_image_cache[map_cache_key] + + // The holomap moves around, the user is always in the center. This slides the holomap. + var/offset_x = bgmap.offset_x + var/offset_y = bgmap.offset_y + extras_holder.pixel_x = bgmap.pixel_x = -1*T_x + offset_x + extras_holder.pixel_y = bgmap.pixel_y = -1*T_y + offset_y + + // Populate other mapper icons + for(var/hc in mapping_units) + var/obj/item/device/mapping_unit/HC = hc + if(HC.mapper_filter != mapper_filter) + continue + var/mob_indicator = HOLOMAP_ERROR + var/turf/TU = get_turf(HC) + // Mapper not on a turf or elsewhere + if(!TU || (TU.z != T_z)) + continue + + // We're the marker + if(HC == src) + mob_indicator = HOLOMAP_YOU + + // The marker is held by a borg + else if(isrobot(HC.loc)) + var/mob/living/silicon/robot/R = HC.loc + if(R.stat == DEAD) + mob_indicator = HOLOMAP_DEAD + else + mob_indicator = HOLOMAP_OTHER + + // The marker is worn by a human + else if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + if(H.stat == DEAD) + mob_indicator = HOLOMAP_DEAD + else + mob_indicator = HOLOMAP_OTHER + + // It's not attached to anything useful + else + mob_indicator = HOLOMAP_DEAD + + // Ask it to update it's icon based on helmet (or whatever) + HC.update_marker() + + // Generate the icon and apply it to the list of images to show the client + if(mob_indicator != HOLOMAP_ERROR) + + // This is so specific because the icons are pixel_x and pixel_y only relative to OUR view of where THEY are relative to us + var/marker_cache_key = "\ref[HC]_[HC.marker_prefix]_[mob_indicator]" + + if(!(marker_cache_key in icon_image_cache)) + var/obj/screen/mapper/marker/mark = new() + mark.icon_state = "[HC.marker_prefix][mob_indicator]" + icon_image_cache[marker_cache_key] = mark + switch(mob_indicator) + if(HOLOMAP_YOU) + mark.layer = 3 // Above the other markers + if(HOLOMAP_DEAD) + mark.layer = 2 + else + mark.layer = 1 + + var/obj/screen/mapper/marker/mark = icon_image_cache[marker_cache_key] + handle_marker(mark,TU.x,TU.y) + extras += mark + + // Marker beacon items + for(var/hb in mapping_beacons) + var/obj/item/device/holomap_beacon/HB = hb + if(HB.mapper_filter != mapper_filter) + continue + + var/turf/TB = get_turf(HB) + // Marker beacon not on a turf or elsewhere + if(!TB || (TB.z != T_z)) + continue + + var/marker_cache_key = "\ref[HB]_marker" + if(!(marker_cache_key in icon_image_cache)) + var/obj/screen/mapper/marker/mark = new() + mark.icon_state = "beacon" + mark.layer = 1 + icon_image_cache[marker_cache_key] = mark + + var/obj/screen/mapper/marker/mark = icon_image_cache[marker_cache_key] + handle_marker(mark,TB.x,TB.y) + extras += mark + + if(badmap) + var/obj/O = icon_image_cache["bad"] + O.pixel_x = T_x - offset_x + O.pixel_y = T_y - offset_y + extras += O + + extras_holder.filters = filter(type = "alpha", icon = mask_icon, x = T_x-(offset_x*0.5), y = T_y-(offset_y*0.5)) + extras_holder.vis_contents = extras + + hud_item.update(bgmap, extras_holder, badmap ? FALSE : pinging) + +/obj/item/device/mapping_unit/proc/update_marker() + marker_prefix = base_prefix + if (prefix_update_head) + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + var/obj/item/helmet = H.get_equipped_item(slot_head) + if(helmet && ("[helmet.type]" in prefix_update_head)) + marker_prefix = prefix_update_head["[helmet.type]"] + return + if (prefix_update_rig) + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + var/obj/item/weapon/rig = H.get_rig() + if(rig && ("[rig.type]" in prefix_update_rig)) + marker_prefix = prefix_update_rig["[rig.type]"] + return + +/obj/item/device/mapping_unit/proc/handle_marker(var/atom/movable/marker,var/TU_x,var/TU_y) + marker.pixel_x = TU_x - 8 //16x16 icons, so to center. + marker.pixel_y = TU_y - 8 + animate(marker, alpha = 0, time = 5, easing = SINE_EASING) + animate(alpha = 255, time = 2, easing = SINE_EASING) + +/obj/item/device/holomap_beacon + name = "holomap beacon" + desc = "When active, the beacon will show itself on mapping units of the same type." + icon_state = "holochip" + w_class = ITEMSIZE_TINY + var/mapper_filter = HOLOMAP_FILTER_STATIONMAP + var/in_list = FALSE + +/obj/item/device/holomap_beacon/Initialize() + . = ..() + if(in_list) // mapped in turned on + in_list = TRUE + mapping_beacons += src + icon_state = initial(icon_state) + in_list ? "_on" : "" + +/obj/item/device/holomap_beacon/attack_self(mob/user) + if(!in_list) + in_list = TRUE + mapping_beacons += src + else + in_list = FALSE + mapping_beacons -= src + icon_state = "[initial(icon_state)][in_list ? "_on" : ""]" + to_chat(user,SPAN_NOTICE("The [src] is now [in_list ? "broadcasting" : "disabled"].")) + +/obj/item/device/holomap_beacon/Destroy() + if(in_list) + mapping_beacons -= src + return ..() + +/obj/item/device/holomap_beacon/deathsquad + name = "deathsquad holomap beacon" + icon_state = "holochip_ds" + mapper_filter = HOLOMAP_FILTER_DEATHSQUAD + +/obj/item/device/holomap_beacon/operative + name = "operative holomap beacon" + icon_state = "holochip_op" + mapper_filter = HOLOMAP_FILTER_NUKEOPS + +/obj/item/device/holomap_beacon/ert + name = "ert holomap beacon" + icon_state = "holochip_ert" + mapper_filter = HOLOMAP_FILTER_ERT + + +#undef HOLOMAP_ERROR +#undef HOLOMAP_YOU +#undef HOLOMAP_OTHER +#undef HOLOMAP_DEAD diff --git a/code/modules/holomap/station_holomap.dm b/code/modules/holomap/station_holomap.dm index 93cf76017d..dc15dabb46 100644 --- a/code/modules/holomap/station_holomap.dm +++ b/code/modules/holomap/station_holomap.dm @@ -240,9 +240,29 @@ origin_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 2) req_components = list() -// TODO -// //Portable holomaps, currently AI/Borg/MoMMI only +/datum/holomap_marker + var/x + var/y + var/z + var/offset_x = -8 + var/offset_y = -8 + var/filter + var/id // used for icon_state of the marker on maps + var/icon = 'icons/holomap_markers.dmi' + var/color //used by path rune markers -// TODO -// OHHHH YEAH - STRATEGIC HOLOMAP! NICE! -// It will need to wait until later tho. +/obj/effect/landmark/holomarker + delete_me = TRUE + + var/filter = HOLOMAP_FILTER_STATIONMAP + var/id = "generic" + +/obj/effect/landmark/holomarker/Initialize() + . = ..() + var/datum/holomap_marker/holomarker = new() + holomarker.id = id + holomarker.filter = filter + holomarker.x = src.x + holomarker.y = src.y + holomarker.z = src.z + holomap_markers["[id]_\ref[src]"] = holomarker diff --git a/icons/effects/64x64.dmi b/icons/effects/64x64.dmi index e4ae33567f..093da559ef 100644 Binary files a/icons/effects/64x64.dmi and b/icons/effects/64x64.dmi differ diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index f578e1f9ac..8c74dc49b5 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/effects/gpshud.dmi b/icons/effects/gpshud.dmi new file mode 100644 index 0000000000..dc8544ce26 Binary files /dev/null and b/icons/effects/gpshud.dmi differ diff --git a/icons/holomap_markers.dmi b/icons/holomap_markers.dmi index 3ea32c120d..9523510710 100644 Binary files a/icons/holomap_markers.dmi and b/icons/holomap_markers.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index dd15d13fb7..3ca9cde72b 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/maps/~map_system/maps.dm b/maps/~map_system/maps.dm index 0863992efe..e5a66d33ae 100644 --- a/maps/~map_system/maps.dm +++ b/maps/~map_system/maps.dm @@ -36,6 +36,7 @@ var/list/all_maps = list() var/static/list/persist_levels = list() // Z-levels where SSpersistence should persist between rounds. Defaults to station_levels if unset. var/static/list/secret_levels = list() // Z-levels that (non-admin) ghosts can't get to var/static/list/empty_levels = list() // Empty Z-levels that may be used for various things + var/static/list/mappable_levels = list()// List of levels where mapping or other similar devices might work fully // End Static Lists // Z-levels available to various consoles, such as the crew monitor. Defaults to station_levels if unset. @@ -122,6 +123,8 @@ var/list/all_maps = list() map_levels = station_levels.Copy() if(!persist_levels?.len) persist_levels = station_levels.Copy() + if(!mappable_levels?.len) + mappable_levels = station_levels.Copy() if(!allowed_jobs || !allowed_jobs.len) allowed_jobs = subtypesof(/datum/job) if(default_skybox) //Type was specified @@ -294,11 +297,13 @@ var/list/all_maps = list() map.base_turf_by_z["[z]"] = base_turf if(transit_chance) map.accessible_z_levels["[z]"] = transit_chance + if(flags & MAP_LEVEL_MAPPABLE) + map.mappable_levels |= z // Holomaps // Auto-center the map if needed (Guess based on maxx/maxy) if (holomap_offset_x < 0) holomap_offset_x = ((HOLOMAP_ICON_SIZE - world.maxx) / 2) - if (holomap_offset_x < 0) + if (holomap_offset_y < 0) holomap_offset_y = ((HOLOMAP_ICON_SIZE - world.maxy) / 2) // Assign them to the map lists LIST_NUMERIC_SET(map.holomap_offset_x, z, holomap_offset_x) diff --git a/polaris.dme b/polaris.dme index af509d7e96..ec2c67895e 100644 --- a/polaris.dme +++ b/polaris.dme @@ -146,11 +146,13 @@ #include "code\_onclick\hud\hud.dm" #include "code\_onclick\hud\human.dm" #include "code\_onclick\hud\map_popups.dm" +#include "code\_onclick\hud\minihud.dm" +#include "code\_onclick\hud\minihud_mapper.dm" +#include "code\_onclick\hud\minihud_rigmech.dm" #include "code\_onclick\hud\movable_screen_objects.dm" #include "code\_onclick\hud\picture_in_picture.dm" #include "code\_onclick\hud\radial.dm" #include "code\_onclick\hud\radial_persistent.dm" -#include "code\_onclick\hud\rigmech.dm" #include "code\_onclick\hud\robot.dm" #include "code\_onclick\hud\screen_objects.dm" #include "code\_onclick\hud\skybox.dm" @@ -1939,6 +1941,7 @@ #include "code\modules\holomap\generate_holomap.dm" #include "code\modules\holomap\holomap_area.dm" #include "code\modules\holomap\holomap_datum.dm" +#include "code\modules\holomap\mapper.dm" #include "code\modules\holomap\station_holomap.dm" #include "code\modules\hydroponics\_hydro_setup.dm" #include "code\modules\hydroponics\grown.dm"