From 87d25f99d5ff1d3cf8b221e3ab9de6195b1d40a2 Mon Sep 17 00:00:00 2001 From: Daggertail Date: Mon, 21 Feb 2022 04:29:11 -0600 Subject: [PATCH 01/22] Python BS + Mapmerge changes --- dependencies.sh | 2 +- tools/bootstrap/python.bat | 3 ++- tools/bootstrap/{python36._pth => python37._pth} | 2 +- tools/bootstrap/python_.ps1 | 2 +- tools/mapmerge2/convert.py | 2 +- tools/mapmerge2/dmm.py | 2 +- tools/mapmerge2/fixup.py | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) rename tools/bootstrap/{python36._pth => python37._pth} (84%) diff --git a/dependencies.sh b/dependencies.sh index 4474cc8736..0a27dd623f 100644 --- a/dependencies.sh +++ b/dependencies.sh @@ -18,7 +18,7 @@ export NODE_VERSION_PRECISE=12.22.4 export SPACEMAN_DMM_VERSION=suite-1.7 # Python version for mapmerge and other tools -export PYTHON_VERSION=3.6.8 +export PYTHON_VERSION=3.7.9 # Auxmos git tag export AUXMOS_VERSION=v0.3.0 diff --git a/tools/bootstrap/python.bat b/tools/bootstrap/python.bat index 3b20c2d436..5bec61b84c 100644 --- a/tools/bootstrap/python.bat +++ b/tools/bootstrap/python.bat @@ -1 +1,2 @@ -@call powershell.exe -NoLogo -ExecutionPolicy Bypass -File "%~dp0\python_.ps1" %* +@echo off +call powershell.exe -NoLogo -ExecutionPolicy Bypass -File "%~dp0\python_.ps1" %* diff --git a/tools/bootstrap/python36._pth b/tools/bootstrap/python37._pth similarity index 84% rename from tools/bootstrap/python36._pth rename to tools/bootstrap/python37._pth index 2bd2e8a7d6..4fe5437261 100644 --- a/tools/bootstrap/python36._pth +++ b/tools/bootstrap/python37._pth @@ -1,4 +1,4 @@ -python36.zip +python37.zip . ..\..\.. diff --git a/tools/bootstrap/python_.ps1 b/tools/bootstrap/python_.ps1 index c9e3915613..93a6f75696 100644 --- a/tools/bootstrap/python_.ps1 +++ b/tools/bootstrap/python_.ps1 @@ -50,7 +50,7 @@ if (!(Test-Path $PythonExe -PathType Leaf)) { [System.IO.Compression.ZipFile]::ExtractToDirectory($Archive, $PythonDir) # Copy a ._pth file without "import site" commented, so pip will work - Copy-Item "$Bootstrap/python36._pth" $PythonDir ` + Copy-Item "$Bootstrap/python37._pth" $PythonDir ` -ErrorAction Stop Remove-Item $Archive diff --git a/tools/mapmerge2/convert.py b/tools/mapmerge2/convert.py index 35fa606834..63fe8cbb9a 100755 --- a/tools/mapmerge2/convert.py +++ b/tools/mapmerge2/convert.py @@ -4,4 +4,4 @@ from . import frontend, dmm if __name__ == '__main__': settings = frontend.read_settings() for fname in frontend.process(settings, "convert"): - dmm.DMM.from_file(fname).to_file(fname, settings.tgm) + dmm.DMM.from_file(fname).to_file(fname, tgm = settings.tgm) diff --git a/tools/mapmerge2/dmm.py b/tools/mapmerge2/dmm.py index 2d87cfc172..453cdf5c8a 100755 --- a/tools/mapmerge2/dmm.py +++ b/tools/mapmerge2/dmm.py @@ -58,7 +58,7 @@ class DMM: self.grid[coord] = self.get_or_generate_key(tile) def generate_new_key(self): - free_keys = self._ensure_free_keys(1) + self._ensure_free_keys(1) max_key = max_key_for(self.key_length) # choose one of the free keys at random key = random.randint(0, max_key - 1) diff --git a/tools/mapmerge2/fixup.py b/tools/mapmerge2/fixup.py index f3133d1d73..6953e7ab77 100644 --- a/tools/mapmerge2/fixup.py +++ b/tools/mapmerge2/fixup.py @@ -96,7 +96,7 @@ def main(repo): converted[path] = merge_map(head_files[path], dmm.DMM.from_bytes(data)) if len(working_commit.parents) != 1: print("A merge commit was encountered before good versions of these maps were found:") - print("\n".join(f" {x}" for x in head_files.keys() - base_files.keys())) + print("\n".join(f" {x}" for x in head_files.keys() - converted.keys())) return 1 working_commit = working_commit.parents[0] From b7035f563f67fd7d2d58699c2cb1e5f507eee8ef Mon Sep 17 00:00:00 2001 From: Changelogs Date: Sat, 26 Feb 2022 00:53:45 +0000 Subject: [PATCH 02/22] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-15523.yml | 4 ---- html/changelogs/archive/2022-02.yml | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-15523.yml diff --git a/html/changelogs/AutoChangeLog-pr-15523.yml b/html/changelogs/AutoChangeLog-pr-15523.yml deleted file mode 100644 index 48767debcb..0000000000 --- a/html/changelogs/AutoChangeLog-pr-15523.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Putnam3145" -delete-after: True -changes: - - balance: "Now slightly easier to get 50,000 points with toxins (50,000 point baseline changed from 200 light range to 175)" diff --git a/html/changelogs/archive/2022-02.yml b/html/changelogs/archive/2022-02.yml index 0715711ef7..d46305bd06 100644 --- a/html/changelogs/archive/2022-02.yml +++ b/html/changelogs/archive/2022-02.yml @@ -154,3 +154,7 @@ now' timothyteakettle: - rscadd: adds snake coiling +2022-02-26: + Putnam3145: + - balance: Now slightly easier to get 50,000 points with toxins (50,000 point baseline + changed from 200 light range to 175) From 45a49c6106374bae5376cecddd7993a3c2719073 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Sat, 5 Mar 2022 13:49:26 -0300 Subject: [PATCH 03/22] Upload files --- code/__DEFINES/_flags/_flags.dm | 2 ++ code/_globalvars/bitfields.dm | 1 + code/_onclick/hud/hud.dm | 20 +++++++++++++++ code/_onclick/hud/screentip.dm | 19 ++++++++++++++ code/game/atoms.dm | 15 +++++------ code/game/turfs/simulated/floor.dm | 1 + code/modules/client/client_procs.dm | 1 + code/modules/client/preferences.dm | 10 ++++++++ code/modules/client/preferences_savefile.dm | 4 +++ interface/skin.dmf | 28 +++++++++++++++------ tgstation.dme | 1 + 11 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 code/_onclick/hud/screentip.dm diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm index 5f591b429b..a73841eff5 100644 --- a/code/__DEFINES/_flags/_flags.dm +++ b/code/__DEFINES/_flags/_flags.dm @@ -37,6 +37,8 @@ GLOBAL_LIST_INIT(bitflags, list( #define OVERLAY_QUEUED_1 (1<<8) ///Item has priority to check when entering or leaving. #define ON_BORDER_1 (1<<9) +///Whether or not this atom shows screentips when hovered over +#define NO_SCREENTIPS_1 (1<<10) ///Prevent clicking things below it on the same turf eg. doors/ fulltile windows. #define PREVENT_CLICK_UNDER_1 (1<<11) #define HOLOGRAM_1 (1<<12) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index df3b6e5334..2dc77d3bcb 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -160,6 +160,7 @@ GLOBAL_LIST_INIT(bitfields, list( "HOLOGRAM_1" = HOLOGRAM_1, "SHOCKED_1" = SHOCKED_1, "INITIALIZED_1" = INITIALIZED_1, + "NO_SCREENTIPS_1" = NO_SCREENTIPS_1, "ADMIN_SPAWNED_1" = ADMIN_SPAWNED_1, "BLOCK_FACE_ATOM_1" = BLOCK_FACE_ATOM_1, "PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1 diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index a5fe8f4ba8..fbd62f0565 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -53,6 +53,22 @@ GLOBAL_LIST_INIT(available_ui_styles, list( var/list/hand_slots // /atom/movable/screen/inventory/hand objects, assoc list of "[held_index]" = object var/list/atom/movable/screen/plane_master/plane_masters = list() // see "appearance_flags" in the ref, assoc list of "[plane]" = object + + ///UI for screentips that appear when you mouse over things + var/atom/movable/screen/screentip/screentip_text + + /// Whether or not screentips are enabled. + /// This is updated by the preference for cheaper reads than would be + /// had with a proc call, especially on one of the hottest procs in the + /// game (MouseEntered). + var/screentips_enabled = TRUE + + /// The color to use for the screentips. + /// This is updated by the preference for cheaper reads than would be + /// had with a proc call, especially on one of the hottest procs in the + /// game (MouseEntered). + var/screentip_color + var/atom/movable/screen/movable/action_button/hide_toggle/hide_actions_toggle var/action_buttons_hidden = FALSE @@ -83,6 +99,8 @@ GLOBAL_LIST_INIT(available_ui_styles, list( plane_masters["[instance.plane]"] = instance instance.backdrop(mymob) + screentip_text = new(null, src) + static_inventory += screentip_text /datum/hud/Destroy() if(mymob.hud_used == src) @@ -117,6 +135,8 @@ GLOBAL_LIST_INIT(available_ui_styles, list( QDEL_LIST(screenoverlays) mymob = null + QDEL_NULL(screentip_text) + return ..() diff --git a/code/_onclick/hud/screentip.dm b/code/_onclick/hud/screentip.dm new file mode 100644 index 0000000000..43b728ade6 --- /dev/null +++ b/code/_onclick/hud/screentip.dm @@ -0,0 +1,19 @@ +/atom/movable/screen/screentip + icon = null + icon_state = null + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + screen_loc = "TOP,LEFT" + maptext_height = 480 + maptext_width = 480 + maptext = "" + +/atom/movable/screen/screentip/Initialize(mapload, _hud) + . = ..() + hud = _hud + update_view() + +/atom/movable/screen/screentip/proc/update_view(datum/source) + SIGNAL_HANDLER + if(!hud || !hud.mymob.client.view_size) //Might not have been initialized by now + return + maptext_width = getviewsize(hud.mymob.client.view_size.getView())[1] * world.icon_size diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 40162a6c9b..925ac41a0b 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1406,11 +1406,12 @@ //Update the screentip to reflect what we're hoverin over /atom/MouseEntered(location, control, params) . = ..() - // Statusbar - // status_bar_set_text(usr, name) // Screentips - // if(usr?.hud_used) - // if(!usr.client?.prefs.screentip_pref || (flags_1 & NO_SCREENTIPS_1)) - // usr.hud_used.screentip_text.maptext = "" - // else - // usr.hud_used.screentip_text.maptext = MAPTEXT("[name]") + var/client/client = usr?.client + var/datum/hud/active_hud = client?.mob?.hud_used + if(active_hud) + if(!client.prefs.screentip_pref || (flags_1 & NO_SCREENTIPS_1)) + active_hud.screentip_text.maptext = "" + else + //We inline a MAPTEXT() here, because there's no good way to statically add to a string like this + active_hud.screentip_text.maptext = MAPTEXT("[name]") diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index 9b6767b874..5e2b5284e1 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -11,6 +11,7 @@ barefootstep = FOOTSTEP_HARD_BAREFOOT clawfootstep = FOOTSTEP_HARD_CLAW heavyfootstep = FOOTSTEP_GENERIC_HEAVY + flags_1 = NO_SCREENTIPS_1 /// Minimum explosion power to break tile var/explosion_power_break_tile = EXPLOSION_POWER_FLOOR_TILE_BREAK diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 9948802bc9..a1f9aacf18 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -1012,6 +1012,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( var/list/actualview = getviewsize(view) update_clickcatcher() parallax_holder.Reset() + mob.hud_used.screentip_text.update_view() mob.reload_fullscreen() if (isliving(mob)) var/mob/living/M = mob diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 3f31540fe2..5fcb6787d2 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -62,6 +62,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/UI_style = null var/outline_enabled = TRUE var/outline_color = COLOR_THEME_MIDNIGHT + var/screentip_pref = TRUE + var/screentip_color = "#ffd391" var/buttons_locked = FALSE var/hotkeys = FALSE @@ -788,6 +790,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "UI Style: [UI_style]
" dat += "Outline: [outline_enabled ? "Enabled" : "Disabled"]
" dat += "Outline Color: [outline_color ? "" : "Theme-based (null)"]    Change
" + dat += "Screentip: [screentip_pref ? "Enabled" : "Disabled"]
" + dat += "Screentip Color:     Change
" dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"]
" dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
" dat += "Show Runechat Chat Bubbles: [chat_on_map ? "Enabled" : "Disabled"]
" @@ -2748,6 +2752,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/pickedOutlineColor = input(user, "Choose your outline color.", "General Preference", outline_color) as color|null if(pickedOutlineColor != pickedOutlineColor) outline_color = pickedOutlineColor // nullable + if("screentip_pref") + screentip_pref = !screentip_pref + if("screentip_color") + var/pickedScreentipColor = input(user, "Choose your screentip color.", "General Preference", screentip_color) as color|null + if(pickedScreentipColor) + screentip_color = pickedScreentipColor if("tgui_lock") tgui_lock = !tgui_lock if("winflash") diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 72b2f5d949..628a51ed44 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -385,6 +385,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["UI_style"] >> UI_style S["outline_color"] >> outline_color S["outline_enabled"] >> outline_enabled + S["screentip_pref"] >> screentip_pref + S["screentip_color"] >> screentip_color S["hotkeys"] >> hotkeys S["chat_on_map"] >> chat_on_map S["max_chat_length"] >> max_chat_length @@ -567,6 +569,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["UI_style"], UI_style) WRITE_FILE(S["outline_enabled"], outline_enabled) WRITE_FILE(S["outline_color"], outline_color) + WRITE_FILE(S["screentip_pref"], screentip_pref) + WRITE_FILE(S["screentip_color"], screentip_color) WRITE_FILE(S["hotkeys"], hotkeys) WRITE_FILE(S["chat_on_map"], chat_on_map) WRITE_FILE(S["max_chat_length"], max_chat_length) diff --git a/interface/skin.dmf b/interface/skin.dmf index ccfcd76571..82a35160e6 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -2,21 +2,21 @@ macro "default" menu "menu" - elem + elem name = "&File" command = "" saved-params = "is-checked" - elem + elem name = "&Quick screenshot\tF2" command = ".screenshot auto" category = "&File" saved-params = "is-checked" - elem + elem name = "&Save screenshot as...\tShift+F2" command = ".screenshot" category = "&File" saved-params = "is-checked" - elem + elem name = "" command = "" category = "&File" @@ -26,21 +26,21 @@ menu "menu" command = ".reconnect" category = "&File" saved-params = "is-checked" - elem + elem name = "&Quit\tAlt-F4" command = ".quit" category = "&File" saved-params = "is-checked" - elem + elem name = "&Help" command = "" saved-params = "is-checked" - elem + elem name = "&Admin Help\tF1" command = "adminhelp" category = "&Help" saved-params = "is-checked" - elem + elem name = "&Hotkeys" command = "hotkeys-help" category = "&Help" @@ -56,6 +56,7 @@ window "mainwindow" anchor2 = none is-default = true saved-params = "pos;size;is-minimized;is-maximized" + statusbar = false icon = 'icons\\ss13_64.png' macro = "default" menu = "menu" @@ -95,6 +96,7 @@ window "mapwindow" anchor2 = none saved-params = "pos;size;is-minimized;is-maximized" is-pane = true + on-status = ".winset \"status_bar.text=[[*]]\" " elem "map" type = MAP pos = 0,0 @@ -107,6 +109,16 @@ window "mapwindow" is-default = true saved-params = "zoom;letterbox;zoom-mode" style = ".center { text-align: center; } .maptext { font-family: 'Small Fonts'; font-size: 7px; -dm-text-outline: 1px black; color: white; line-height: 1.1; } .command_headset { font-weight: bold;\tfont-size: 8px; } .small { font-size: 6px; } .big { font-size: 8px; } .reallybig { font-size: 8px; } .extremelybig { font-size: 8px; } .greentext { color: #00FF00; font-size: 7px; } .redtext { color: #FF0000; font-size: 7px; } .clown { color: #FF69Bf; font-size: 7px; font-weight: bold; } .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; } .italics { font-size: 6px; }" + elem "status_bar" + type = LABEL + pos = 0,1008 + size = 280x16 + anchor1 = 0,100 + text = "" + align = left + background-color = #222222 + text-color = #ffffff + border = line window "infowindow" elem "infowindow" diff --git a/tgstation.dme b/tgstation.dme index 8c7f064d60..eafcf93610 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -288,6 +288,7 @@ #include "code\_onclick\hud\revenanthud.dm" #include "code\_onclick\hud\robot.dm" #include "code\_onclick\hud\screen_objects.dm" +#include "code\_onclick\hud\screentip.dm" #include "code\_onclick\hud\simple_animal.dm" #include "code\_onclick\hud\swarmer.dm" #include "code\_onclick\hud\screen_objects\clickdelay.dm" From 5f083fbdddf8472ab23bab207663a4d5f3577149 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Sat, 5 Mar 2022 14:22:42 -0300 Subject: [PATCH 04/22] Upload files --- code/modules/client/client_procs.dm | 10 +- html/statbrowser.html | 428 +++++++++++++++++++++------- 2 files changed, 330 insertions(+), 108 deletions(-) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 9948802bc9..3d17ea5992 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -154,7 +154,15 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( var/atom/target = locate(href_list["statpanel_item_target"]) if(!target) return - Click(target, target.loc, null, "[href_list["statpanel_item_shiftclick"]?"shift=1;":null][href_list["statpanel_item_ctrlclick"]?"ctrl=1;":null]&alt=[href_list["statpanel_item_altclick"]?"alt=1;":null]", FALSE, "statpanel") + var/button = "left=1" + switch(href_list["statpanel_item_click"]) + if("middle") + button = "middle=1" + if("right") + button = "right=1" + else + button = "left=1" + Click(target, target.loc, null, "[button];[href_list["statpanel_item_shiftclick"]?"shift=1;":null][href_list["statpanel_item_ctrlclick"]?"ctrl=1;":null]&alt=[href_list["statpanel_item_altclick"]?"alt=1;":null]", FALSE, "statpanel") /client/proc/is_content_unlocked() if(!prefs.unlock_content) diff --git a/html/statbrowser.html b/html/statbrowser.html index 1cb089a189..ecb47c419c 100644 --- a/html/statbrowser.html +++ b/html/statbrowser.html @@ -12,16 +12,27 @@ font-size: 12px !important; margin: 0 !important; padding: 0 !important; + overflow-x: hidden; + overflow-y: scroll; } + body.dark { background-color: #131313; - color: #abc6ec; + color: #b2c4dd; + scrollbar-base-color: #1c1c1c; + scrollbar-face-color: #3b3b3b; + scrollbar-3dlight-color: #252525; + scrollbar-highlight-color: #252525; + scrollbar-track-color: #1c1c1c; + scrollbar-arrow-color: #929292; + scrollbar-shadow-color: #3b3b3b; } #menu { background-color: #F0F0F0; position: fixed; width: 100%; + z-index: 100; } .dark #menu { @@ -38,7 +49,7 @@ } .dark a { - color: #abc6ec; + color: #b2c4dd; } a:hover, .dark a:hover { @@ -80,73 +91,115 @@ .button { background-color: #dfdfdf; - border-color: #cecece; - border-width: 1px; - border-style: solid; + border: 1px solid #cecece; + border-bottom-width: 2px; color: rgba(0, 0, 0, 0.7); - padding: 6px 4px; + padding: 6px 4px 4px; text-align: center; text-decoration: none; font-size: 12px; margin: 0; cursor: pointer; - transition-duration: 0.25s; + transition-duration: 100ms; order: 3; min-width: 40px; } .dark button { - background-color: #444444; + background-color: #222222; border-color: #343434; - color: rgba(255, 255, 255, 0.7); + color: rgba(255, 255, 255, 0.5); } .button:hover { background-color: #ececec; + transition-duration: 0; } .dark button:hover { - background-color: #4d4d4d; + background-color: #2e2e2e; } .button:active, .button.active { background-color: #ffffff; color: black; - border-top: 1px solid #cecece; - border-left: 1px solid #cecece; - border-right: 1px solid #cecece; - border-bottom: 1px solid #ffffff; + border-top-color: #cecece; + border-left-color: #cecece; + border-right-color: #cecece; + border-bottom-color: #ffffff; } .dark .button:active, .dark .button.active { - background-color: #131313; + background-color: #444444; color: white; - border-top: 1px solid #343434; - border-left: 1px solid #343434; - border-right: 1px solid #343434; - border-bottom: 1px solid #131313; + border-top-color: #343434; + border-left-color: #343434; + border-right-color: #343434; + border-bottom-color: #ffffff; } .grid-container { - display: inline-flex; - flex-wrap: wrap; - justify-content: flex-start; - align-items: flex-start; - min-width: 0; - min-height: 0; - white-space: pre-wrap; + margin: -2px; + margin-right: -15px; } .grid-item { - color: black; - width: 150px; - font-size: 11px; - line-height: 24px; - text-align: left; - min-width: 0; - min-height: 0; - white-space: pre-wrap; - padding-right: 12px; /* A little more than two spaces, to look good in IE8 where flex-justify does nothing */ + position: relative; + display: inline-block; + width: 100%; + box-sizing: border-box; + overflow: visible; + padding: 3px 2px; + text-decoration: none; + } + + @media only screen and (min-width: 300px) { + .grid-item { + width: 50%; + } + } + + @media only screen and (min-width: 430px) { + .grid-item { + width: 33%; + } + } + + @media only screen and (min-width: 560px) { + .grid-item { + width: 25%; + } + } + + @media only screen and (min-width: 770px) { + .grid-item { + width: 20%; + } + } + + .grid-item:hover { + z-index: 1; + } + + .grid-item:hover .grid-item-text { + width: auto; + text-decoration: underline; + } + + .grid-item-text { + display: inline-block; + width: 100%; + background-color: #ffffff; + margin: 0 -6px; + padding: 0 6px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + pointer-events: none; + } + + .dark .grid-item-text { + background-color: #131313; } .link { @@ -173,6 +226,10 @@ -ms-interpolation-mode: nearest-neighbor; image-rendering: pixelated; } + + .interview_panel_controls, .interview_panel_stats { + margin-bottom: 10px; + } @@ -242,53 +299,6 @@ if (!String.prototype.trim) { }, 0, 'test'); }()) -// Browser passthrough code --------------------------------------------------- -if (window.location) { - var anti_spam = []; // wow I wish I could use e.repeat but IE is dumb and doesn't have it. - document[addEventListenerKey]("keydown", function(e) { - if(e.target && (e.target.localName == "input" || e.target.localName == "textarea")) - return; - if(e.defaultPrevented) - return; // do e.preventDefault() to prevent this behavior. - if(e.which) { - if(!anti_spam[e.which]) { - anti_spam[e.which] = true; - var key = String.fromCharCode(e.which); - var x = event.x || event.clientX; - var y = event.y || event.clientY; - if(x || y ) // if either of these exist this is happening after a click - return; - window.location.href = "byond://winset?command=keyDown " + e.key; - } - } - }); - document[addEventListenerKey]("keyup", function(e) { - if(e.target && (e.target.localName == "input" || e.target.localName == "textarea")) - return; - if(e.defaultPrevented) - return; - if(e.which) { - anti_spam[e.which] = false; - var key = String.fromCharCode(e.which); - var x = event.x || event.clientX; - var y = event.y || event.clientY; - if( x || y ) // if either of these exist this is happening after a click - return; - - window.location.href = "byond://winset?command=keyUp " + e.key; - } - }); -} -/* document.addEventListener("mousedown", function(e){ - var shiftPressed=0; - var evt = e?e:window.event; - shiftPressed=evt.shiftKey; - if (shiftPressed) { - return false; - } - return true; -}); */ - // Status panel implementation ------------------------------------------------ var status_tab_parts = ["Loading..."]; var current_tab = null; @@ -300,20 +310,80 @@ var spell_tabs = []; var verb_tabs = []; var verbs = [["", ""]]; // list with a list inside var tickets = []; +var interviewManager = {status: "", interviews: []}; var sdql2 = []; var permanent_tabs = []; // tabs that won't be cleared by wipes var turfcontents = []; var turfname = ""; var imageRetryDelay = 500; var imageRetryLimit = 50; -var menu = document.querySelector('#menu'); -var under_menu = document.querySelector('#under_menu'); -var statcontentdiv = document.querySelector('#statcontent'); +var menu = document.getElementById('menu'); +var under_menu = document.getElementById('under_menu'); +var statcontentdiv = document.getElementById('statcontent'); var storedimages = []; +var split_admin_tabs = false; + +var connected = false; +var commandQueue = []; + +// Any BYOND verb call must go through this, as if a verb is sent during reconnect then +// it will cause the reconnect to fail. +// This function will either call immediately, or queue until +// BYOND confirms we are connected. +function send_byond_command(command) { + var href = "byond://winset?command=" + command; + + if (connected) { + window.location.href = href; + } else { + commandQueue.push(href); + } +} + +// Any BYOND commands that could result in the client's focus changing go through this +// to ensure that when we relinquish our focus, we don't do it after the result of +// a command has already taken focus for itself. +function run_after_focus(callback) { + setTimeout(callback, 0); +} + +function connected_to_server() { + if (connected) { + return; + } + + connected = true; + + for (var index = 0; index < commandQueue.length; index++) { + // This is just setting it a lot, is this not going to cancel? + window.location.href = commandQueue[index]; + } + + commandQueue = []; +} + +function update_split_admin_tabs(status) { + status = (status == true); + + if (split_admin_tabs !== status) { + if (split_admin_tabs === true) { + removeStatusTab("Events"); + removeStatusTab("Fun"); + removeStatusTab("Game"); + } + update_verbs(); + } + split_admin_tabs = status; +} function createStatusTab(name) { - if (name.indexOf(".") != -1) - name = name.split(".")[0]; + if (name.indexOf(".") != -1) { + var splitName = name.split("."); + if (split_admin_tabs && splitName[0] === "Admin") + name = splitName[1]; + else + name = splitName[0]; + } if(document.getElementById(name) || name.trim() == "") return; if(!verb_tabs.includes(name) && !permanent_tabs.includes(name)) @@ -386,6 +456,7 @@ function checkStatusTab() { if(!verb_tabs.includes(menu.children[i].id) && !permanent_tabs.includes(menu.children[i].id)) menu.removeChild(menu.children[i]); } + function remove_verb(v) { var verb_to_remove = v; // to_remove = [verb:category, verb:name] for(var i = verbs.length - 1; i >= 0; i--){ @@ -403,7 +474,14 @@ function check_verbs() { } function verbs_cat_check(cat) { - var tabCat = cat.indexOf(".") != -1 ? cat.split(".")[0] : cat; + var tabCat = cat; + if (cat.indexOf(".") != -1) { + var splitName = cat.split("."); + if (split_admin_tabs && splitName[0] === "Admin") + tabCat = splitName[1]; + else + tabCat = splitName[0]; + } var verbs_in_cat = 0; var verbcat = ""; if(!verb_tabs.includes(tabCat)){ @@ -412,7 +490,14 @@ function verbs_cat_check(cat) { } for(var v = 0; v < verbs.length; v++){ var part = verbs[v]; - verbcat = part[0].indexOf(".") != -1 ? part[0].split(".")[0] : part[0]; + verbcat = part[0]; + if (verbcat.indexOf(".") != -1) { + var splitName = verbcat.split("."); + if (split_admin_tabs && splitName[0] === "Admin") + verbcat = splitName[1]; + else + verbcat = splitName[0]; + } if(verbcat != tabCat || verbcat.trim() == ""){ continue; } @@ -441,6 +526,11 @@ function wipe_verbs() { checkStatusTab(); // remove all empty verb tabs } +function update_verbs() { + wipe_verbs(); + send_byond_command("Update-Verbs"); +} + function add_verb_list(v) { var to_add = JSON.parse(v); // list of a list with category and verb inside it to_add.sort(); // sort what we're adding @@ -448,7 +538,14 @@ function add_verb_list(v) { var part = to_add[i]; if(!part[0]) continue; - var category = part[0].indexOf(".") == -1 ? part[0] : part[0].split(".")[0]; + var category = part[0]; + if (category.indexOf(".") != -1) { + var splitName = category.split("."); + if (split_admin_tabs && splitName[0] === "Admin") + category = splitName[1]; + else + category = splitName[0]; + } if(findVerbindex(part[1], verbs)) continue; if(verb_tabs.includes(category)){ @@ -478,6 +575,7 @@ function remove_verb_list(v) { // passes a 2D list of (verbcategory, verbname) creates tabs and adds verbs to respective list // example (IC, Say) function init_verbs(c, v) { + connected_to_server(); wipe_verbs(); // remove all verb categories so we can replace them checkStatusTab(); // remove all status tabs verb_tabs = JSON.parse(c); @@ -510,12 +608,12 @@ function SendTabsToByond(){ } function SendTabToByond(tab) { - window.location.href = "byond://winset?command=Send-Tabs " + tab; + send_byond_command("Send-Tabs " + tab); } //Byond can't have this tab anymore since we're removing it function TakeTabFromByond(tab) { - window.location.href = "byond://winset?command=Remove-Tabs " + tab; + send_byond_command("Remove-Tabs " + tab); } function update(global_data, ping_entry, other_entries) { @@ -625,6 +723,7 @@ function tab_change(tab) { draw_debug(); } else if(tab == "Tickets") { draw_tickets(); +// draw_interviews(); } else if(tab == "SDQL2") { draw_sdql2(); }else if(tab == turfname) { @@ -636,8 +735,9 @@ function tab_change(tab) { } function set_byond_tab(tab){ - window.location.href = "byond://winset?command=Set-Tab " + tab; + send_byond_command("Set-Tab " + tab); } + function draw_debug() { statcontentdiv[textContentKey] = ""; var wipeverbstabs = document.createElement("div"); @@ -646,12 +746,26 @@ function draw_debug() { link[textContentKey] = "Wipe All Verbs"; wipeverbstabs.appendChild(link); document.getElementById("statcontent").appendChild(wipeverbstabs); + var wipeUpdateVerbsTabs = document.createElement("div"); + var updateLink = document.createElement("a"); + updateLink.onclick = function() {update_verbs()}; + updateLink[textContentKey] = "Wipe and Update All Verbs"; + wipeUpdateVerbsTabs.appendChild(updateLink); + document.getElementById("statcontent").appendChild(wipeUpdateVerbsTabs); var text = document.createElement("div"); text[textContentKey] = "Verb Tabs:"; document.getElementById("statcontent").appendChild(text); var table1 = document.createElement("table"); for(var i=0; i < verb_tabs.length ; i++) { var part = verb_tabs[i]; + // Hide subgroups except admin subgroups if they are split + if (verb_tabs[i].lastIndexOf(".") != -1) { + var splitName = verb_tabs[i].split("."); + if (split_admin_tabs && splitName[0] === "Admin") + part = splitName[1]; + else + continue; + } var tr = document.createElement("tr"); var td1 = document.createElement("td"); td1[textContentKey] = part; @@ -740,7 +854,7 @@ function draw_status() { } if(verb_tabs.length == 0 || !verbs) { - window.location.href = "byond://winset?command=Fix-Stat-Panel"; + send_byond_command("Fix-Stat-Panel"); } } @@ -776,6 +890,11 @@ function update_tickets(T){ if(current_tab == "Tickets") draw_tickets(); } +function update_interviews(I){ + interviewManager = JSON.parse(I); + if(current_tab == "Tickets") + draw_interviews(); +} function update_sdql2(S) { sdql2 = JSON.parse(S); if(sdql2.length > 0 && !verb_tabs.includes("SDQL2")) { @@ -805,12 +924,21 @@ function remove_tickets() { } checkStatusTab(); } + +function remove_interviews() { + if(tickets) { + tickets = []; + } + checkStatusTab(); +} + // removes MC, Tickets and MC tabs. function remove_admin_tabs() { href_token = null; remove_mc(); remove_tickets(); remove_sdql2(); +// remove_interviews(); } function add_admin_tabs(ht) { @@ -875,7 +1003,17 @@ function draw_listedturf() { // rather than every onmousedown getting the "part" of the last entry. return function(e) { e.preventDefault(); - clickcatcher = "?src=_statpanel_;statpanel_item_target=" + part[1] + ";statpanel_item_click=1"; + clickcatcher = "?src=_statpanel_;statpanel_item_target=" + part[1]; + switch(e.button){ + case 1: + clickcatcher += ";statpanel_item_click=middle" + break; + case 2: + clickcatcher += ";statpanel_item_click=right" + break; + default: + clickcatcher += ";statpanel_item_click=left" + } if(e.shiftKey){ clickcatcher += ";statpanel_item_shiftclick=1"; } @@ -912,7 +1050,7 @@ function draw_sdql2(){ var td2 = document.createElement("td"); if(part[2]) { var a = document.createElement("a"); - a.href = "?src=" + "_statpanel_" + ";statpanel_item_target=" + part[2] + ";statpanel_item_click=1"; + a.href = "?src=" + "_statpanel_" + ";statpanel_item_target=" + part[2] + ";statpanel_item_click=left"; a[textContentKey] = part[1]; td2.appendChild(a); } else { @@ -938,12 +1076,12 @@ function draw_tickets() { var td2 = document.createElement("td"); if(part[2]) { var a = document.createElement("a"); - a.href = "?_src_=holder;admin_token=" + href_token + ";ahelp=" + part[2] + ";ahelp_action=ticket;statpanel_item_click=1;action=ticket" ; + a.href = "?_src_=holder;admin_token=" + href_token + ";ahelp=" + part[2] + ";ahelp_action=ticket;statpanel_item_click=left;action=ticket" ; a[textContentKey] = part[1]; td2.appendChild(a); } else if(part[3]){ var a = document.createElement("a"); - a.href = "?src=_statpanel_" + ";statpanel_item_target=" + part[3] + ";statpanel_item_click=1"; + a.href = "?src=_statpanel_" + ";statpanel_item_target=" + part[3] + ";statpanel_item_click=left"; a[textContentKey] = part[1]; td2.appendChild(a); } else { @@ -956,6 +1094,55 @@ function draw_tickets() { document.getElementById("statcontent").appendChild(table); } +function draw_interviews() { + var body = document.createElement("div"); + var header = document.createElement("h3"); + header[textContentKey] = "Interviews"; + body.appendChild(header); + var manDiv = document.createElement("div"); + manDiv.className = "interview_panel_controls" + var manLink = document.createElement("a"); + manLink[textContentKey] = "Open Interview Manager Panel"; + manLink.href = "?_src_=holder;admin_token=" + href_token + ";interview_man=1;statpanel_item_click=left"; + manDiv.appendChild(manLink); + body.appendChild(manDiv); + + // List interview stats + var statsDiv = document.createElement("table"); + statsDiv.className="interview_panel_stats"; + for (var key in interviewManager.status) { + var d = document.createElement("div"); + var tr = document.createElement("tr"); + var stat_name = document.createElement("td"); + var stat_text = document.createElement("td"); + stat_name[textContentKey] = key; + stat_text[textContentKey] = interviewManager.status[key]; + tr.appendChild(stat_name); + tr.appendChild(stat_text); + statsDiv.appendChild(tr); + } + body.appendChild(statsDiv); + document.getElementById("statcontent").appendChild(body); + + // List interviews if any are open + var table = document.createElement("table"); + table.className = "interview_panel_table"; + if(!interviewManager) + return; + for(var i = 0; i < interviewManager.interviews.length; i++) { + var part = interviewManager.interviews[i]; + var tr = document.createElement("tr"); + var td = document.createElement("td"); + var a = document.createElement("a"); + a[textContentKey] = part["status"]; + a.href = "?_src_=holder;admin_token=" + href_token + ";interview=" + part["ref"] + ";statpanel_item_click=left"; + td.appendChild(a); + tr.appendChild(td); + table.appendChild(tr); + } + document.getElementById("statcontent").appendChild(table); +} + function draw_spells(cat) { statcontentdiv[textContentKey] = ""; var table = document.createElement("table"); @@ -968,7 +1155,7 @@ function draw_spells(cat) { var td2 = document.createElement("td"); if(part[3]) { var a = document.createElement("a"); - a.href = "?src=" + part[3] + ";statpanel_item_click=1"; + a.href = "?src=" + part[3] + ";statpanel_item_click=left"; a[textContentKey] = part[2]; td2.appendChild(a); } else { @@ -981,16 +1168,34 @@ function draw_spells(cat) { document.getElementById("statcontent").appendChild(table); } +function make_verb_onclick(command) { + return function() { + run_after_focus(function() { + send_byond_command(command); + }); + }; +} + function draw_verbs(cat){ statcontentdiv[textContentKey] = ""; var table = document.createElement("div"); var additions = {}; // additional sub-categories to be rendered table.className = "grid-container"; sortVerbs(); + if (split_admin_tabs && cat.lastIndexOf(".") != -1) { + var splitName = cat.split("."); + if (splitName[0] === "Admin") + cat = splitName[1]; + } verbs.reverse(); // sort verbs backwards before we draw for (var i = 0; i < verbs.length; ++i) { var part = verbs[i]; var name = part[0]; + if (split_admin_tabs && name.lastIndexOf(".") != -1) { + var splitName = name.split("."); + if (splitName[0] === "Admin") + name = splitName[1]; + } var command = part[1]; if (command && name.lastIndexOf(cat, 0) != -1 && (name.length == cat.length || name.charAt(cat.length) == ".")) { @@ -1002,9 +1207,13 @@ function draw_verbs(cat){ } var a = document.createElement("a"); - a.href = "byond://winset?command=" + command.replace(/\s/g, "-"); - a[textContentKey] = command; + a.href = "#" + a.onclick = make_verb_onclick(command.replace(/\s/g, "-")); a.className = "grid-item"; + var t = document.createElement("span"); + t[textContentKey] = command; + t.className = "grid-item-text"; + a.appendChild(t); (subCat ? additions[subCat] : table).appendChild(a); } } @@ -1029,7 +1238,7 @@ function set_theme(which) { if (which == "light") { document.body.className = ""; set_style_sheet("browserOutput_white"); - } else if (which == "dark" || which == "default") { + } else if (which == "dark") { document.body.className = "dark"; set_style_sheet("browserOutput"); } @@ -1040,7 +1249,7 @@ function set_style_sheet(sheet) { var currentSheet = document.getElementById("goonStyle"); currentSheet.parentElement.removeChild(currentSheet); } - var head = document.getElementsByTagName('head')[0]; + var head = document.getElementsByTagName('head')[0]; var sheetElement = document.createElement("link"); sheetElement.id = "goonStyle"; sheetElement.rel = "stylesheet"; @@ -1050,9 +1259,14 @@ function set_style_sheet(sheet) { head.appendChild(sheetElement); } -document[addEventListenerKey]("click", function(e) { - window.location.href = "byond://winset?map.focus=true"; -}); +function restoreFocus() { + run_after_focus(function() { + window.location.href = "byond://winset?map.focus=true"; + }); +} + +document[addEventListenerKey]("mouseup", restoreFocus); +document[addEventListenerKey]("keyup", restoreFocus); if(!current_tab) { addPermanentTab("Status"); @@ -1061,7 +1275,7 @@ if(!current_tab) { window.onload = function() { NotifyByondOnload(); - }; +}; function NotifyByondOnload() { window.location.href = "byond://winset?command=Panel-Ready"; From 21b68944e7216d65042708022dc39516fab65c8c Mon Sep 17 00:00:00 2001 From: SandPoot Date: Sat, 5 Mar 2022 16:00:12 -0300 Subject: [PATCH 05/22] Upload files --- code/__HELPERS/AStar.dm | 209 ----------- code/__HELPERS/heap.dm | 59 +-- code/__HELPERS/path.dm | 349 ++++++++++++++++++ code/game/machinery/doors/airlock.dm | 6 +- code/game/objects/objs.dm | 13 +- code/game/objects/structures/girders.dm | 7 +- code/game/objects/structures/grille.dm | 7 +- code/game/objects/structures/morgue.dm | 7 +- code/game/objects/structures/plasticflaps.dm | 18 +- code/game/objects/structures/tables_racks.dm | 14 +- code/game/objects/structures/window.dm | 12 +- code/game/turfs/turf.dm | 20 + code/modules/antagonists/blob/blob/theblob.dm | 9 +- .../changeling/powers/tiny_prick.dm | 2 +- .../integrated_electronics/subtypes/smart.dm | 2 +- .../mob/living/carbon/monkey/combat.dm | 2 +- .../mob/living/simple_animal/bot/bot.dm | 7 +- .../mob/living/simple_animal/bot/cleanbot.dm | 2 +- .../mob/living/simple_animal/bot/firebot.dm | 2 +- .../mob/living/simple_animal/bot/floorbot.dm | 4 +- .../mob/living/simple_animal/bot/medbot.dm | 4 +- .../mob/living/simple_animal/bot/mulebot.dm | 2 +- .../mob/living/simple_animal/parrot.dm | 2 +- tgstation.dme | 2 +- 24 files changed, 466 insertions(+), 295 deletions(-) delete mode 100644 code/__HELPERS/AStar.dm create mode 100644 code/__HELPERS/path.dm diff --git a/code/__HELPERS/AStar.dm b/code/__HELPERS/AStar.dm deleted file mode 100644 index 5e03d9b350..0000000000 --- a/code/__HELPERS/AStar.dm +++ /dev/null @@ -1,209 +0,0 @@ -/* -A Star pathfinding algorithm -Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of -windows along the route into account. -Use: -your_list = AStar(start location, end location, moving atom, distance proc, max nodes, maximum node depth, minimum distance to target, adjacent proc, atom id, turfs to exclude, check only simulated) - -Optional extras to add on (in order): -Distance proc : the distance used in every A* calculation (length of path and heuristic) -MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) -Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) -Mintargetdist: Minimum distance to the target before path returns, could be used to get -near a target, but not right to it - for an AI mob with a gun, for example. -Adjacent proc : returns the turfs to consider around the actually processed node -Simulated only : whether to consider unsimulated turfs or not (used by some Adjacent proc) - -Also added 'exclude' turf to avoid travelling over; defaults to null - -Actual Adjacent procs : - - /turf/proc/reachableAdjacentTurfs : returns reachable turfs in cardinal directions (uses simulated_only) - - /turf/proc/reachableAdjacentAtmosTurfs : returns turfs in cardinal directions reachable via atmos - -*/ -#define PF_TIEBREAKER 0.005 -//tiebreker weight.To help to choose between equal paths -////////////////////// -//datum/PathNode object -////////////////////// -#define MASK_ODD 85 -#define MASK_EVEN 170 - - -//A* nodes variables -/datum/PathNode - var/turf/source //turf associated with the PathNode - var/datum/PathNode/prevNode //link to the parent PathNode - var/f //A* Node weight (f = g + h) - var/g //A* movement cost variable - var/h //A* heuristic variable - var/nt //count the number of Nodes traversed - var/bf //bitflag for dir to expand.Some sufficiently advanced motherfuckery - -/datum/PathNode/New(s,p,pg,ph,pnt,_bf) - source = s - prevNode = p - g = pg - h = ph - f = g + h*(1+ PF_TIEBREAKER) - nt = pnt - bf = _bf - -/datum/PathNode/proc/setp(p,pg,ph,pnt) - prevNode = p - g = pg - h = ph - f = g + h*(1+ PF_TIEBREAKER) - nt = pnt - -/datum/PathNode/proc/calc_f() - f = g + h - -////////////////////// -//A* procs -////////////////////// - -//the weighting function, used in the A* algorithm -/proc/PathWeightCompare(datum/PathNode/a, datum/PathNode/b) - return a.f - b.f - -//reversed so that the Heap is a MinHeap rather than a MaxHeap -/proc/HeapPathWeightCompare(datum/PathNode/a, datum/PathNode/b) - return b.f - a.f - -//wrapper that returns an empty list if A* failed to find a path -/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableTurftest, id=null, turf/exclude=null, simulated_only = 1) - var/l = SSpathfinder.mobs.getfree(caller) - while(!l) - stoplag(3) - l = SSpathfinder.mobs.getfree(caller) - var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent,id, exclude, simulated_only) - - SSpathfinder.mobs.found(l) - if(!path) - path = list() - return path - -/proc/cir_get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableTurftest, id=null, turf/exclude=null, simulated_only = 1) - var/l = SSpathfinder.circuits.getfree(caller) - while(!l) - stoplag(3) - l = SSpathfinder.circuits.getfree(caller) - var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent,id, exclude, simulated_only) - SSpathfinder.circuits.found(l) - if(!path) - path = list() - return path - -/proc/AStar(caller, _end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableTurftest, id=null, turf/exclude=null, simulated_only = 1) - //sanitation - var/turf/end = get_turf(_end) - var/turf/start = get_turf(caller) - if(!start || !end) - stack_trace("Invalid A* start or destination") - return 0 - if( start.z != end.z || start == end ) //no pathfinding between z levels - return 0 - if(maxnodes) - //if start turf is farther than maxnodes from end turf, no need to do anything - if(call(start, dist)(end) > maxnodes) - return 0 - maxnodedepth = maxnodes //no need to consider path longer than maxnodes - var/datum/Heap/open = new /datum/Heap(/proc/HeapPathWeightCompare) //the open list - var/list/openc = new() //open list for node check - var/list/path = null //the returned path, if any - //initialization - var/datum/PathNode/cur = new /datum/PathNode(start,null,0,call(start,dist)(end),0,15,1)//current processed turf - open.Insert(cur) - openc[start] = cur - //then run the main loop - while(!open.IsEmpty() && !path) - cur = open.Pop() //get the lower f turf in the open list - //get the lower f node on the open list - //if we only want to get near the target, check if we're close enough - var/closeenough - if(mintargetdist) - closeenough = call(cur.source,dist)(end) <= mintargetdist - - - //found the target turf (or close enough), let's create the path to it - if(cur.source == end || closeenough) - path = new() - path.Add(cur.source) - while(cur.prevNode) - cur = cur.prevNode - path.Add(cur.source) - break - //get adjacents turfs using the adjacent proc, checking for access with id - if((!maxnodedepth)||(cur.nt <= maxnodedepth))//if too many steps, don't process that path - for(var/i = 0 to 3) - var/f= 1<>1) //getting reverse direction throught swapping even and odd bits.((f & 01010101)<<1)|((f & 10101010)>>1) - var/newg = cur.g + call(cur.source,dist)(T) - if(CN) - //is already in open list, check if it's a better way from the current turf - CN.bf &= 15^r //we have no closed, so just cut off exceed dir.00001111 ^ reverse_dir.We don't need to expand to checked turf. - if((newg < CN.g) ) - if(call(cur.source,adjacent)(caller, T, id, simulated_only)) - CN.setp(cur,newg,CN.h,cur.nt+1) - open.ReSort(CN)//reorder the changed element in the list - else - //is not already in open list, so add it - if(call(cur.source,adjacent)(caller, T, id, simulated_only)) - CN = new(T,cur,newg,call(T,dist)(end),cur.nt+1,15^r) - open.Insert(CN) - openc[T] = CN - cur.bf = 0 - CHECK_TICK - //reverse the path to get it from start to finish - if(path) - for(var/i = 1 to round(0.5*path.len)) - path.Swap(i,path.len-i+1) - openc = null - //cleaning after us - return path - -//Returns adjacent turfs in cardinal directions that are reachable -//simulated_only controls whether only simulated turfs are considered or not - -/turf/proc/reachableAdjacentTurfs(caller, ID, simulated_only) - var/list/L = new() - var/turf/T - var/static/space_type_cache = typecacheof(/turf/open/space) - - for(var/k in 1 to GLOB.cardinals.len) - T = get_step(src,GLOB.cardinals[k]) - if(!T || (simulated_only && space_type_cache[T.type])) - continue - if(!T.density && !LinkBlockedWithAccess(T,caller, ID)) - L.Add(T) - return L - -/turf/proc/reachableTurftest(caller, var/turf/T, ID, simulated_only) - if(T && !T.density && !(simulated_only && SSpathfinder.space_type_cache[T.type]) && !LinkBlockedWithAccess(T,caller, ID)) - return TRUE - -//Returns adjacent turfs in cardinal directions that are reachable via atmos -/turf/proc/reachableAdjacentAtmosTurfs() - return atmos_adjacent_turfs - -/turf/proc/LinkBlockedWithAccess(turf/T, caller, ID) - var/adir = get_dir(src, T) - var/rdir = ((adir & MASK_ODD)<<1)|((adir & MASK_EVEN)>>1) - for(var/obj/structure/window/W in src) - if(!W.CanAStarPass(ID, adir)) - return 1 - for(var/obj/machinery/door/window/W in src) - if(!W.CanAStarPass(ID, adir)) - return 1 - for(var/obj/O in T) - if(!O.CanAStarPass(ID, rdir, caller)) - return 1 - - return 0 diff --git a/code/__HELPERS/heap.dm b/code/__HELPERS/heap.dm index 916e7fc05c..eabcb0e0dc 100644 --- a/code/__HELPERS/heap.dm +++ b/code/__HELPERS/heap.dm @@ -1,39 +1,45 @@ - ////////////////////// -//datum/Heap object +//datum/heap object ////////////////////// -/datum/Heap +/datum/heap var/list/L var/cmp -/datum/Heap/New(compare) +/datum/heap/New(compare) L = new() cmp = compare -/datum/Heap/proc/IsEmpty() - return !L.len +/datum/heap/Destroy(force, ...) + for(var/i in L) // because this is before the list helpers are loaded + qdel(i) + L = null + return ..() -//Insert and place at its position a new node in the heap -/datum/Heap/proc/Insert(atom/A) +/datum/heap/proc/is_empty() + return !length(L) + +//insert and place at its position a new node in the heap +/datum/heap/proc/insert(atom/A) L.Add(A) - Swim(L.len) + swim(length(L)) //removes and returns the first element of the heap //(i.e the max or the min dependant on the comparison function) -/datum/Heap/proc/Pop() - if(!L.len) +/datum/heap/proc/pop() + if(!length(L)) return 0 . = L[1] - L[1] = L[L.len] - L.Cut(L.len) - if(L.len) - Sink(1) + L[1] = L[length(L)] + L.Cut(length(L)) + if(length(L)) + sink(1) + //Get a node up to its right position in the heap -/datum/Heap/proc/Swim(var/index) +/datum/heap/proc/swim(index) var/parent = round(index * 0.5) while(parent > 0 && (call(cmp)(L[index],L[parent]) > 0)) @@ -42,21 +48,21 @@ parent = round(index * 0.5) //Get a node down to its right position in the heap -/datum/Heap/proc/Sink(var/index) - var/g_child = GetGreaterChild(index) +/datum/heap/proc/sink(index) + var/g_child = get_greater_child(index) while(g_child > 0 && (call(cmp)(L[index],L[g_child]) < 0)) L.Swap(index,g_child) index = g_child - g_child = GetGreaterChild(index) + g_child = get_greater_child(index) //Returns the greater (relative to the comparison proc) of a node children //or 0 if there's no child -/datum/Heap/proc/GetGreaterChild(var/index) - if(index * 2 > L.len) +/datum/heap/proc/get_greater_child(index) + if(index * 2 > length(L)) return 0 - if(index * 2 + 1 > L.len) + if(index * 2 + 1 > length(L)) return index * 2 if(call(cmp)(L[index * 2],L[index * 2 + 1]) < 0) @@ -65,12 +71,11 @@ return index * 2 //Replaces a given node so it verify the heap condition -/datum/Heap/proc/ReSort(atom/A) +/datum/heap/proc/resort(atom/A) var/index = L.Find(A) - Swim(index) - Sink(index) + swim(index) + sink(index) -/datum/Heap/proc/List() +/datum/heap/proc/List() . = L.Copy() - diff --git a/code/__HELPERS/path.dm b/code/__HELPERS/path.dm new file mode 100644 index 0000000000..96becc1a59 --- /dev/null +++ b/code/__HELPERS/path.dm @@ -0,0 +1,349 @@ +/** + * This file contains the stuff you need for using JPS (Jump Point Search) pathing, an alternative to A* that skips + * over large numbers of uninteresting tiles resulting in much quicker pathfinding solutions. Mind that diagonals + * cost the same as cardinal moves currently, so paths may look a bit strange, but should still be optimal. + */ + +/** + * This is the proc you use whenever you want to have pathfinding more complex than "try stepping towards the thing" + * + * Arguments: + * * caller: The movable atom that's trying to find the path + * * end: What we're trying to path to. It doesn't matter if this is a turf or some other atom, we're gonna just path to the turf it's on anyway + * * max_distance: The maximum number of steps we can take in a given path to search (default: 30, 0 = infinite) + * * mintargetdistance: Minimum distance to the target before path returns, could be used to get near a target, but not right to it - for an AI mob with a gun, for example. + * * id: An ID card representing what access we have and what doors we can open. Its location relative to the pathing atom is irrelevant + * * simulated_only: Whether we consider turfs without atmos simulation (AKA do we want to ignore space) + * * exclude: If we want to avoid a specific turf, like if we're a mulebot who already got blocked by some turf + */ +/proc/get_path_to(caller, end, max_distance = 30, mintargetdist, id=null, simulated_only = TRUE, turf/exclude) + if(!caller || !get_turf(end)) + return + + var/l = SSpathfinder.mobs.getfree(caller) + while(!l) + stoplag(3) + l = SSpathfinder.mobs.getfree(caller) + + var/list/path + var/datum/pathfind/pathfind_datum = new(caller, end, id, max_distance, mintargetdist, simulated_only, exclude) + path = pathfind_datum.search() + qdel(pathfind_datum) + + SSpathfinder.mobs.found(l) + if(!path) + path = list() + return path + +/** + * A helper macro to see if it's possible to step from the first turf into the second one, minding things like door access and directional windows. + * Note that this can only be used inside the [datum/pathfind][pathfind datum] since it uses variables from said datum + * If you really want to optimize things, optimize this, cuz this gets called a lot + */ +#define CAN_STEP(cur_turf, next) (next && !next.density && cur_turf.Adjacent(next) && !(simulated_only && SSpathfinder.space_type_cache[next.type]) && !cur_turf.LinkBlockedWithAccess(next,caller, id) && (next != avoid)) +/// Another helper macro for JPS, for telling when a node has forced neighbors that need expanding +#define STEP_NOT_HERE_BUT_THERE(cur_turf, dirA, dirB) ((!CAN_STEP(cur_turf, get_step(cur_turf, dirA)) && CAN_STEP(cur_turf, get_step(cur_turf, dirB)))) + +/// The JPS Node datum represents a turf that we find interesting enough to add to the open list and possibly search for new tiles from +/datum/jps_node + /// The turf associated with this node + var/turf/tile + /// The node we just came from + var/datum/jps_node/previous_node + /// The A* node weight (f_value = number_of_tiles + heuristic) + var/f_value + /// The A* node heuristic (a rough estimate of how far we are from the goal) + var/heuristic + /// How many steps it's taken to get here from the start (currently pulling double duty as steps taken & cost to get here, since all moves incl diagonals cost 1 rn) + var/number_tiles + /// How many steps it took to get here from the last node + var/jumps + /// Nodes store the endgoal so they can process their heuristic without a reference to the pathfind datum + var/turf/node_goal + +/datum/jps_node/New(turf/our_tile, datum/jps_node/incoming_previous_node, jumps_taken, turf/incoming_goal) + tile = our_tile + jumps = jumps_taken + if(incoming_goal) // if we have the goal argument, this must be the first/starting node + node_goal = incoming_goal + else if(incoming_previous_node) // if we have the parent, this is from a direct lateral/diagonal scan, we can fill it all out now + previous_node = incoming_previous_node + number_tiles = previous_node.number_tiles + jumps + node_goal = previous_node.node_goal + heuristic = get_dist(tile, node_goal) + f_value = number_tiles + heuristic + // otherwise, no parent node means this is from a subscan lateral scan, so we just need the tile for now until we call [datum/jps/proc/update_parent] on it + +/datum/jps_node/Destroy(force, ...) + previous_node = null + return ..() + +/datum/jps_node/proc/update_parent(datum/jps_node/new_parent) + previous_node = new_parent + node_goal = previous_node.node_goal + jumps = get_dist(tile, previous_node.tile) + number_tiles = previous_node.number_tiles + jumps + heuristic = get_dist(tile, node_goal) + f_value = number_tiles + heuristic + +/// TODO: Macro this to reduce proc overhead +/proc/HeapPathWeightCompare(datum/jps_node/a, datum/jps_node/b) + return b.f_value - a.f_value + +/// The datum used to handle the JPS pathfinding, completely self-contained +/datum/pathfind + /// The thing that we're actually trying to path for + var/atom/movable/caller + /// The turf where we started at + var/turf/start + /// The turf we're trying to path to (note that this won't track a moving target) + var/turf/end + /// The open list/stack we pop nodes out from (TODO: make this a normal list and macro-ize the heap operations to reduce proc overhead) + var/datum/heap/open + ///An assoc list that serves as the closed list & tracks what turfs came from where. Key is the turf, and the value is what turf it came from + var/list/sources + /// The list we compile at the end if successful to pass back + var/list/path + + // general pathfinding vars/args + /// An ID card representing what access we have and what doors we can open. Its location relative to the pathing atom is irrelevant + var/obj/item/card/id/id + /// How far away we have to get to the end target before we can call it quits + var/mintargetdist = 0 + /// I don't know what this does vs , but they limit how far we can search before giving up on a path + var/max_distance = 30 + /// Space is big and empty, if this is TRUE then we ignore pathing through unsimulated tiles + var/simulated_only + /// A specific turf we're avoiding, like if a mulebot is being blocked by someone t-posing in a doorway we're trying to get through + var/turf/avoid + +/datum/pathfind/New(atom/movable/caller, atom/goal, id, max_distance, mintargetdist, simulated_only, avoid) + src.caller = caller + end = get_turf(goal) + open = new /datum/heap(/proc/HeapPathWeightCompare) + sources = new() + src.id = id + src.max_distance = max_distance + src.mintargetdist = mintargetdist + src.simulated_only = simulated_only + src.avoid = avoid + +/// The proc you use to run the search, returns FALSE if it's invalid, an empty list if no path could be found, or a valid path to the target +/datum/pathfind/proc/search() + start = get_turf(caller) + if(!start || !end) + stack_trace("Invalid A* start or destination") + return FALSE + if(start.z != end.z || start == end ) //no pathfinding between z levels + return FALSE + if(max_distance && (max_distance < get_dist(start, end))) //if start turf is farther than max_distance from end turf, no need to do anything + return FALSE + + //initialization + var/datum/jps_node/current_processed_node = new (start, -1, 0, end) + open.insert(current_processed_node) + sources[start] = start // i'm sure this is fine + + //then run the main loop + while(!open.is_empty() && !path) + if(!caller) + return + current_processed_node = open.pop() //get the lower f_value turf in the open list + if(max_distance && (current_processed_node.number_tiles > max_distance))//if too many steps, don't process that path + continue + + var/turf/current_turf = current_processed_node.tile + for(var/scan_direction in list(EAST, WEST, NORTH, SOUTH)) + lateral_scan_spec(current_turf, scan_direction, current_processed_node) + + for(var/scan_direction in list(NORTHEAST, SOUTHEAST, NORTHWEST, SOUTHWEST)) + diag_scan_spec(current_turf, scan_direction, current_processed_node) + + CHECK_TICK + + //we're done! reverse the path to get it from start to finish + if(path) + for(var/i = 1 to round(0.5 * length(path))) + path.Swap(i, length(path) - i + 1) + sources = null + qdel(open) + return path + +/// Called when we've hit the goal with the node that represents the last tile, then sets the path var to that path so it can be returned by [datum/pathfind/proc/search] +/datum/pathfind/proc/unwind_path(datum/jps_node/unwind_node) + path = new() + var/turf/iter_turf = unwind_node.tile + path.Add(iter_turf) + + while(unwind_node.previous_node) + var/dir_goal = get_dir(iter_turf, unwind_node.previous_node.tile) + for(var/i = 1 to unwind_node.jumps) + iter_turf = get_step(iter_turf,dir_goal) + path.Add(iter_turf) + unwind_node = unwind_node.previous_node + +/** + * For performing lateral scans from a given starting turf. + * + * These scans are called from both the main search loop, as well as subscans for diagonal scans, and they treat finding interesting turfs slightly differently. + * If we're doing a normal lateral scan, we already have a parent node supplied, so we just create the new node and immediately insert it into the heap, ezpz. + * If we're part of a subscan, we still need for the diagonal scan to generate a parent node, so we return a node datum with just the turf and let the diag scan + * proc handle transferring the values and inserting them into the heap. + * + * Arguments: + * * original_turf: What turf did we start this scan at? + * * heading: What direction are we going in? Obviously, should be cardinal + * * parent_node: Only given for normal lateral scans, if we don't have one, we're a diagonal subscan. +*/ +/datum/pathfind/proc/lateral_scan_spec(turf/original_turf, heading, datum/jps_node/parent_node) + var/steps_taken = 0 + + var/turf/current_turf = original_turf + var/turf/lag_turf = original_turf + + while(TRUE) + if(path) + return + lag_turf = current_turf + current_turf = get_step(current_turf, heading) + steps_taken++ + if(!CAN_STEP(lag_turf, current_turf)) + return + + if(current_turf == end || (mintargetdist && (get_dist(current_turf, end) <= mintargetdist))) + var/datum/jps_node/final_node = new(current_turf, parent_node, steps_taken) + sources[current_turf] = original_turf + if(parent_node) // if this is a direct lateral scan we can wrap up, if it's a subscan from a diag, we need to let the diag make their node first, then finish + unwind_path(final_node) + return final_node + else if(sources[current_turf]) // already visited, essentially in the closed list + return + else + sources[current_turf] = original_turf + + if(parent_node && parent_node.number_tiles + steps_taken > max_distance) + return + + var/interesting = FALSE // have we found a forced neighbor that would make us add this turf to the open list? + + switch(heading) + if(NORTH) + if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, EAST, NORTHEAST)) + interesting = TRUE + if(SOUTH) + if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, SOUTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, EAST, SOUTHEAST)) + interesting = TRUE + if(EAST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHEAST)) + interesting = TRUE + if(WEST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHWEST)) + interesting = TRUE + + if(interesting) + var/datum/jps_node/newnode = new(current_turf, parent_node, steps_taken) + if(parent_node) // if we're a diagonal subscan, we'll handle adding ourselves to the heap in the diag + open.insert(newnode) + return newnode + +/** + * For performing diagonal scans from a given starting turf. + * + * Unlike lateral scans, these only are called from the main search loop, so we don't need to worry about returning anything, + * though we do need to handle the return values of our lateral subscans of course. + * + * Arguments: + * * original_turf: What turf did we start this scan at? + * * heading: What direction are we going in? Obviously, should be diagonal + * * parent_node: We should always have a parent node for diagonals +*/ +/datum/pathfind/proc/diag_scan_spec(turf/original_turf, heading, datum/jps_node/parent_node) + var/steps_taken = 0 + var/turf/current_turf = original_turf + var/turf/lag_turf = original_turf + + while(TRUE) + if(path) + return + lag_turf = current_turf + current_turf = get_step(current_turf, heading) + steps_taken++ + if(!CAN_STEP(lag_turf, current_turf)) + return + + if(current_turf == end || (mintargetdist && (get_dist(current_turf, end) <= mintargetdist))) + var/datum/jps_node/final_node = new(current_turf, parent_node, steps_taken) + sources[current_turf] = original_turf + unwind_path(final_node) + return + else if(sources[current_turf]) // already visited, essentially in the closed list + return + else + sources[current_turf] = original_turf + + if(parent_node.number_tiles + steps_taken > max_distance) + return + + var/interesting = FALSE // have we found a forced neighbor that would make us add this turf to the open list? + var/datum/jps_node/possible_child_node // otherwise, did one of our lateral subscans turn up something? + + switch(heading) + if(NORTHWEST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, EAST, NORTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHWEST)) + interesting = TRUE + else + possible_child_node = (lateral_scan_spec(current_turf, WEST) || lateral_scan_spec(current_turf, NORTH)) + if(NORTHEAST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHEAST)) + interesting = TRUE + else + possible_child_node = (lateral_scan_spec(current_turf, EAST) || lateral_scan_spec(current_turf, NORTH)) + if(SOUTHWEST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, EAST, SOUTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHWEST)) + interesting = TRUE + else + possible_child_node = (lateral_scan_spec(current_turf, SOUTH) || lateral_scan_spec(current_turf, WEST)) + if(SOUTHEAST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, SOUTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHEAST)) + interesting = TRUE + else + possible_child_node = (lateral_scan_spec(current_turf, SOUTH) || lateral_scan_spec(current_turf, EAST)) + + if(interesting || possible_child_node) + var/datum/jps_node/newnode = new(current_turf, parent_node, steps_taken) + open.insert(newnode) + if(possible_child_node) + possible_child_node.update_parent(newnode) + open.insert(possible_child_node) + if(possible_child_node.tile == end || (mintargetdist && (get_dist(possible_child_node.tile, end) <= mintargetdist))) + unwind_path(possible_child_node) + return + +/** + * For seeing if we can actually move between 2 given turfs while accounting for our access and the caller's pass_flags + * + * Arguments: + * * caller: The movable, if one exists, being used for mobility checks to see what tiles it can reach + * * ID: An ID card that decides if we can gain access to doors that would otherwise block a turf + * * simulated_only: Do we only worry about turfs with simulated atmos, most notably things that aren't space? +*/ +/turf/proc/LinkBlockedWithAccess(turf/destination_turf, caller, ID) + var/actual_dir = get_dir(src, destination_turf) + + for(var/obj/structure/window/iter_window in src) + if(!iter_window.CanAStarPass(ID, actual_dir)) + return TRUE + + for(var/obj/machinery/door/window/iter_windoor in src) + if(!iter_windoor.CanAStarPass(ID, actual_dir)) + return TRUE + + var/reverse_dir = get_dir(destination_turf, src) + for(var/obj/iter_object in destination_turf) + if(!iter_object.CanAStarPass(ID, reverse_dir, caller)) + return TRUE + + return FALSE + +#undef CAN_STEP +#undef STEP_NOT_HERE_BUT_THERE diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 982e40a79b..ce29d1947d 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -102,7 +102,7 @@ rad_insulation = RAD_MEDIUM_INSULATION var/static/list/airlock_overlays = list() - + /// sigh var/unelectrify_timerid @@ -1278,8 +1278,8 @@ assemblytype = /obj/structure/door_assembly/door_assembly_extmai update_icon() -/obj/machinery/door/airlock/CanAStarPass(obj/item/card/id/ID) -//Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off) +/obj/machinery/door/airlock/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) + //Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off) return !density || (check_access(ID) && !locked && hasPower()) /obj/machinery/door/airlock/emag_act(mob/user) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index e263678943..cf256dd6fc 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -238,7 +238,18 @@ /obj/get_dumping_location(datum/component/storage/source,mob/user) return get_turf(src) -/obj/proc/CanAStarPass() +/** + * This proc is used for telling whether something can pass by this object in a given direction, for use by the pathfinding system. + * + * Trying to generate one long path across the station will call this proc on every single object on every single tile that we're seeing if we can move through, likely + * multiple times per tile since we're likely checking if we can access said tile from multiple directions, so keep these as lightweight as possible. + * + * Arguments: + * * ID- An ID card representing what access we have (and thus if we can open things like airlocks or windows to pass through them). The ID card's physical location does not matter, just the reference + * * to_dir- What direction we're trying to move in, relevant for things like directional windows that only block movement in certain directions + * * caller- The movable we're checking pass flags for, if we're making any such checks + **/ +/obj/proc/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) . = !density /obj/proc/check_uplink_validity() diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 50240aabb3..22f9cf7111 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -305,11 +305,10 @@ else return 0 -/obj/structure/girder/CanAStarPass(ID, dir, caller) +/obj/structure/girder/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) . = !density - if(ismovable(caller)) - var/atom/movable/mover = caller - . = . || (mover.pass_flags & PASSGRILLE) + if(istype(caller)) + . = . || (caller.pass_flags & PASSGRILLE) /obj/structure/girder/deconstruct(disassembled = TRUE) if(!(flags_1 & NODECONSTRUCT_1)) diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 2ac85f79dc..a09ce578e7 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -133,11 +133,10 @@ else return !density -/obj/structure/grille/CanAStarPass(ID, dir, caller) +/obj/structure/grille/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) . = !density - if(ismovable(caller)) - var/atom/movable/mover = caller - . = . || (mover.pass_flags & PASSGRILLE) + if(istype(caller)) + . = . || (caller.pass_flags & PASSGRILLE) /obj/structure/grille/attackby(obj/item/W, mob/user, params) user.DelayNextAction(CLICK_CD_MELEE) diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 73268009ce..383b57a0b0 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -371,8 +371,7 @@ GLOBAL_LIST_EMPTY(crematoriums) else return 0 -/obj/structure/tray/m_tray/CanAStarPass(ID, dir, caller) +/obj/structure/tray/m_tray/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) . = !density - if(ismovable(caller)) - var/atom/movable/mover = caller - . = . || (mover.pass_flags & PASSTABLE) + if(istype(caller)) + . = . || (caller.pass_flags & PASSTABLE) diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm index ff8287d77b..46d5316458 100644 --- a/code/game/objects/structures/plasticflaps.dm +++ b/code/game/objects/structures/plasticflaps.dm @@ -56,18 +56,18 @@ return FALSE return TRUE -/obj/structure/plasticflaps/CanAStarPass(ID, to_dir, caller) +/obj/structure/plasticflaps/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) if(isliving(caller)) if(isbot(caller)) - return 1 + return TRUE - var/mob/living/M = caller - if(!(SEND_SIGNAL(M, COMSIG_CHECK_VENTCRAWL)) && M.mob_size != MOB_SIZE_TINY) - return 0 - var/atom/movable/M = caller - if(M && M.pulling) - return CanAStarPass(ID, to_dir, M.pulling) - return 1 //diseases, stings, etc can pass + var/mob/living/living_caller = caller + if(!(SEND_SIGNAL(living_caller, COMSIG_CHECK_VENTCRAWL)) && living_caller.mob_size != MOB_SIZE_TINY) + return FALSE + + if(caller?.pulling) + return CanAStarPass(ID, to_dir, caller.pulling) + return TRUE //diseases, stings, etc can pass /obj/structure/plasticflaps/CanPass(atom/movable/A, turf/T) if(istype(A) && (A.pass_flags & PASSGLASS)) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 065c0d2f28..24342e44bd 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -109,11 +109,10 @@ else return !density -/obj/structure/table/CanAStarPass(ID, dir, caller) +/obj/structure/table/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) . = !density - if(ismovable(caller)) - var/atom/movable/mover = caller - . = . || (mover.pass_flags & PASSTABLE) + if(istype(caller)) + . = . || (caller.pass_flags & PASSTABLE) /obj/structure/table/proc/tableplace(mob/living/user, mob/living/pushed_mob) pushed_mob.forceMove(src.loc) @@ -702,11 +701,10 @@ else return 0 -/obj/structure/rack/CanAStarPass(ID, dir, caller) +/obj/structure/rack/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) . = !density - if(ismovable(caller)) - var/atom/movable/mover = caller - . = . || (mover.pass_flags & PASSTABLE) + if(istype(caller)) + . = . || (caller.pass_flags & PASSTABLE) /obj/structure/rack/MouseDrop_T(obj/O, mob/user) . = ..() diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 4853d553dc..e8e170446b 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -90,13 +90,13 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) . = ..() if(direct) setDir(direct) - + if(extra_reinforced && anchored) state = PRWINDOW_SECURE else if(reinf && anchored) state = WINDOW_SCREWED_TO_FRAME - + if(mapload && electrochromatic_id && electrochromatic_id[1] == "!") electrochromatic_id = SSmapping.get_obfuscated_id(electrochromatic_id) @@ -571,13 +571,13 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) /obj/structure/window/get_dumping_location(obj/item/storage/source,mob/user) return null -/obj/structure/window/CanAStarPass(ID, to_dir) +/obj/structure/window/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) if(!density) - return 1 + return TRUE if((dir == FULLTILE_WINDOW_DIR) || (dir == to_dir)) - return 0 + return FALSE - return 1 + return TRUE /obj/structure/window/GetExplosionBlock() return reinf && fulltile ? real_explosion_block : 0 diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 85de964463..419378acb9 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -638,3 +638,23 @@ GLOBAL_LIST_EMPTY(station_turfs) var/obj/machinery/door/D = locate() in src if(D?.density) . += D.opacity? 29 : 19 // glass doors are slightly more resistant to screaming + +/** + * Returns adjacent turfs to this turf that are reachable, in all cardinal directions + * + * Arguments: + * * caller: The movable, if one exists, being used for mobility checks to see what tiles it can reach + * * ID: An ID card that decides if we can gain access to doors that would otherwise block a turf + * * simulated_only: Do we only worry about turfs with simulated atmos, most notably things that aren't space? +*/ +/turf/proc/reachableAdjacentTurfs(caller, ID, simulated_only) + var/static/space_type_cache = typecacheof(/turf/open/space) + . = list() + + for(var/iter_dir in GLOB.cardinals) + var/turf/turf_to_check = get_step(src,iter_dir) + if(!turf_to_check || (simulated_only && space_type_cache[turf_to_check.type])) + continue + if(turf_to_check.density || LinkBlockedWithAccess(turf_to_check, caller, ID)) + continue + . += turf_to_check diff --git a/code/modules/antagonists/blob/blob/theblob.dm b/code/modules/antagonists/blob/blob/theblob.dm index a95f73f90d..8ca7e65c3f 100644 --- a/code/modules/antagonists/blob/blob/theblob.dm +++ b/code/modules/antagonists/blob/blob/theblob.dm @@ -75,11 +75,10 @@ /obj/structure/blob/CanAtmosPass(turf/T) return !atmosblock -/obj/structure/blob/CanAStarPass(ID, dir, caller) - . = 0 - if(ismovable(caller)) - var/atom/movable/mover = caller - . = . || (mover.pass_flags & PASSBLOB) +/obj/structure/blob/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) + . = FALSE + if(istype(caller)) + . = . || (caller.pass_flags & PASSBLOB) /obj/structure/blob/update_icon() //Updates color based on overmind color if we have an overmind. if(overmind) diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm index 9bda1bf5b9..d69c3cc65a 100644 --- a/code/modules/antagonists/changeling/powers/tiny_prick.dm +++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm @@ -47,7 +47,7 @@ return if(!isturf(user.loc)) return - if(!AStar(user, target.loc, /turf/proc/Distance, changeling.sting_range, simulated_only = 0)) + if(!get_path_to(user, target, max_distance = changeling.sting_range, simulated_only = FALSE)) return return 1 diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm index 5dc866c556..e1fdc36552 100644 --- a/code/modules/integrated_electronics/subtypes/smart.dm +++ b/code/modules/integrated_electronics/subtypes/smart.dm @@ -97,7 +97,7 @@ return idc.access = assembly.access_card.access var/turf/a_loc = get_turf(assembly) - var/list/P = cir_get_path_to(assembly, locate(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2),a_loc.z), /turf/proc/Distance_cardinal, 0, 200, id=idc, exclude=get_turf(get_pin_data_as_type(IC_INPUT,3, /atom)), simulated_only = 0) + var/list/P = get_path_to(assembly, locate(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2),a_loc.z), 200, id=idc, exclude=get_turf(get_pin_data_as_type(IC_INPUT,3, /atom)), simulated_only = 0) if(!P) activate_pin(3) diff --git a/code/modules/mob/living/carbon/monkey/combat.dm b/code/modules/mob/living/carbon/monkey/combat.dm index 13d234092c..0c9895c729 100644 --- a/code/modules/mob/living/carbon/monkey/combat.dm +++ b/code/modules/mob/living/carbon/monkey/combat.dm @@ -30,7 +30,7 @@ return 0 if(myPath.len <= 0) - myPath = get_path_to(src, get_turf(target), /turf/proc/Distance, MAX_RANGE_FIND + 1, 250,1) + myPath = get_path_to(src, target, 250, 1) if(myPath) if(myPath.len > 0) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index ca9ca75e6c..3aec65f6fb 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -551,7 +551,7 @@ Pass a positive integer as an argument to override a bot's default speed. var/datum/job/captain/All = new/datum/job/captain all_access.access = All.get_access() - set_path(get_path_to(src, waypoint, /turf/proc/Distance_cardinal, 0, 200, id=all_access)) + set_path(get_path_to(src, waypoint, 200, id=all_access)) calling_ai = caller //Link the AI to the bot! ai_waypoint = waypoint @@ -730,6 +730,7 @@ Pass a positive integer as an argument to override a bot's default speed. access_card.access = user_access + prev_access //Adds the user's access, if any. mode = BOT_SUMMON speak("Responding.", radio_channel) + calc_summon_path() if("ejectpai") @@ -765,12 +766,12 @@ Pass a positive integer as an argument to override a bot's default speed. // given an optional turf to avoid /mob/living/simple_animal/bot/proc/calc_path(turf/avoid) check_bot_access() - set_path(get_path_to(src, patrol_target, /turf/proc/Distance_cardinal, 0, 120, id=access_card, exclude=avoid)) + set_path(get_path_to(src, patrol_target, 120, id=access_card, exclude=avoid)) /mob/living/simple_animal/bot/proc/calc_summon_path(turf/avoid) check_bot_access() spawn() - set_path(get_path_to(src, summon_target, /turf/proc/Distance_cardinal, 0, 150, id=access_card, exclude=avoid)) + set_path(get_path_to(src, summon_target, 150, id=access_card, exclude=avoid)) if(!path.len) //Cannot reach target. Give up and announce the issue. speak("Summon command failed, destination unreachable.",radio_channel) bot_reset() diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index c683267a58..ed68d4d272 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -302,7 +302,7 @@ if(!path || path.len == 0) //No path, need a new one //Try to produce a path to the target, and ignore airlocks to which it has access. - path = get_path_to(src, target.loc, /turf/proc/Distance_cardinal, 0, 30, id=access_card) + path = get_path_to(src, target, 30, id=access_card) if(!bot_move(target)) add_to_ignore(target) target = null diff --git a/code/modules/mob/living/simple_animal/bot/firebot.dm b/code/modules/mob/living/simple_animal/bot/firebot.dm index 66d1955502..9eea7a85b1 100644 --- a/code/modules/mob/living/simple_animal/bot/firebot.dm +++ b/code/modules/mob/living/simple_animal/bot/firebot.dm @@ -223,7 +223,7 @@ if(target_fire && (get_dist(src, target_fire) > 2)) - path = get_path_to(src, get_turf(target_fire), /turf/proc/Distance_cardinal, 0, 30, 1, id=access_card) + path = get_path_to(src, target_fire, 30, 1, id=access_card) mode = BOT_MOVING if(!path.len) soft_reset() diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index 5f0075aaa2..c8b502f032 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -263,9 +263,9 @@ if(path.len == 0) if(!isturf(target)) var/turf/TL = get_turf(target) - path = get_path_to(src, TL, /turf/proc/Distance_cardinal, 0, 30, id=access_card,simulated_only = 0) + path = get_path_to(src, TL, 30, id=access_card,simulated_only = 0) else - path = get_path_to(src, target, /turf/proc/Distance_cardinal, 0, 30, id=access_card,simulated_only = 0) + path = get_path_to(src, target, 30, id=access_card,simulated_only = 0) if(!bot_move(target)) add_to_ignore(target) diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index e00adfd8e8..0b21f4a825 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -492,10 +492,10 @@ return if(patient && path.len == 0 && (get_dist(src,patient) > 1)) - path = get_path_to(src, get_turf(patient), /turf/proc/Distance_cardinal, 0, 30,id=access_card) + path = get_path_to(src, patient, 30, id=access_card) mode = BOT_MOVING if(!path.len) //try to get closer if you can't reach the patient directly - path = get_path_to(src, get_turf(patient), /turf/proc/Distance_cardinal, 0, 30,1,id=access_card) + path = get_path_to(src, patient, 30, 1, id=access_card) if(!path.len) //Do not chase a patient we cannot reach. soft_reset() diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index dcb5e5f237..56e2f66feb 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -530,7 +530,7 @@ // calculates a path to the current destination // given an optional turf to avoid /mob/living/simple_animal/bot/mulebot/calc_path(turf/avoid = null) - path = get_path_to(src, target, /turf/proc/Distance_cardinal, 0, 250, id=access_card, exclude=avoid) + path = get_path_to(src, target, 250, id=access_card, exclude=avoid) // sets the current destination // signals all beacons matching the delivery code diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 7c7a684cf3..4cc66bc4bd 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -655,7 +655,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( item = I break if(item) - if(!AStar(src, get_turf(item), /turf/proc/Distance_cardinal)) + if(!get_path_to(src, item)) item = null continue return item diff --git a/tgstation.dme b/tgstation.dme index 8c7f064d60..47f37ec53f 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -171,7 +171,6 @@ #include "code\__HELPERS\_string_lists.dm" #include "code\__HELPERS\angles.dm" #include "code\__HELPERS\areas.dm" -#include "code\__HELPERS\AStar.dm" #include "code\__HELPERS\chat.dm" #include "code\__HELPERS\cmp.dm" #include "code\__HELPERS\config.dm" @@ -193,6 +192,7 @@ #include "code\__HELPERS\mobs.dm" #include "code\__HELPERS\mouse_control.dm" #include "code\__HELPERS\names.dm" +#include "code\__HELPERS\path.dm" #include "code\__HELPERS\priority_announce.dm" #include "code\__HELPERS\pronouns.dm" #include "code\__HELPERS\qdel.dm" From 7bf1bcf8573536aded62d352802f9bed012d6974 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Sat, 5 Mar 2022 16:18:17 -0300 Subject: [PATCH 06/22] Some updates --- code/__HELPERS/path.dm | 26 +++++++++++++------ .../changeling/powers/tiny_prick.dm | 4 +-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/code/__HELPERS/path.dm b/code/__HELPERS/path.dm index 96becc1a59..7ae72c3fa2 100644 --- a/code/__HELPERS/path.dm +++ b/code/__HELPERS/path.dm @@ -5,7 +5,8 @@ */ /** - * This is the proc you use whenever you want to have pathfinding more complex than "try stepping towards the thing" + * This is the proc you use whenever you want to have pathfinding more complex than "try stepping towards the thing". + * If no path was found, returns an empty list, which is important for bots like medibots who expect an empty list rather than nothing. * * Arguments: * * caller: The movable atom that's trying to find the path @@ -15,8 +16,9 @@ * * id: An ID card representing what access we have and what doors we can open. Its location relative to the pathing atom is irrelevant * * simulated_only: Whether we consider turfs without atmos simulation (AKA do we want to ignore space) * * exclude: If we want to avoid a specific turf, like if we're a mulebot who already got blocked by some turf + * * skip_first: Whether or not to delete the first item in the path. This would be done because the first item is the starting tile, which can break movement for some creatures. */ -/proc/get_path_to(caller, end, max_distance = 30, mintargetdist, id=null, simulated_only = TRUE, turf/exclude) +/proc/get_path_to(caller, end, max_distance = 30, mintargetdist, id=null, simulated_only = TRUE, turf/exclude, skip_first=TRUE) if(!caller || !get_turf(end)) return @@ -33,12 +35,14 @@ SSpathfinder.mobs.found(l) if(!path) path = list() + if(length(path) > 0 && skip_first) + path.Cut(1,2) return path /** * A helper macro to see if it's possible to step from the first turf into the second one, minding things like door access and directional windows. - * Note that this can only be used inside the [datum/pathfind][pathfind datum] since it uses variables from said datum - * If you really want to optimize things, optimize this, cuz this gets called a lot + * Note that this can only be used inside the [datum/pathfind][pathfind datum] since it uses variables from said datum. + * If you really want to optimize things, optimize this, cuz this gets called a lot. */ #define CAN_STEP(cur_turf, next) (next && !next.density && cur_turf.Adjacent(next) && !(simulated_only && SSpathfinder.space_type_cache[next.type]) && !cur_turf.LinkBlockedWithAccess(next,caller, id) && (next != avoid)) /// Another helper macro for JPS, for telling when a node has forced neighbors that need expanding @@ -128,16 +132,21 @@ src.simulated_only = simulated_only src.avoid = avoid -/// The proc you use to run the search, returns FALSE if it's invalid, an empty list if no path could be found, or a valid path to the target +/** + * search() is the proc you call to kick off and handle the actual pathfinding, and kills the pathfind datum instance when it's done. + * + * If a valid path was found, it's returned as a list. If invalid or cross-z-level params are entered, or if there's no valid path found, we + * return null, which [/proc/get_path_to] translates to an empty list (notable for simple bots, who need empty lists) + */ /datum/pathfind/proc/search() start = get_turf(caller) if(!start || !end) stack_trace("Invalid A* start or destination") - return FALSE + return if(start.z != end.z || start == end ) //no pathfinding between z levels - return FALSE + return if(max_distance && (max_distance < get_dist(start, end))) //if start turf is farther than max_distance from end turf, no need to do anything - return FALSE + return //initialization var/datum/jps_node/current_processed_node = new (start, -1, 0, end) @@ -165,6 +174,7 @@ if(path) for(var/i = 1 to round(0.5 * length(path))) path.Swap(i, length(path) - i + 1) + sources = null qdel(open) return path diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm index d69c3cc65a..2e684b514b 100644 --- a/code/modules/antagonists/changeling/powers/tiny_prick.dm +++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm @@ -47,8 +47,8 @@ return if(!isturf(user.loc)) return - if(!get_path_to(user, target, max_distance = changeling.sting_range, simulated_only = FALSE)) - return + if(!length(get_path_to(user, target, max_distance = changeling.sting_range, simulated_only = FALSE))) + return // no path within the sting's range is found. what a weird place to use the pathfinding system return 1 /obj/effect/proc_holder/changeling/sting/sting_feedback(mob/user, mob/target) From 062da9bb05016ec0a250a3e29a6c5039ca2dbf67 Mon Sep 17 00:00:00 2001 From: Putnam3145 Date: Sun, 6 Mar 2022 13:18:59 -0800 Subject: [PATCH 07/22] fixed dynamic days (lowertext) --- code/modules/holiday/dynamic.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/holiday/dynamic.dm b/code/modules/holiday/dynamic.dm index fb50ea72ca..f75dd37542 100644 --- a/code/modules/holiday/dynamic.dm +++ b/code/modules/holiday/dynamic.dm @@ -3,7 +3,7 @@ /datum/holiday/dynamic/shouldCelebrate(dd, mm, yy, ww, ddd) var/list/days = CONFIG_GET(keyed_list/dynamic_mode_days) - return ddd in days + return lowertext(ddd) in days /datum/holiday/dynamic/celebrate() GLOB.dynamic_forced_threat_level = rand(90, 100) From 993db662377489f455cc4597bf9083cb1598778f Mon Sep 17 00:00:00 2001 From: Putnam3145 Date: Sun, 6 Mar 2022 14:54:48 -0800 Subject: [PATCH 08/22] Adds a LOT more enthalpy to tritium --- code/modules/atmospherics/auxgm/gas_types.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/atmospherics/auxgm/gas_types.dm b/code/modules/atmospherics/auxgm/gas_types.dm index aaf3945aac..6ee234d64d 100644 --- a/code/modules/atmospherics/auxgm/gas_types.dm +++ b/code/modules/atmospherics/auxgm/gas_types.dm @@ -129,7 +129,7 @@ heat_penalty = 10 transmit_modifier = 30 fire_products = list(GAS_H2O = 1) - enthalpy = 40000 + enthalpy = 300000 fire_burn_rate = 2 fire_radiation_released = 50 // arbitrary number, basically 60 moles of trit burning will just barely start to harm you fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50 From 1f00f61380715dc642e330ca890466236031c6e9 Mon Sep 17 00:00:00 2001 From: Timothy Teakettle <59849408+timothyteakettle@users.noreply.github.com> Date: Mon, 7 Mar 2022 19:45:27 +0000 Subject: [PATCH 09/22] Update plating.dm --- code/game/turfs/simulated/floor/plating.dm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index c831faca61..ce105f93d2 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -125,8 +125,11 @@ if(istype(W, /obj/item/stack/tile/material)) var/turf/newturf = PlaceOnTop(/turf/open/floor/material, flags = CHANGETURF_INHERIT_AIR) newturf.set_custom_materials(W.custom_materials) + newturf.add_atom_colour(C.atom_colours[WASHABLE_COLOUR_PRIORITY], FIXED_COLOUR_PRIORITY) else if(W.turf_type) var/turf/open/floor/T = PlaceOnTop(W.turf_type, flags = CHANGETURF_INHERIT_AIR) + if(length(C.atom_colours) && C.atom_colours[WASHABLE_COLOUR_PRIORITY] != null) + T.add_atom_colour(C.atom_colours[WASHABLE_COLOUR_PRIORITY], FIXED_COLOUR_PRIORITY) if(istype(W, /obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow var/obj/item/stack/tile/light/L = W var/turf/open/floor/light/F = T From 59451d90883068bb3a56027c346467e034781155 Mon Sep 17 00:00:00 2001 From: Timothy Teakettle <59849408+timothyteakettle@users.noreply.github.com> Date: Mon, 7 Mar 2022 19:45:58 +0000 Subject: [PATCH 10/22] Update plating.dm --- code/game/turfs/simulated/floor/plating.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index ce105f93d2..b23ce17c72 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -125,7 +125,8 @@ if(istype(W, /obj/item/stack/tile/material)) var/turf/newturf = PlaceOnTop(/turf/open/floor/material, flags = CHANGETURF_INHERIT_AIR) newturf.set_custom_materials(W.custom_materials) - newturf.add_atom_colour(C.atom_colours[WASHABLE_COLOUR_PRIORITY], FIXED_COLOUR_PRIORITY) + if(length(C.atom_colours) && C.atom_colours[WASHABLE_COLOUR_PRIORITY] != null) + newturf.add_atom_colour(C.atom_colours[WASHABLE_COLOUR_PRIORITY], FIXED_COLOUR_PRIORITY) else if(W.turf_type) var/turf/open/floor/T = PlaceOnTop(W.turf_type, flags = CHANGETURF_INHERIT_AIR) if(length(C.atom_colours) && C.atom_colours[WASHABLE_COLOUR_PRIORITY] != null) From e717e21b26b4b1408849e7fe16799dc4f85189a7 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Tue, 8 Mar 2022 00:37:05 +0000 Subject: [PATCH 11/22] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-15541.yml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-15541.yml diff --git a/html/changelogs/AutoChangeLog-pr-15541.yml b/html/changelogs/AutoChangeLog-pr-15541.yml deleted file mode 100644 index 3fdeb4f594..0000000000 --- a/html/changelogs/AutoChangeLog-pr-15541.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "TripleShades" -delete-after: True -changes: - - rscdel: "[Omega] Hundreds of decals -tweak: [Omega] Three airlock rooms to space dont have vents now to stop tiles ripping up" From 5bee0119fd29992a9521c372f1ca020f44f38dfa Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Mon, 7 Mar 2022 20:42:31 -0600 Subject: [PATCH 12/22] Automatic changelog generation for PR #15538 [ci skip] --- html/changelogs/AutoChangeLog-pr-15538.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-15538.yml diff --git a/html/changelogs/AutoChangeLog-pr-15538.yml b/html/changelogs/AutoChangeLog-pr-15538.yml new file mode 100644 index 0000000000..3a7439e42d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-15538.yml @@ -0,0 +1,4 @@ +author: "Putnam3145" +delete-after: True +changes: + - config: "Made a config entry lowertext thing actually work right" From 9aded1bc3c76f90b50a80e26af01abf5e358acf3 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Wed, 9 Mar 2022 00:36:18 +0000 Subject: [PATCH 13/22] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-15538.yml | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-15538.yml diff --git a/html/changelogs/AutoChangeLog-pr-15538.yml b/html/changelogs/AutoChangeLog-pr-15538.yml deleted file mode 100644 index 3a7439e42d..0000000000 --- a/html/changelogs/AutoChangeLog-pr-15538.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Putnam3145" -delete-after: True -changes: - - config: "Made a config entry lowertext thing actually work right" From 357e11cd84ae094b15760ba15574610ddcae7220 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Wed, 9 Mar 2022 23:49:44 -0300 Subject: [PATCH 14/22] Better fix of this big stupid --- .../modules/mining/equipment/kinetic_crusher.dm | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm b/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm index e2013cb73f..03624416f2 100644 --- a/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm +++ b/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm @@ -120,15 +120,27 @@ new /obj/effect/temp_visual/kinetic_blast(target) playsound(target.loc, 'sound/weapons/kenetic_accel.ogg', 60, 0) +// I'm gonna be honest, i cannot trust admins to be smart about it. +/obj/item/crusher_trophy/blaster_tubes/mask/vv_edit_var(var_name, var_value) + switch(var_name) + if(NAMEOF(src, bonus_value)) + if(istype(loc, /obj/item/kinetic_crusher)) + var/datum/component/two_handed/TH = H.GetComponent(/datum/component/two_handed) + TH.force_wielded -= bonus_value + TH.force_wielded += var_value + . = ..() + /obj/item/crusher_trophy/blaster_tubes/mask/add_to(obj/item/kinetic_crusher/H, mob/living/user) . = ..() if(.) - H.force += bonus_value + var/datum/component/two_handed/TH = H.GetComponent(/datum/component/two_handed) + TH.force_wielded += bonus_value /obj/item/crusher_trophy/blaster_tubes/mask/remove_from(obj/item/kinetic_crusher/H, mob/living/user) . = ..() if(.) - H.force -= bonus_value + var/datum/component/two_handed/TH = H.GetComponent(/datum/component/two_handed) + TH.force_wielded -= bonus_value //lava imp /obj/item/crusher_trophy/blaster_tubes/impskull From 1825d224bf554935cd42f0cda6d2cb53fc514df5 Mon Sep 17 00:00:00 2001 From: SandPoot Date: Wed, 9 Mar 2022 23:56:02 -0300 Subject: [PATCH 15/22] Fuck --- modular_sand/code/modules/mining/equipment/kinetic_crusher.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm b/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm index 03624416f2..f1af8ecdec 100644 --- a/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm +++ b/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm @@ -125,7 +125,7 @@ switch(var_name) if(NAMEOF(src, bonus_value)) if(istype(loc, /obj/item/kinetic_crusher)) - var/datum/component/two_handed/TH = H.GetComponent(/datum/component/two_handed) + var/datum/component/two_handed/TH = loc.GetComponent(/datum/component/two_handed) TH.force_wielded -= bonus_value TH.force_wielded += var_value . = ..() From efc541692d726bf8b16920cb0c0d04dd3ea31f33 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 10 Mar 2022 17:46:49 -0600 Subject: [PATCH 16/22] Automatic changelog generation for PR #15520 [ci skip] --- html/changelogs/AutoChangeLog-pr-15520.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-15520.yml diff --git a/html/changelogs/AutoChangeLog-pr-15520.yml b/html/changelogs/AutoChangeLog-pr-15520.yml new file mode 100644 index 0000000000..6de9ce0005 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-15520.yml @@ -0,0 +1,5 @@ +author: "Daggertail" +delete-after: True +changes: + - code_imp: "Updated Python BS to 3.7.9" + - code_imp: "Changed some Mapmerge lines" From d8a91fb3a57c4f2c0059d0ead3da2ad030ce82ee Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 10 Mar 2022 17:47:17 -0600 Subject: [PATCH 17/22] Automatic changelog generation for PR #15540 [ci skip] --- html/changelogs/AutoChangeLog-pr-15540.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-15540.yml diff --git a/html/changelogs/AutoChangeLog-pr-15540.yml b/html/changelogs/AutoChangeLog-pr-15540.yml new file mode 100644 index 0000000000..0555a089eb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-15540.yml @@ -0,0 +1,4 @@ +author: "Putnam3145" +delete-after: True +changes: + - balance: "Massively buffed trit fires (and nerfed trit-*generating* fires equally)" From 5b0d0ef02d84acc9a338c22560df9d013729c07b Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 10 Mar 2022 17:50:31 -0600 Subject: [PATCH 18/22] Automatic changelog generation for PR #15534 [ci skip] --- html/changelogs/AutoChangeLog-pr-15534.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-15534.yml diff --git a/html/changelogs/AutoChangeLog-pr-15534.yml b/html/changelogs/AutoChangeLog-pr-15534.yml new file mode 100644 index 0000000000..c73d7c5ea7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-15534.yml @@ -0,0 +1,5 @@ +author: "SandPoot" +delete-after: True +changes: + - rscadd: "Screentips." + - rscdel: "Removed the bottom grey section of the screen." From 6d3fe538ba581d517e6cfc3ddd7fe73cfa9f9129 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 10 Mar 2022 17:51:02 -0600 Subject: [PATCH 19/22] Automatic changelog generation for PR #15535 [ci skip] --- html/changelogs/AutoChangeLog-pr-15535.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-15535.yml diff --git a/html/changelogs/AutoChangeLog-pr-15535.yml b/html/changelogs/AutoChangeLog-pr-15535.yml new file mode 100644 index 0000000000..5202058e85 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-15535.yml @@ -0,0 +1,4 @@ +author: "SandPoot" +delete-after: True +changes: + - refactor: "Updated statpanel." From a307150afdfd44ab50de8d3e0e5816a4024a03f2 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 10 Mar 2022 17:51:10 -0600 Subject: [PATCH 20/22] Automatic changelog generation for PR #15536 [ci skip] --- html/changelogs/AutoChangeLog-pr-15536.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-15536.yml diff --git a/html/changelogs/AutoChangeLog-pr-15536.yml b/html/changelogs/AutoChangeLog-pr-15536.yml new file mode 100644 index 0000000000..44d0f94c68 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-15536.yml @@ -0,0 +1,4 @@ +author: "SandPoot" +delete-after: True +changes: + - refactor: "Implements JPS Pathfinding!" From 69389daa3b9ccd003b3febe1e8157924fc4463e2 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 10 Mar 2022 17:51:20 -0600 Subject: [PATCH 21/22] Automatic changelog generation for PR #15543 [ci skip] --- html/changelogs/AutoChangeLog-pr-15543.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-15543.yml diff --git a/html/changelogs/AutoChangeLog-pr-15543.yml b/html/changelogs/AutoChangeLog-pr-15543.yml new file mode 100644 index 0000000000..2068bf7b12 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-15543.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - rscadd: "spraypainting a stack of floor tiles makes the placed tile the same colour" From 67fb160e664c9746baa559e46b8e60c5a37376df Mon Sep 17 00:00:00 2001 From: Changelogs Date: Fri, 11 Mar 2022 00:39:15 +0000 Subject: [PATCH 22/22] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-15520.yml | 5 ----- html/changelogs/AutoChangeLog-pr-15534.yml | 5 ----- html/changelogs/AutoChangeLog-pr-15535.yml | 4 ---- html/changelogs/AutoChangeLog-pr-15536.yml | 4 ---- html/changelogs/AutoChangeLog-pr-15540.yml | 4 ---- html/changelogs/AutoChangeLog-pr-15543.yml | 4 ---- 6 files changed, 26 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-15520.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-15534.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-15535.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-15536.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-15540.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-15543.yml diff --git a/html/changelogs/AutoChangeLog-pr-15520.yml b/html/changelogs/AutoChangeLog-pr-15520.yml deleted file mode 100644 index 6de9ce0005..0000000000 --- a/html/changelogs/AutoChangeLog-pr-15520.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Daggertail" -delete-after: True -changes: - - code_imp: "Updated Python BS to 3.7.9" - - code_imp: "Changed some Mapmerge lines" diff --git a/html/changelogs/AutoChangeLog-pr-15534.yml b/html/changelogs/AutoChangeLog-pr-15534.yml deleted file mode 100644 index c73d7c5ea7..0000000000 --- a/html/changelogs/AutoChangeLog-pr-15534.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "SandPoot" -delete-after: True -changes: - - rscadd: "Screentips." - - rscdel: "Removed the bottom grey section of the screen." diff --git a/html/changelogs/AutoChangeLog-pr-15535.yml b/html/changelogs/AutoChangeLog-pr-15535.yml deleted file mode 100644 index 5202058e85..0000000000 --- a/html/changelogs/AutoChangeLog-pr-15535.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SandPoot" -delete-after: True -changes: - - refactor: "Updated statpanel." diff --git a/html/changelogs/AutoChangeLog-pr-15536.yml b/html/changelogs/AutoChangeLog-pr-15536.yml deleted file mode 100644 index 44d0f94c68..0000000000 --- a/html/changelogs/AutoChangeLog-pr-15536.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SandPoot" -delete-after: True -changes: - - refactor: "Implements JPS Pathfinding!" diff --git a/html/changelogs/AutoChangeLog-pr-15540.yml b/html/changelogs/AutoChangeLog-pr-15540.yml deleted file mode 100644 index 0555a089eb..0000000000 --- a/html/changelogs/AutoChangeLog-pr-15540.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Putnam3145" -delete-after: True -changes: - - balance: "Massively buffed trit fires (and nerfed trit-*generating* fires equally)" diff --git a/html/changelogs/AutoChangeLog-pr-15543.yml b/html/changelogs/AutoChangeLog-pr-15543.yml deleted file mode 100644 index 2068bf7b12..0000000000 --- a/html/changelogs/AutoChangeLog-pr-15543.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "timothyteakettle" -delete-after: True -changes: - - rscadd: "spraypainting a stack of floor tiles makes the placed tile the same colour"