diff --git a/citadel.dme b/citadel.dme index 97305cff74d..79af566be9b 100644 --- a/citadel.dme +++ b/citadel.dme @@ -79,6 +79,7 @@ #include "code\__DEFINES\shields.dm" #include "code\__DEFINES\shuttle.dm" #include "code\__DEFINES\shuttles.dm" +#include "code\__DEFINES\skin.dm" #include "code\__DEFINES\sonar.dm" #include "code\__DEFINES\spaceman_dmm.dm" #include "code\__DEFINES\spans.dm" @@ -4210,6 +4211,8 @@ #include "interface\interface.dm" #include "interface\stylesheet.dm" #include "interface\skin.dmf" +#include "interface\menus\_menus.dm" +#include "interface\menus\main.dm" #include "maps\Generic\death_triggers.dm" #include "maps\Generic\map_data.dm" #include "maps\Generic\misc.dm" diff --git a/code/__DEFINES/_planes+layers.dm b/code/__DEFINES/_planes+layers.dm index 469a38acb53..8c1c48fb6a9 100644 --- a/code/__DEFINES/_planes+layers.dm +++ b/code/__DEFINES/_planes+layers.dm @@ -50,10 +50,10 @@ What is the naming convention for planes or layers? /// Reserved for use in space/parallax #define PARALLAX_PLANE -90 /// everything layering below -#define PARALLAX_VIS_LAYER_BELOW -10000 +#define PARALLAX_VIS_LAYER_BELOW -100 #define PARALLAX_LAYER_CENTER 0 /// ditto -#define PARALLAX_VIS_LAYER_ABOVE 10000 +#define PARALLAX_VIS_LAYER_ABOVE 100 ///For the Looking Glass holodecks #define PLANE_LOOKINGGLASS -77 ///For the Looking Glass holodecks diff --git a/code/__DEFINES/skin.dm b/code/__DEFINES/skin.dm new file mode 100644 index 00000000000..ae37f4970e0 --- /dev/null +++ b/code/__DEFINES/skin.dm @@ -0,0 +1,54 @@ +//? WINCLONE IDs; NO SKIN CONTROL MAY HAVE THESE IDs, OR WINCLONE WILL NOT CREATE "NAKED" CONTROLS OF THIS TYPE! +#define SKIN_ID_ABSTRACT_MENU "menu" +#define SKIN_ID_ABSTRACT_WINDOW "window" +#define SKIN_ID_ABSTRACT_PANE "pane" +#define SKIN_ID_ABSTRACT_MACRO "macro" + +//? SKIN ELEMENT IDS; MAKE SURE TO UPDATE THESE WHEN CHANGING SKIN.DMF. +//! Window IDs +/// main game window +#define SKIN_WINDOW_ID_MAIN "mainwindow" + +//! Map IDs +/// main game viewport +#define SKIN_MAP_ID_VIEWPORT "mapwindow.map" + +//! Splitter IDs +/// main window split +#define SKIN_SPLITTER_ID_MAIN "mainwindow.split" + +//? Dynamic skin IDs used by code-generated elements +//? button groups MUST have unique ids to all skin elements! +// TOP LEVEL MENU // +#define SKIN_ID_MAIN_MENU "menu_main" + // what it attaches to + #define SKIN_ID_MAIN_MENU_ATTACH_TO "mainwindow" + // categories + #define SKIN_ID_MENU_CATEGORY_FILE "menu_file" + #define SKIN_ID_MENU_CATEGORY_ZOOM "menu_zoom" + #define SKIN_ID_MENU_CATEGORY_SCALING "menu_scaling" + #define SKIN_ID_MENU_CATEGORY_HELP "menu_help" + // groups + #define SKIN_BUTTON_GROUP_MAP_ZOOM "group_map_zoom" + #define SKIN_BUTTON_GROUP_MAP_SCALING "group_map_scaling" + #define SKIN_BUTTON_GROUP_MAP_WIDESCREEN "group_map_widescreen" + // buttons - file + #define SKIN_ID_MENU_BUTTON_SCREENSHOT "menubutton_screenshot" + #define SKIN_ID_MENU_BUTTON_SCREENSHOT_QUICK "menubutton_screenshot_quick" + #define SKIN_ID_MENU_BUTTON_RECONNECT "menubutton_reconnect" + #define SKIN_ID_MENU_BUTTON_PING "menubutton_ping" + #define SKIN_ID_MENU_BUTTON_QUIT "menubutton_quit" + // buttons - size + #define SKIN_ID_MENU_BUTTON_STRETCH_TO_FIT "menubutton_stretchtofit" + #define SKIN_ID_MENU_BUTTON_STRETCH_NO_LETTERBOX "menubutton_stretchtofill" + #define SKIN_ID_MENU_BUTTON_FOR_RESOLUTION(resolution) "menubutton_[resolution]x[resolution]" + #define SKIN_ID_MENU_BUTTON_WIDESCREEN_ENABLED "menubutton_widescreen_on" + #define SKIN_ID_MENU_BUTTON_WIDESCREEN_DISABLED "menubutton_widescreen_off" + #define SKIN_ID_MENU_BUTTON_AUTO_FIT_VIEWPORT "menubutton_auto_fit_viewport" + #define SKIN_ID_MENU_BUTTON_FIT_VIEWPORT "menubutton_fit_viewport" + // buttons - scaling + #define SKIN_ID_MENU_BUTTON_NEAREST_NEIGHBOR "menubutton_scale_distort" + #define SKIN_ID_MENU_BUTTON_POINT_SAMPLE "menubutton_scale_normal" + #define SKIN_ID_MENU_BUTTON_BILINEAR "menubutton_scale_blur" + // buttons - help + #define SKIN_ID_MENU_BUTTON_ADMINHELP "menubutton_adminhelp" diff --git a/code/__HELPERS/view.dm b/code/__HELPERS/view.dm index 0ea599b9800..a31533592d7 100644 --- a/code/__HELPERS/view.dm +++ b/code/__HELPERS/view.dm @@ -1,167 +1,3 @@ -/** - * This is intended to be a full wrapper. DO NOT directly modify its values. - * Container for client viewsize. - */ -/datum/view_data - /// Width offset to apply to the default view string if we're not supressed for some reason. - var/width = 0 - /// Height offset to apply to the default view string, see above. - var/height = 0 - /** - * This client's current "default" view, in the format "WidthxHeight". - * We add/remove from this when we want to change their window size. - */ - var/default = "" - /** - * This client's current zoom level, if it's not being supressed. - * If it's 0, we autoscale to the size of the window. Otherwise it's treated as the ratio between - * the pixels on the map and output pixels. Only looks proper nice in increments of whole numbers (iirc). - * Stored here so other parts of the code have a non blocking way of getting a user's functional zoom. - */ - var/zoom = 0 - /** - * If the view is currently being supressed by some other "monitor". - * For when you want to own the client's eye without fucking with their viewport. - * Doesn't make sense for a binocoler to effect your view in a camera console. - */ - var/is_suppressed = FALSE - /// The client that owns this view packet. - var/client/chief = null - -/datum/view_data/New(client/owner, view_string) - default = view_string - chief = owner - apply() - -/datum/view_data/proc/setDefault(string) - default = string - apply() - -/datum/view_data/proc/safeApplyFormat() - if(isZooming()) - assertFormat() - return - resetFormat() - -/// T-Pose -/datum/view_data/proc/assertFormat() -// if(!is_preference_enabled(/datum/client_preference/scaling_viewport)) -// return - // winset(chief, "mapwindow.map", "zoom=0") - // We're using icon dropdown instead - -/// Cuck -/datum/view_data/proc/resetFormat() -// if(!is_preference_enabled(/datum/client_preference/scaling_viewport)) -// return - // winset(chief, "mapwindow.map", "zoom=[chief.prefs.pixel_size]") - // We're using icon dropdown instead - -/datum/view_data/proc/setZoomMode() -// if(!is_preference_enabled(/datum/client_preference/scaling_viewport)) -// return - // winset(chief, "mapwindow.map", "zoom-mode=[chief.prefs.scaling_method]") - // We're using icon dropdown instead - -/datum/view_data/proc/isZooming() - return (width || height) - -/datum/view_data/proc/resetToDefault() - width = 0 - height = 0 - apply() - -/datum/view_data/proc/add(toAdd) - width += toAdd - height += toAdd - apply() - -/datum/view_data/proc/addTo(toAdd) - var/list/shitcode = getviewsize(toAdd) - width += shitcode[1] - height += shitcode[2] - apply(toAdd) - -/datum/view_data/proc/setTo(toAdd) - /** - * Backward compatability to account. - * For a change in how sizes get calculated. we used to include world.view in - * This, but it was jank, so I had to move it. - */ - var/list/shitcode = getviewsize(toAdd) - width = shitcode[1] - height = shitcode[2] - apply(toAdd) - -/datum/view_data/proc/setBoth(wid, hei) - width = wid - height = hei - apply() - -/datum/view_data/proc/setWidth(wid) - width = wid - apply() - -/datum/view_data/proc/setHeight(hei) - width = hei - apply() - -/datum/view_data/proc/addToWidth(toAdd) - width += toAdd - apply() - -/datum/view_data/proc/addToHeight(screen, toAdd) - height += toAdd - apply() - -/datum/view_data/proc/apply(forced) - if(!chief.is_preference_enabled(/datum/client_preference/scaling_viewport) && !forced) - return - chief.change_view(getView(), forced) - safeApplyFormat() - if(chief.prefs.auto_fit_viewport) - chief.fit_viewport() - -/datum/view_data/proc/supress() - is_suppressed = TRUE - apply() - -/datum/view_data/proc/unsupress() - is_suppressed = FALSE - apply() - -/datum/view_data/proc/getView() - var/list/temp = getviewsize(default) - if(is_suppressed) - return "[temp[1]]x[temp[2]]" - return "[width + temp[1]]x[height + temp[2]]" - -/datum/view_data/proc/zoomIn() - resetToDefault() - animate(chief, pixel_x = 0, pixel_y = 0, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW) - -/datum/view_data/proc/zoomOut(radius = 0, offset = 0, direction = FALSE) - if(direction) - var/_x = 0 - var/_y = 0 - switch(direction) - if(NORTH) - _y = offset - if(EAST) - _x = offset - if(SOUTH) - _y = -offset - if(WEST) - _x = -offset - animate(chief, pixel_x = world.icon_size*_x, pixel_y = world.icon_size*_y, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW) - // Ready for this one? - setTo(radius) -/* -/proc/getScreenSize(widescreen) - if(widescreen) - return CONFIG_GET(string/default_view) - return CONFIG_GET(string/default_view_square) -*/ /proc/getviewsize(view) if(isnum(view)) var/totalviewrange = 1 + 2 * view @@ -169,9 +5,3 @@ else var/list/viewrangelist = splittext(view,"x") return list(text2num(viewrangelist[1]), text2num(viewrangelist[2])) - -/proc/in_view_range(mob/user, atom/A) - var/list/view_range = getviewsize(user.client.view) - var/turf/source = get_turf(user) - var/turf/target = get_turf(A) - return ISINRANGE(target.x, source.x - view_range[1], source.x + view_range[1]) && ISINRANGE(target.y, source.y - view_range[1], source.y + view_range[1]) diff --git a/code/__global_init.dm b/code/__global_init.dm index 08cc2aa8b61..cafa0772844 100644 --- a/code/__global_init.dm +++ b/code/__global_init.dm @@ -24,3 +24,12 @@ var/world_log_redirected = FALSE /datum/world_log_shunter/New() world.ensure_logging_active() + +//! Debugging +var/datum/world_debug_enabler/world_debug_enabler = new + +/datum/world_debug_enabler/New() + var/debug_server = world.GetConfig("env", "AUXTOOLS_DEBUG_DLL") + if (debug_server) + call(debug_server, "auxtools_init")() + enable_debugging() diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index eb8e7ee2c2f..d66d5c6d469 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -6,6 +6,7 @@ adjacency code. */ +// todo: unify with normal click procs /mob/living/silicon/robot/ClickOn(var/atom/A, var/params) if(world.time <= next_click) return @@ -15,23 +16,23 @@ build_click(src, client.buildmode, params, A) return - var/list/modifiers = params2list(params) - if(modifiers["shift"] && modifiers["ctrl"]) + var/list/unpacked_params = params2list(params) + if(unpacked_params["shift"] && unpacked_params["ctrl"]) CtrlShiftClickOn(A) return - if(modifiers["shift"] && modifiers["middle"]) + if(unpacked_params["shift"] && unpacked_params["middle"]) ShiftMiddleClickOn(A) return - if(modifiers["middle"]) + if(unpacked_params["middle"]) MiddleClickOn(A) return - if(modifiers["shift"]) + if(unpacked_params["shift"]) ShiftClickOn(A) return - if(modifiers["alt"]) // alt and alt-gr (rightalt) + if(unpacked_params["alt"]) // alt and alt-gr (rightalt) AltClickOn(A) return - if(modifiers["ctrl"]) + if(unpacked_params["ctrl"]) CtrlClickOn(A) return @@ -58,49 +59,47 @@ return */ - var/obj/item/W = get_active_held_item() + //? Grab click semantics + var/obj/item/I = get_active_held_item() + //? Core cyborg code // Cyborgs have no range-checking unless there is item use - if(!W) + if(!I) if(bolt && !bolt.malfunction && A.loc != module) return A.add_hiddenprint(src) A.attack_robot(src) return - // buckled cannot prevent machine interlinking but stops arm movement - if( buckled ) + //? Handle special cases + if(I == A) + // attack_self + I.attack_self(src) + // todo: refactor + trigger_aiming(TARGET_CAN_CLICK) return - if(W == A) + //? check if we can click from our current location + var/ranged_generics_allowed = loc?.AllowClick(src, A, I) - W.attack_self(src) - return - - // cyborgs are prohibited from using storage items so we can I think safely remove (A.loc in contents) - if(A == loc || (A in loc) || (A in contents)) - // No adjacency checks - - var/resolved = A.attackby(W, src, params) - if(!resolved && A && W) - W.afterattack(A,src,1,params) - return - - if(!isturf(loc)) - return - - // cyborgs are prohibited from using storage items so we can I think safely remove (A.loc && isturf(A.loc.loc)) - if(isturf(A) || isturf(A.loc)) - if(A.Adjacent(src)) // see adjacent.dm - - var/resolved = A.attackby(W, src, params) - if(!resolved && A && W) - W.afterattack(A, src, 1, params) - return + if(Reachability(A, null, I?.reach, I)) + //? attempt melee attack chain + if(I) + I.melee_attack_chain(A, src, CLICKCHAIN_HAS_PROXIMITY, unpacked_params) else - W.afterattack(A, src, 0, params) - return - return + melee_attack_chain(A, CLICKCHAIN_HAS_PROXIMITY, unpacked_params) + // todo: refactor aiming + trigger_aiming(TARGET_CAN_CLICK) + return + else if(ranged_generics_allowed) + //? attempt ranged attack chain + if(I) + I.ranged_attack_chain(A, src, NONE, params) + else + ranged_attack_chain(A, NONE, unpacked_params) + // todo: refactor aiming + trigger_aiming(TARGET_CAN_CLICK) + return //Middle click cycles through selected modules. /mob/living/silicon/robot/MiddleClickOn(var/atom/A) diff --git a/code/_onclick/hud/_screen_object.dm b/code/_onclick/hud/_screen_object.dm index 2f8c9178b61..78c80cd123e 100644 --- a/code/_onclick/hud/_screen_object.dm +++ b/code/_onclick/hud/_screen_object.dm @@ -9,7 +9,7 @@ /atom/movable/screen name = "" icon = 'icons/mob/screen1.dmi' - appearance_flags = TILE_BOUND|PIXEL_SCALE|NO_CLIENT_COLOR + appearance_flags = PIXEL_SCALE | NO_CLIENT_COLOR layer = LAYER_HUD_BASE plane = PLANE_PLAYER_HUD var/obj/master = null //A reference to the object in the slot. Grabs or items, generally. diff --git a/code/_onclick/hud/ability_screen_objects.dm b/code/_onclick/hud/ability_screen_objects.dm index e6e4a2db14c..aef68a6f5a3 100644 --- a/code/_onclick/hud/ability_screen_objects.dm +++ b/code/_onclick/hud/ability_screen_objects.dm @@ -176,8 +176,6 @@ /mob/Login() . = ..() - if(client?.is_preference_enabled(/datum/client_preference/scaling_viewport)) - INVOKE_ASYNC(client, /client.verb/SetWindowIconSize, client.prefs.icon_size) if(ability_master) ability_master.toggle_open(1) diff --git a/code/_rendering/clickcatcher/clickcatcher.dm b/code/_rendering/clickcatcher/clickcatcher.dm index 6ea14a666e0..92f25672ec5 100644 --- a/code/_rendering/clickcatcher/clickcatcher.dm +++ b/code/_rendering/clickcatcher/clickcatcher.dm @@ -31,7 +31,9 @@ */ /atom/movable/screen/click_catcher/proc/UpdateFill(view_size_x, view_size_y) - screen_loc = "1,1 to [view_size_x],[view_size_y]" + var/matrix/transforming = matrix() + transforming.Scale(view_size_x, view_size_y) + transform = transforming /atom/movable/screen/click_catcher/Click(location, control, params) var/list/modifiers = params2list(params) @@ -63,12 +65,8 @@ /** * Makes a clickcatcher if necessary, and ensures it's fit to our size. */ -/client/proc/update_clickcatcher(list/view_override) +/client/proc/update_clickcatcher() if(!click_catcher) click_catcher = new screen |= click_catcher - if(view_override) - click_catcher.UpdateFill(view_override[1], view_override[2]) - else - var/list/view_list = getviewsize(view) - click_catcher.UpdateFill(view_list[1], view_list[2]) + click_catcher.UpdateFill(current_viewport_width, current_viewport_height) diff --git a/code/_rendering/mob.dm b/code/_rendering/mob.dm index 4035b71cce2..413af28f7d1 100644 --- a/code/_rendering/mob.dm +++ b/code/_rendering/mob.dm @@ -14,6 +14,16 @@ reload_fullscreen() client.update_clickcatcher() + INVOKE_ASYNC(client, /client/proc/init_viewport_blocking) + +/** + * reloads rendering after screen viewport size change + */ +/mob/proc/refit_rendering() + client?.parallax_holder?.Reset(force = TRUE) + reload_fullscreen() + client?.update_clickcatcher() + /** * destroys screen rendering. call on mob del */ diff --git a/code/_rendering/parallax/parallax_holder.dm b/code/_rendering/parallax/parallax_holder.dm index c2863b2f37b..20c436c35ce 100644 --- a/code/_rendering/parallax/parallax_holder.dm +++ b/code/_rendering/parallax/parallax_holder.dm @@ -175,6 +175,8 @@ continue L.SetView(C.view, TRUE) . |= L + if(vis_holder) + . |= vis_holder C.screen |= . if(!secondary_map) var/atom/movable/screen/plane_master/parallax_white/PM = locate() in C.screen @@ -191,6 +193,7 @@ if(QDELETED(C)) return C.screen -= layers + C.screen -= vis_holder if(!secondary_map) var/atom/movable/screen/plane_master/parallax_white/PM = locate() in C.screen if(PM) @@ -311,4 +314,5 @@ parallax_holder = new(src) /atom/movable/screen/parallax_vis - screen_loc = "CENTER,CENTER" + screen_loc = "LEFT,BOTTOM" + icon = null diff --git a/code/_rendering/parallax/parallax_object.dm b/code/_rendering/parallax/parallax_object.dm index e0c909fba6c..f807d1b3798 100644 --- a/code/_rendering/parallax/parallax_object.dm +++ b/code/_rendering/parallax/parallax_object.dm @@ -5,7 +5,7 @@ plane = PARALLAX_PLANE screen_loc = "CENTER-7,CENTER-7" mouse_opacity = MOUSE_OPACITY_TRANSPARENT - appearance_flags = PIXEL_SCALE | KEEP_TOGETHER + appearance_flags = PIXEL_SCALE | KEEP_TOGETHER | TILE_BOUND // notice - all parallax layers are 15x15 tiles. They roll over every 240 pixels. /// pixel x/y shift per real x/y diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index cfd8435bd3f..4152e18afd0 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -1,3 +1,30 @@ +/datum/config_entry/string/max_viewport_size + default = "19x15" + +/datum/config_entry/string/max_viewport_size/ValidateAndSet(str_val) + if(!istext(str_val)) + return FALSE + var/list/split = splittext(str_val, "x") + if(length(split) != 2) + return FALSE + var/width = text2num(split[1]) + var/height = text2num(split[2]) + if(height < 1 || width < 1 || height > 67 || width > 67) + return FALSE + . = ..() + if(!.) + return + config.update_player_viewsize() + +/* +/datum/config_entry/string/game_viewport_size + default = "19x15" + +/datum/config_entry/string/max_viewport_size/ValidateAndSet(str_val) + . = ..() +*/ +//! BYONd world.view is immutable, this config is left here as a todo. If it's still immutable by, say, 516, remove it. + /datum/config_entry/number/starlight default = 2 @@ -51,13 +78,3 @@ /datum/config_entry/flag/emojis default = TRUE - -/datum/config_entry/number/max_client_view_x - default = 21 - min_val = 15 - max_val = 50 // Byond (the) limits - -/datum/config_entry/number/max_client_view_y - default = 15 - min_val = 15 - max_val = 50 // Byond (the) limits diff --git a/code/datums/mocking/client.dm b/code/datums/mocking/client.dm index fd99e34520c..cb7d37ff967 100644 --- a/code/datums/mocking/client.dm +++ b/code/datums/mocking/client.dm @@ -11,3 +11,6 @@ /// The mob the client controls var/mob/mob + + var/current_viewport_width = 15 + var/current_viewport_height = 15 diff --git a/code/datums/perspective.dm b/code/datums/perspective.dm index a6587b74b74..de3728ac07c 100644 --- a/code/datums/perspective.dm +++ b/code/datums/perspective.dm @@ -3,6 +3,25 @@ * * allows managed control of client viewport/eye changes * + * ! - - - ATTENTION - - - + * ! These datums will modify mob variables. + * ! This can result in AI mobs having inconsistent sight/whatevers + * ! from what they'd usually have after a player enters, and + * ! leaves them. + * ! Mob reset_perspective() should handle most of these cases, but be wary. + * + * ! - - - ATTENTION - - - + * ! View sizes should only be for clients. + * ! Do not read off this for stuff like AI view/target tracking. + * + * ? - - - ATTENTION - - - + * ? We track MAXIMUM view sizes. + * ? Client can resize up/down as needed. + * ? If you want to force a client to a specific size, + * ? regardless of their widescreen setting, + * ? use client.set_temporary_view. + * ? Augmented view will also ignore client prefs on widescreen, *kinda.* + * * as of right now, perspectives will **trample** the following on every set: * client.eye * client.lazy_eye (unimplemented) @@ -42,11 +61,9 @@ /// sight var var/sight = SEE_SELF /// active clients - this is not the same as mobs because a client can be looking somewhere that isn't their mob - var/list/client/clients = list() + var/list/client/clients /// mobs that are using this - required for clean gcs var/list/mob/mobs = list() - /// view size - var/view_size /// when a client logs out of a mob, and it's using us, the mob should reset to its self_perspective var/reset_on_logout = TRUE /// see in dark @@ -54,6 +71,22 @@ /// see_invisible var/see_invisible = SEE_INVISIBLE_LIVING + //! view size + /// default view; if null, world.view + var/default_view_size + /// view size increase x + var/augment_view_width = 0 + /// view size increase y + var/augment_view_height = 0 + /// suppression sources + var/list/view_suppression + /// cached view x + var/cached_view_width + /// cached view y + var/cached_view_height + /// view cache needs recompute + var/view_dirty = TRUE + /datum/perspective/Destroy() KickAll() ClearMobs() @@ -73,7 +106,7 @@ return if(C.using_perspective) CRASH("client already had perspective") - clients += C + LAZYADD(clients, C) C.using_perspective = src SEND_SIGNAL(src, COMSIG_PERSPECTIVE_CLIENT_REGISTER, C) Apply(C) @@ -85,7 +118,7 @@ SHOULD_CALL_PARENT(TRUE) if(!(C in clients)) return - clients -= C + LAZYREMOVE(clients, C) Remove(C) // if we're not doing this as part of a switch have them immediately switch to the mob // oh and make sure they unregister @@ -100,7 +133,7 @@ * gets all clients viewing us */ /datum/perspective/proc/GetClients() - return clients.Copy() + return isnull(clients)? list() : clients.Copy() /** * kicks all clients off us @@ -118,6 +151,7 @@ /** * registers as a mob's current perspective + * sets mob vars as necessary */ /datum/perspective/proc/AddMob(mob/M) SHOULD_CALL_PARENT(TRUE) @@ -127,13 +161,20 @@ return mobs += M M.using_perspective = src + M.sight = sight + M.see_in_dark = see_in_dark + M.see_invisible = see_invisible SEND_SIGNAL(src, COMSIG_PERSPECTIVE_MOB_ADD, M) /** * unregisters as a mob's current perspective + * resets mob vars to initial() values */ /datum/perspective/proc/RemoveMob(mob/M, switching = FALSE) SHOULD_CALL_PARENT(TRUE) + M.sight = initial(M.sight) + M.see_in_dark = initial(M.see_in_dark) + M.see_invisible = initial(M.see_invisible) mobs -= M SEND_SIGNAL(src, COMSIG_PERSPECTIVE_MOB_REMOVE, M, switching) if(M.using_perspective == src) @@ -184,11 +225,7 @@ if(changed != C.eye) C.parallax_holder?.Reset(force = TRUE) C.perspective = GetEyeMode(C) - C.mob.sight = sight - C.mob.see_in_dark = see_in_dark - C.mob.see_invisible = see_invisible - if(view_size) - C.change_view(view_size, TRUE) + update_view_size(C) /** * update all viewers @@ -294,13 +331,94 @@ for(var/client/C as anything in clients) C.mob.see_invisible = see_invisible -/datum/perspective/proc/SetViewSize(new_size) - var/change = view_size == new_size - view_size = new_size - if(change) - for(var/client/C as anything in clients) - C.change_view(new_size, TRUE) +//! view size +/** + * Sets our default size. + * + * DO NOT use this in place of augmented. + * User widescreen preferences WILL override this! + * + * Ideally this shouldn't be used at all. + */ +/datum/perspective/proc/set_default_view(size) + if(size == default_view_size) + return + // assert it y'know, works + if(isnum(size)) + default_view_size = size + else if(isnull(size)) + default_view_size = size + else if(istext(size)) + var/list/split = splittext(size, "x") + ASSERT(split.len == 2) + // if the above runtimes, let it so we know who fucked up + default_view_size = size + else + CRASH("What?") + view_dirty = TRUE + update_view_size() +/** + * Sets augmented size ontop of default size. + * + * Currently only supports positive numbers. Numbers apply equally + * to both directions of a dimension. + */ +/datum/perspective/proc/set_augmented_view(width, height) + if(!isnum(height) || !isnum(width)) + CRASH("invalid") + // no negative augment values for now + augment_view_height = max(0, height) + augment_view_width = max(0, width) + view_dirty = TRUE + update_view_size() + +/datum/perspective/proc/update_view_size(client/C) + if(view_dirty) + recompute_view_size() + if(C) + C.request_viewport_update() + else + for(var/client/_C as anything in clients) + update_view_size(_C) + +/datum/perspective/proc/recompute_view_size() + view_dirty = FALSE + if(isnum(default_view_size)) + cached_view_height = default_view_size + augment_view_width + cached_view_width = default_view_size + augment_view_height + return + else if(isnull(default_view_size)) + cached_view_width = GLOB.max_client_view_x + augment_view_width + cached_view_height = GLOB.max_client_view_y + augment_view_height + return + var/list/parsed = splittext(default_view_size, "x") + if(length(parsed) != 2) + cached_view_width = GLOB.max_client_view_x + augment_view_width + cached_view_height = GLOB.max_client_view_y + augment_view_height + return + cached_view_width = parsed[1] + augment_view_width + cached_view_height = parsed[2] + augment_view_height + +/datum/perspective/proc/suppress_view(source) + var/was = LAZYLEN(view_suppression) + LAZYOR(view_suppression, source) + if(!was && LAZYLEN(view_suppression)) + view_dirty = TRUE + update_view_size() + +/datum/perspective/proc/unsuppress_view(source) + var/was = LAZYLEN(view_suppression) + LAZYREMOVE(view_suppression, source) + if(was && !LAZYLEN(view_suppression)) + view_dirty = TRUE + update_view_size() + +/datum/perspective/proc/ensure_view_cached() + if(view_dirty) + recompute_view_size() + +//! remotes /datum/perspective/proc/considered_remote(mob/M) return eye == M diff --git a/code/game/atoms/atoms_movable.dm b/code/game/atoms/atoms_movable.dm index eea32440766..da36fc7b20c 100644 --- a/code/game/atoms/atoms_movable.dm +++ b/code/game/atoms/atoms_movable.dm @@ -1,6 +1,7 @@ /atom/movable layer = OBJ_LAYER - appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER + // todo: evaluate if we need TILE_BOUND + SET_APPEARANCE_FLAGS(TILE_BOUND | PIXEL_SCALE) // todo: kill this (only used for elcetropacks) var/moved_recently = FALSE diff --git a/code/game/mecha/combat/fighter.dm b/code/game/mecha/combat/fighter.dm index 3abcdefd5b1..749d09ddee5 100644 --- a/code/game/mecha/combat/fighter.dm +++ b/code/game/mecha/combat/fighter.dm @@ -225,7 +225,12 @@ //causes damage when running into objects /obj/mecha/combat/fighter/Bump(atom/obstacle) . = ..() + // this isn't defined becuase this is shitcode anyways and i'm just patching it + // todo: why the fuck are you guys doing snowflake collision code?? + if(TIMER_COOLDOWN_CHECK(src, "fighter_collision")) + return if(istype(obstacle, /obj) || istype(obstacle, /turf)) + TIMER_COOLDOWN_START(src, "fighter_collision", 5 SECONDS) occupant_message("Collision Alert!") take_damage(20, "brute") playsound(src, 'sound/effects/grillehit.ogg', 50, 1) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 183d9528199..3c151c9ff0c 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -837,7 +837,7 @@ /obj/mecha/proc/mechstep(direction) var/current_dir = dir //For strafing var/result = get_step(src,direction) - if(result && Move(result)) + if(result && Move(result, direction)) if(stomp_sound) playsound(src,stomp_sound,40,1) handle_equipment_movement() diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm index 891159fb3af..46f019bcb5e 100644 --- a/code/game/mecha/mecha_actions.dm +++ b/code/game/mecha/mecha_actions.dm @@ -363,10 +363,10 @@ src.log_message("Toggled zoom mode.") src.occupant_message("Zoom mode [zoom?"en":"dis"]abled.") if(zoom) - myclient.change_view(12, TRUE) + myclient.set_temporary_view(GLOB.max_client_view_x + 5, GLOB.max_client_view_y + 5) src.occupant << sound('sound/mecha/imag_enh.ogg',volume=50) else - myclient.change_view("[myclient.last_view_x_dim*2]x[myclient.last_view_y_dim*2]", TRUE) // Reset to default + myclient.reset_temporary_view() return diff --git a/code/game/objects/_items.dm b/code/game/objects/_items.dm index 96f1bc1b407..7efc229789a 100644 --- a/code/game/objects/_items.dm +++ b/code/game/objects/_items.dm @@ -629,11 +629,11 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. else if(!zoom && user.item_by_slot(wornslot) != src && wornslot != FALSE) to_chat(user, "You need to wear the [devicename] to look through it properly") cannotzoom = 1 - //We checked above if they are a human and returned already if they weren't. var/mob/living/carbon/human/H = user if(!zoom && !cannotzoom) - INVOKE_ASYNC(H.client, /client.proc/change_view, viewsize, TRUE) // Reset to default + H.ensure_self_perspective() + H.self_perspective.set_augmented_view(viewsize - 2, viewsize - 2) zoom = 1 var/tilesize = 32 @@ -652,14 +652,12 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. if (WEST) H.client.pixel_x = -viewoffset H.client.pixel_y = 0 - H.visible_message("[user] peers through the [zoomdevicename ? "[zoomdevicename] of the [src.name]" : "[src.name]"].") H.looking_elsewhere = TRUE H.handle_vision() - else - - + H.ensure_self_perspective() + H.self_perspective.set_augmented_view(0, 0) zoom = 0 H.client.pixel_x = 0 @@ -667,8 +665,6 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. H.looking_elsewhere = FALSE H.handle_vision() - INVOKE_ASYNC(H.client, H.client.is_preference_enabled(/datum/client_preference/scaling_viewport) ? /client.verb/OnResize : /client.proc/change_view, world.view) // Reset to default - if(!cannotzoom) user.visible_message("[zoomdevicename ? "[user] looks up from the [src.name]" : "[user] lowers the [src.name]"].") diff --git a/code/game/objects/effects/map_effects/portal.dm b/code/game/objects/effects/map_effects/portal.dm index 0611f6def2a..536863d3247 100644 --- a/code/game/objects/effects/map_effects/portal.dm +++ b/code/game/objects/effects/map_effects/portal.dm @@ -50,7 +50,7 @@ when portals are shortly lived, or when portals are made to be obvious with spec opacity = TRUE plane = TURF_PLANE layer = ABOVE_TURF_LAYER - appearance_flags = PIXEL_SCALE|KEEP_TOGETHER // Removed TILE_BOUND so things not visible on the other side stay hidden from the viewer. + SET_APPEARANCE_FLAGS(PIXEL_SCALE) var/obj/effect/map_effect/portal/counterpart = null // The portal line or master that this is connected to, on the 'other side'. diff --git a/code/game/world.dm b/code/game/world.dm index bdc185f122e..a22d9fe1582 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -18,13 +18,6 @@ GLOBAL_LIST(topic_status_cache) //This happens after the Master subsystem new(s) (it's a global datum) //So subsystems globals exist, but are not initialised /world/New() - //! init auxtools - var/debug_server = world.GetConfig("env", "AUXTOOLS_DEBUG_DLL") - if (debug_server) - call(debug_server, "auxtools_init")() - enable_debugging() - // AUXTOOLS_CHECK(AUXTOOLS_YAML) - log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!") var/tempfile = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set @@ -40,6 +33,7 @@ GLOBAL_LIST(topic_status_cache) InitTgs() config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER]) + config.update_world_viewsize() //! Since world.view is immutable, we load it here. //SetupLogs depends on the RoundID, so lets check //DB schema and set RoundID if we can diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm index e67da7bef99..c60de4eabe0 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm @@ -54,6 +54,12 @@ /proc/_ispath(path, type) return ispath(path, type) +/proc/_json_encode(list/L) + return json_encode(L) + +/proc/_json_decode(json) + return json_decode(json) + /proc/_length(E) return length(E) @@ -153,9 +159,17 @@ /proc/_sin(X) return sin(X) +/proc/_list2params(L) + return list2params(L) + /proc/_list_construct(...) . = args.Copy() +/proc/_list_construct_assoc(...) + . = list() + for(var/i in 1 to (args.len - 1) step 2) + .[args[i]] = args[i+1] + /proc/_list_add(list/L, ...) if (args.len < 2) return diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 26c9f8a9d17..2dbfdc5d133 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -831,11 +831,11 @@ Traitors and the like can also be revived with the previous role mostly intact. set name = "Change View Range" set desc = "switches between 1x and custom views" - var/view = src.view - if(view == world.view) - view = input("Select view range:", "FUCK YE", 7) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128) + if(!using_temporary_viewsize) + var/number = input("Select view range:", "FUCK YE", 7) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,18,20,22,24,26,28,30,32,34) + set_temporary_view(number * 2 + 1, number * 2 + 1) else - SetWindowIconSize(prefs.icon_size) + reset_temporary_view() log_admin("[key_name(usr)] changed their view range to [view].") //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 765a4532320..304d73c8023 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -53,6 +53,35 @@ /// the perspective we're currently using var/datum/perspective/using_perspective + //! Viewport + /// what we *think* their current viewport size is in pixels + var/assumed_viewport_spx + /// what we *think* their current viewport size is in pixels + var/assumed_viewport_spy + /// what we *think* their current viewport zoom is + var/assumed_viewport_zoom + /// what we *think* their current viewport letterboxing setting is + var/assumed_viewport_box + /// current view x - for fast access + var/current_viewport_width + /// current view y - for fast access + var/current_viewport_height + /// if things are manipulating the viewport we don't want other things to touch it + var/viewport_rwlock = TRUE //! default block so we can release it during init_viewport + /// viewport update queued? + var/viewport_queued = FALSE + /// forced temporary view + var/temporary_viewsize_width + /// forced temporary view + var/temporary_viewsize_height + /// temporary view active? + var/using_temporary_viewsize = FALSE + + //! menu button statuses + var/list/menu_buttons_checked = list() + //! menu group statuses + var/list/menu_group_status = list() + //////////////// //ADMIN THINGS// //////////////// @@ -149,6 +178,3 @@ /// If this client has been fully initialized or not var/fully_created = FALSE - - /// datum wrapper for client view - var/datum/view_data/view_size diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 9c58fe62cdf..f9e7330cf00 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -151,32 +151,38 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( /////////// /client/New(TopicData) + //! pre-connect-ish + // set appadmin for profiling or it might not work (?) (this is old code we just assume it's here for a reason) world.SetConfig("APP/admin", ckey, "role=admin") - //var/tdata = TopicData //save this for later use - TopicData = null //Prevent calls to client.Topic from connect - - if(connection != "seeker" && connection != "web")//Invalid connection type. + // block client.Topic() calls from connect + TopicData = null + // kick out invalid connections + if(connection != "seeker" && connection != "web") return null - + // kick out guests if(!config_legacy.guests_allowed && IsGuestKey(key)) alert(src,"This server doesn't allow guest accounts to play. Please go to http://www.byond.com/ and register for a key.","Guest","OK") del(src) return - + // pre-connect greeting to_chat(src, "If the title screen is black, resources are still downloading. Please be patient until the title screen appears.") - + // register in globals GLOB.clients += src GLOB.directory[ckey] = src + //! Resolve storage datums // resolve persistent data persistent = resolve_client_data(ckey) - // todo: move resolve database data up here + // todo: move resolve database data up here but above preferences + // todo: move preferences up here but above persistent + //! Setup user interface + // todo: move top level menu here, for now it has to be under prefs. // Instantiate tgui panel tgui_panel = new(src, "browseroutput") + //! Setup admin tooling GLOB.ahelp_tickets.ClientLogin(src) - SSserver_maint.UpdateHubStatus() var/connecting_admin = FALSE //because de-admined admins connecting should be treated like admins. //Admin Authorisation holder = admin_datums[ckey] @@ -221,6 +227,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if(!debug_tools_allowed) world.SetConfig("APP/admin", ckey, null) //END CITADEL EDIT + // todo: refactor and hoist //preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum) prefs = GLOB.preferences_datums[ckey] if(!prefs) @@ -228,10 +235,15 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( GLOB.preferences_datums[ckey] = prefs prefs.client = src + // todo: refactor prefs.last_ip = address //these are gonna be used for banning prefs.last_id = computer_id //these are gonna be used for banning //fps = prefs.clientfps //(prefs.clientfps < 0) ? RECOMMENDED_FPS : prefs.clientfps + // todo: hoist + // build top level menu + GLOB.main_window_menu.setup(src) + var/full_version = "[byond_version].[byond_build ? byond_build : "xxx"]" log_access("Login: [key_name(src)] from [address ? address : "localhost"]-[computer_id] || BYOND v[full_version]") /* @@ -382,6 +394,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( send_resources() + //? Startup rendering + pre_init_viewport() mob.reload_rendering() if(prefs.lastchangelog != GLOB.changelog_hash) //bolds the changelog button on the interface so we know there are updates. @@ -401,8 +415,13 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if(isnum(account_age) && account_age <= 2) log_and_message_admins("PARANOIA: [key_name(src)] has a very new BYOND account ([account_age] days).") + //? We are done + // set initialized initialized = TRUE + // show any migration errors prefs.auto_flush_errors() + // update our hub label + SSserver_maint.UpdateHubStatus() ////////////// //DISCONNECT// @@ -744,40 +763,38 @@ GLOBAL_VAR_INIT(log_clicks, FALSE) . = ..() /client/proc/change_view(new_size, forced, translocate) - if(!is_preference_enabled(/datum/client_preference/scaling_viewport)) - if (isnull(new_size)) - CRASH("change_view called without argument.") + set waitfor = FALSE // to async temporary view + // todo: refactor this, client view changes should be ephemeral. + var/list/L = getviewsize(new_size) + set_temporary_view(L[1], L[2]) - if(view == new_size) - // unnecessary - return +/** + * directly sets our view + * you should probably be using perspective datums most of the time instead + * WARNING: this is verbatim; aka, view = 7 is 15 width 15 height, NOT 7x7! + * + * furthermore, this proc is BLOCKING. + */ +/client/proc/set_temporary_view(width, height) + if(!width || !height || width < 0 || height < 0) + reset_temporary_view() + return + using_temporary_viewsize = FALSE + temporary_viewsize_width = width + temporary_viewsize_height = height + request_viewport_update() - /* - if(prefs && !prefs.widescreenpref && new_size == CONFIG_GET(string/default_view)) - new_size = CONFIG_GET(string/default_view_square) - */ - - view = new_size - mob.reload_rendering() - if(!translocate) - reset_perspective() - else if(!. || forced) - if(!translocate) - reset_perspective() - mob?.reload_fullscreen() - update_clickcatcher() - if(forced) - view = new_size - else - INVOKE_ASYNC(src, /client.verb/OnResize) - /* - mob.reload_fullscreen() - if (isliving(mob)) - var/mob/living/M = mob - M.update_damage_hud() - if (prefs.auto_fit_viewport) - addtimer(CALLBACK(src,.verb/fit_viewport,10)) //Delayed to avoid wingets from Login calls. - */ +/** + * resets our temporary view + * you should probably be using perspective datums most of the time instead + * + * furthermore, this proc is BLOCKING + */ +/client/proc/reset_temporary_view() + using_temporary_viewsize = FALSE + temporary_viewsize_height = null + temporary_viewsize_width = null + request_viewport_update() /** * switch perspective - null will cause us to shunt our eye to nullspace! diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index c09fd9e5a2d..f9449480d4e 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -235,49 +235,3 @@ if(eyeobj) return eyeobj return src - -/client/verb/fit_viewport() - set name = "Fit Viewport" - set category = "OOC" - set desc = "Fit the width of the map window to match the viewport" - - // Fetch the client's aspect ratio - var/view_size = getviewsize(view) - var/aspect_ratio = view_size[1] / view_size[2] - - // Calculate desired pixel width using window size and aspect ratio - var/sizes = params2list(winget(src, "mainwindow.split;mapwindow", "size")) - var/map_size = splittext(sizes["mapwindow.size"], "x") - var/height = text2num(map_size[2]) - var/desired_width = round(height * aspect_ratio) - if (text2num(map_size[1]) == desired_width) - return // You're already good fam. - - var/split_size = splittext(sizes["mainwindow.split.size"], "x") - var/split_width = text2num(split_size[1]) - - // Calculate and apply our best estimate - // +4 pixels are for the eidth of the splitter's handle - var/pct = 100 * (desired_width + 4) / split_width - winset(src, "mainwindow.split", "splitter=[pct]") - - // Apply an ever-lowering offset until we finish or fail - var/delta - for(var/safety in 1 to 10) - var/after_size = winget(src, "mapwindow", "size") - map_size = splittext(after_size, "x") - var/got_width = text2num(map_size[1]) - - if (got_width == desired_width) - return // Success! - - // Calculate a probable delta value based on the diff - else if (isnull(delta)) - delta = 100 * (desired_width - got_width) / split_width - - // If we overshot, halve the delta and reverse direction - else if ((delta > 0 && got_width > desired_width) || (delta < 0 && got_width < desired_width)) - delta = -delta/2 - - pct += delta - winset(src, "mainwindow.split", "splitter=[pct]") diff --git a/code/modules/client/viewport.dm b/code/modules/client/viewport.dm index cfcb3d87e78..f2e49f035ab 100644 --- a/code/modules/client/viewport.dm +++ b/code/modules/client/viewport.dm @@ -1,78 +1,401 @@ -GLOBAL_VAR_INIT(lock_client_view_x, null) -GLOBAL_VAR_INIT(lock_client_view_y, null) -GLOBAL_LIST_INIT(valid_icon_sizes, list(32, 48, 64, 72, 96, 128)) +// these two variables control max dynamic resize for viewport +GLOBAL_VAR_INIT(max_client_view_x, 19) +GLOBAL_VAR_INIT(max_client_view_y, 15) +// these two variables **reflect** (config updates, not this!) effective default viewsize for view(), range(), etc. +GLOBAL_VAR_INIT(game_view_x, 19) +GLOBAL_VAR_INIT(game_view_y, 15) +// minimum size viewports can be +GLOBAL_VAR_INIT(min_client_view_x, 15) +GLOBAL_VAR_INIT(min_client_view_y, 15) +// these two variables, if set, lock all clients to a certain viewsize no matter what +GLOBAL_VAR(lock_client_view_x) +GLOBAL_VAR(lock_client_view_y) -/datum/preferences - var/icon_size = 64 - -/client - var/last_view_x_dim = 7 - var/last_view_y_dim = 7 - - -/client/verb/SetCozyViewInwards(val as num|text) - set hidden = 1 - - if(prefs && val != prefs.icon_size) - prefs.icon_size += val - SScharacters.queue_preferences_save(prefs) - winset(src, "mapwindow.map", "icon-size=[prefs.icon_size]") - OnResize() - -/client/verb/SetWindowIconSize(val as num|text) - set hidden = 1 - winset(src, "mapwindow.map", "icon-size=[val]") - if(prefs && val != prefs.icon_size) - prefs.icon_size = val - SScharacters.queue_preferences_save(prefs) - OnResize() - -/client/verb/OnResize() - set hidden = 1 - if(!is_preference_enabled(/datum/client_preference/scaling_viewport)) - return - var/divisor = text2num(winget(src, "mapwindow.map", "icon-size")) || world.icon_size - if(!isnull(GLOB.lock_client_view_x) && !isnull(GLOB.lock_client_view_y)) - last_view_x_dim = GLOB.lock_client_view_x - last_view_y_dim = GLOB.lock_client_view_y +/** + * forces world viewsize; use for config VAS + */ +/datum/controller/configuration/proc/update_world_viewsize() + //! BYONd world.view is immutable, for now we're stuck read-onlying it. + if(isnum(world.view)) + GLOB.game_view_x = GLOB.game_view_y = world.view else - var/winsize_string = winget(src, "mapwindow.map", "size") - last_view_x_dim = GLOB.lock_client_view_x || clamp(ROUND_UP(text2num(winsize_string) / divisor), 15, (CONFIG_GET(number/max_client_view_x)) || (CONFIG_GET(number/max_client_view_x))) - last_view_y_dim = GLOB.lock_client_view_y || clamp(ROUND_UP(text2num(copytext(winsize_string,findtext(winsize_string,"x")+1,0)) / divisor), 15, (CONFIG_GET(number/max_client_view_y)) || 41) + // is text + var/list/viewsize = splittext(world.view, "x") + GLOB.game_view_x = viewsize[1] + GLOB.game_view_y = viewsize[2] - if(last_view_x_dim % 2 == 0) - last_view_x_dim++ - if(last_view_y_dim % 2 == 0) - last_view_y_dim++ +/** + * forces screensize update; use for config VAS + */ +/datum/controller/configuration/proc/update_player_viewsize() + var/viewsize_raw = CONFIG_GET(string/max_viewport_size) + var/list/viewsize = splittext(viewsize_raw, "x") + GLOB.max_client_view_x = text2num(viewsize[1]) + GLOB.max_client_view_y = text2num(viewsize[2]) - for(var/check_icon_size in GLOB.valid_icon_sizes) - winset(src, "menu.icon[check_icon_size]", "is-checked=false") - winset(src, "menu.icon[divisor]", "is-checked=true") +//! Core viewport procs +//! These don't obey the read-write lock, and should only be called from the synchronized procs! - view = "[last_view_x_dim]x[last_view_y_dim]" +/** + * populates important vars that things may read early before we sleep + */ +/client/proc/pre_init_viewport() + PRIVATE_PROC(TRUE) + current_viewport_width = GLOB.max_client_view_x + current_viewport_height = GLOB.max_client_view_y - // Reset eye/perspective - reset_perspective() -/* var/last_perspective = perspective +/** + * called on client init to do this without blocking client/New + */ +/client/proc/init_viewport_blocking() + PRIVATE_PROC(TRUE) + fetch_viewport() + refit_viewsize() + // release the initial lock + viewport_rwlock = FALSE + +/** + * called to manually update viewport vars since the skin macro is only triggered on resize + * + * return TRUE if changed + */ +/client/proc/fetch_viewport() + PRIVATE_PROC(TRUE) + . = FALSE + // get vars only; they have to manually refit + var/list/got = params2list(winget(src, SKIN_MAP_ID_VIEWPORT, "size;zoom;letterbox")) + var/got_zoom = text2num(got["zoom"]) || 0 + var/got_box = (got["letterbox"] == "true") + var/list/split = splittext(got["size"], "x") + var/got_spx + var/got_spy + if(length(split) == 2) + got_spx = text2num(split[1]) || (WORLD_ICON_SIZE * GLOB.game_view_x) + got_spy = text2num(split[2]) || (WORLD_ICON_SIZE * GLOB.game_view_y) + else + stack_trace("fetch_viewport failed to get spx/spy") + got_spx = (WORLD_ICON_SIZE * GLOB.game_view_x) + got_spy = (WORLD_ICON_SIZE * GLOB.game_view_y) + if(got_zoom != assumed_viewport_zoom || got_spx != assumed_viewport_spx || got_spy != assumed_viewport_spy || got_box != assumed_viewport_box) + . = TRUE + assumed_viewport_zoom = got_zoom + assumed_viewport_spx = got_spx + assumed_viewport_spy = got_spy + assumed_viewport_box = got_box + +/** + * called to refit our view size as necessary + * + * this is automatically called every time something modifies us + */ +/client/proc/refit_viewsize(no_fit) + PRIVATE_PROC(TRUE) + if(!isnull(GLOB.lock_client_view_x) && !isnull(GLOB.lock_client_view_y)) + view = "[GLOB.lock_client_view_x]x[GLOB.lock_client_view_y]" + on_refit_viewsize(GLOB.lock_client_view_x, GLOB.lock_client_view_y, no_fit) + return + if(using_temporary_viewsize) + view = "[temporary_viewsize_width]x[temporary_viewsize_height]" + on_refit_viewsize(temporary_viewsize_width, temporary_viewsize_height, no_fit) + return + var/widescreen = is_widescreen_enabled() + if(!widescreen) + // if not widescreen, we should just force to 15 x 15 and their augmented view + var/width = 15 + (using_perspective.augment_view_width * 2) + var/height = 15 + (using_perspective.augment_view_height * 2) + view = "[width]x[height]" + on_refit_viewsize(width, height, no_fit) + return + var/stretch_to_fit = assumed_viewport_zoom == 0 + using_perspective.ensure_view_cached() + var/max_width = using_perspective.cached_view_width + var/max_height = using_perspective.cached_view_height + if(stretch_to_fit) + // option 1: they're stretching to fit + if(assumed_viewport_box) + // fit everything + view = "[max_width]x[max_height]" + on_refit_viewsize(max_width, max_height) + return + // option 2: they're stretching to fit the longest side + else + // todo: handle horizontally-wider view sizes + // for now we only care about horizontal fit + var/stretch_pixel_amount = assumed_viewport_spy / max_height + var/available_width = assumed_viewport_spx / stretch_pixel_amount + available_width = CEILING(available_width, 1) + available_width = clamp(available_width, GLOB.min_client_view_x, max_width) + view = "[available_width]x[max_height]" + on_refit_viewsize(available_width, max_height, no_fit) + return + // option 3: scale as necessary + var/pixels_per_tile = assumed_viewport_zoom * WORLD_ICON_SIZE + var/div_x = assumed_viewport_spx / pixels_per_tile + var/div_y = assumed_viewport_spy / pixels_per_tile + div_x = CEILING(div_x, 1) + div_y = CEILING(div_y, 1) + var/desired_width = clamp(div_x, GLOB.min_client_view_x, max_width) + var/desired_height = clamp(div_y, GLOB.min_client_view_y, max_height) + view = "[desired_width]x[desired_height]" + on_refit_viewsize(desired_width, desired_height, no_fit) + +/client/proc/on_refit_viewsize(new_width, new_height, no_fit) + var/changed = (current_viewport_height != new_height) || (current_viewport_width != new_width) + if(changed) + current_viewport_width = new_width + current_viewport_height = new_height + if(!no_fit && is_auto_fit_viewport_enabled()) + fit_viewport() + post_refit_viewsize(changed) + +/** + * updates everything when our viewport changes + */ +/client/proc/post_refit_viewsize(changed) + if(!changed) + return + // force perspective swap for ??? reasons (???) + var/old_perspective = perspective perspective = MOB_PERSPECTIVE - if(perspective != last_perspective) - perspective = last_perspective - var/last_eye = eye - eye = mob - if(eye != last_eye) - eye = last_eye - if(mob) - mob.reload_fullscreen() */ - update_clickcatcher() + if(old_perspective != perspective) + perspective = old_perspective + // i don't understand the above + mob?.refit_rendering() -/client/verb/force_onresize_view_update() - set name = "Force Client View Update" +/** + * called to fit our viewport to what we **should** have to get our effective view. + */ +/client/proc/fit_viewport() + // by now we already fetched viewport + // figure out how big they want it to be based on their settings + // note: we only care about horizontal because the splitter.. is, well, horizontal. + var/desired_pixel_width + // todo: pref + // maximum % they're willing to have their map + var/maximum_splitter_percent = 80 + // splitter intrinsic width + var/assumed_splitter_width = 4 + // effective size + var/widescreen = is_widescreen_enabled() + var/effective_view_width + var/effective_view_height + // todo: optimize the shit out of this proc and the view system + if(!isnull(GLOB.lock_client_view_x) && !isnull(GLOB.lock_client_view_y)) + effective_view_width = GLOB.lock_client_view_x + effective_view_height = GLOB.lock_client_view_y + else if(widescreen) + effective_view_width = clamp(temporary_viewsize_width || using_perspective.cached_view_width, GLOB.min_client_view_x, 67) + effective_view_height = clamp(temporary_viewsize_height || using_perspective.cached_view_height, GLOB.min_client_view_y, 67) + else + effective_view_width = clamp(temporary_viewsize_width || (15 + (using_perspective.augment_view_width * 2)), GLOB.min_client_view_x, 67) + effective_view_height = clamp(temporary_viewsize_height || (15 + (using_perspective.augment_view_height * 2)), GLOB.min_client_view_y, 67) + if(assumed_viewport_zoom == 0) + // they're stretching to fit; this makes it annoying + var/aspect_ratio = effective_view_width / effective_view_height + // we have to fit everything + // viewport_spy should never change since that's not what the player can drag, + // unless they resize the outer window itself. + desired_pixel_width = assumed_viewport_spy * aspect_ratio + else + // they're not using stretch to fit; this makes it very easy + desired_pixel_width = WORLD_ICON_SIZE * assumed_viewport_zoom * effective_view_width + // grab their screen size (or what counts as it) + var/list/fetching = winget(src, SKIN_SPLITTER_ID_MAIN, "size") + var/list/parsed = splittext(fetching, "x") + var/screen_width = text2num(parsed[1]) + // grab what percent we should go to + var/current_percent = min(100 * ((desired_pixel_width + assumed_splitter_width) / screen_width), maximum_splitter_percent) + // initial set + winset(src, SKIN_SPLITTER_ID_MAIN, "splitter=[current_percent]") + // funny method of correcting for misalignments via binary-search-like behavior + var/delta + fetching = winget(src, SKIN_MAP_ID_VIEWPORT, "size") + parsed = splittext(fetching, "x") + var/current_width = text2num(parsed[1]) + var/last_width = current_width + if(current_width == desired_pixel_width) + // done + return + // nope, start adjusting + delta = 100 * ((desired_pixel_width - current_width) / screen_width) + for(var/safety in 1 to 10) + current_percent = min(current_percent + delta, maximum_splitter_percent) + winset(src, SKIN_SPLITTER_ID_MAIN, "splitter=[current_percent]") + fetching = winget(src, SKIN_MAP_ID_VIEWPORT, "size") + parsed = splittext(fetching, "x") + current_width = text2num(parsed[1]) + // done? + if(current_width == desired_pixel_width) + return + // if we're literally down to a pixel how about like Don't + if(last_width == current_width) + return + last_width = current_width + // keep adjusting + // overshot? + if ((delta > 0 && current_width > desired_pixel_width) || (delta < 0 && current_width < desired_pixel_width)) + // reverse in half + delta = -delta / 2 + +//! Synchronized procs; calling these from outside is fine. +/** + * call this when things change to queue an update + * + * should only be called **in code**, not by the skin! + * + * warning: this proc doesn't refit instantly, it only queues. + * client decides what time is best to refit. + */ +/client/proc/request_viewport_update() + set waitfor = FALSE + if(!viewport_rwlock) + _request_viewport_update() + if(!viewport_queued) + viewport_queued = TRUE + addtimer(CALLBACK(src, .proc/_request_viewport_update), 0) + +/** + * call this when things change to queue an update + * + * should only be called **in code**, not by the skin! + * + * blocks until finished + */ +/client/proc/request_viewport_update_blocking() + if(!viewport_rwlock) + _request_viewport_update() + return + if(!viewport_queued) + viewport_queued = TRUE + addtimer(CALLBACK(src, .proc/_request_viewport_update), 0) + UNTIL(!viewport_queued) + +// todo : locks are probably bad when working with request fit +/client/proc/_request_viewport_update(no_fit, no_fetch) + PRIVATE_PROC(TRUE) + // if rwlocked, wait; people should be queuing and not calling this directly + UNTIL(!viewport_rwlock) + // clear queued + viewport_queued = FALSE + // lock + viewport_rwlock = TRUE + if(!no_fetch) + fetch_viewport() + // refit size based on viewport + refit_viewsize(no_fit) + // release lock + viewport_rwlock = FALSE + +/** + * call this to request a viewport fit + */ +/client/proc/request_viewport_fit(no_recalc) + set waitfor = FALSE + _request_viewport_fit(no_recalc) + +/** + * call this to request a viewport fit + * forcefully fits + * blocks until finished + * don't call this from non-buttons because it blocks and + * absolutely can stack and ruin everyone's day. + */ +/client/proc/viewport_fit_blocking(no_recalc) + UNTIL(!viewport_rwlock) + return _request_viewport_fit(no_recalc) + +// todo : locks are probably bad when working with request update +/client/proc/_request_viewport_fit(no_recalc) + PRIVATE_PROC(TRUE) + // if viewport is locked, ignore it + if(viewport_rwlock) + return + // acquire lock + viewport_rwlock = TRUE + // just in case because this is called by a lot of things that are too shitcoded to update (like my own code) + fetch_viewport() + // fit viewport + fit_viewport() + // fetch values; we do not trust on-size to trigger + fetch_viewport() + // then refit the viewsize if needed + if(!no_recalc) + refit_viewsize() + // release lock + viewport_rwlock = FALSE + +/** + * called directly by the skin when resizing + * + * @params + * - w - width of viewport in pixels + * - h - height of viewport in pixels + * - z - zoom of viewport + * - b - are we letterboxing? + */ +/client/verb/on_viewport(w as text, h as text, z as num, b as num) + set name = ".on_viewport" + set hidden = TRUE + if(viewport_rwlock) // something is fucking around, don't edit for them + return + // get vars + assumed_viewport_spx = text2num(w) + assumed_viewport_spy = text2num(h) + assumed_viewport_zoom = z + assumed_viewport_box = b + // refit + _request_viewport_update(TRUE, TRUE) + +/** + * automatically fit their viewport to show everything optimally + */ +/client/verb/user_fit_viewport() + set name = "Fit Viewport" + set category = "OOC" + set desc = "Fit the width of the map window to match the viewport" + request_viewport_fit() + +/client/verb/force_map_zoom(n as num) + set name = ".viewport_zoom" + set hidden = TRUE set src = usr set category = "Debug" - OnResize() + if(!isnum(n) || n < 0) + to_chat(usr, SPAN_WARNING("invalid number")) + return + if(viewport_rwlock) + to_chat(usr, SPAN_WARNING("Viewport is rwlocked; try again later.")) + return + winset(src, SKIN_MAP_ID_VIEWPORT, "zoom=[n]") + assumed_viewport_zoom = n + request_viewport_update(TRUE) + +/client/verb/force_map_box(n as num) + set name = ".viewport_box" + set hidden = TRUE + set src = usr + set category = "Debug" + if(viewport_rwlock) + to_chat(usr, SPAN_WARNING("Viewport is rwlocked; try again later.")) + return + n = !!n // force bool + winset(src, SKIN_MAP_ID_VIEWPORT, "letterbox=[n? "true" : "false"]") + assumed_viewport_box = n + request_viewport_update(TRUE) + +/client/verb/force_onresize_view_update() + set name = ".viewport_update" + set hidden = TRUE + set src = usr + set category = "Debug" + if(viewport_rwlock) + to_chat(usr, SPAN_WARNING("Viewport is rwlocked; try again later.")) + return + request_viewport_update() /client/verb/show_winset_debug_values() - set name = "Show Client View Debug Values" + set name = ".viewport_debug" + set hidden = TRUE set src = usr set category = "Debug" @@ -83,3 +406,5 @@ GLOBAL_LIST_INIT(valid_icon_sizes, list(32, 48, 64, 72, 96, 128)) to_chat(usr, "Icon size: [divisor]") to_chat(usr, "xDim: [round(text2num(winsize_string) / divisor)]") to_chat(usr, "yDim: [round(text2num(copytext(winsize_string,findtext(winsize_string,"x")+1,0)) / divisor)]") + to_chat(usr, "server thinks viewport is:[assumed_viewport_spx]/[assumed_viewport_spy]/[assumed_viewport_zoom]/[assumed_viewport_box]") + to_chat(usr, "server thinks view is:[current_viewport_width]x[current_viewport_height]") diff --git a/code/modules/client/winset_wrappers.dm b/code/modules/client/winset_wrappers.dm index 96050471ff5..4c467629174 100644 --- a/code/modules/client/winset_wrappers.dm +++ b/code/modules/client/winset_wrappers.dm @@ -12,3 +12,6 @@ /client/proc/_winshow(window, show) winshow(src, window, show) + +/client/proc/_command(str) + winset(src, null, "command=[str]") diff --git a/code/modules/mapping/map_helpers/network_builder/power_cable.dm b/code/modules/mapping/map_helpers/network_builder/power_cable.dm index 55b5c0f7188..d5c61e30007 100644 --- a/code/modules/mapping/map_helpers/network_builder/power_cable.dm +++ b/code/modules/mapping/map_helpers/network_builder/power_cable.dm @@ -40,7 +40,7 @@ /obj/map_helper/network_builder/power_cable/build() if(!network_directions) return - var/knot = KNOT_FORCED || (KNOT_AUTO && detect_knot()) + var/knot = (src.knot == KNOT_FORCED) || ((src.knot == KNOT_AUTO) && detect_knot()) if(knot) for(var/i in GLOB.cardinal) if(!(network_directions & i)) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index a42907e7eed..21b9d64b89c 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -4,6 +4,7 @@ voice_name = "unknown" icon = 'icons/effects/effects.dmi' //We have an ultra-complex update icons that overlays everything, don't load some stupid random male human icon_state = "nothing" + SET_APPEARANCE_FLAGS(TILE_BOUND | PIXEL_SCALE | KEEP_TOGETHER) /// To check if we've need to roll for damage on movement while an item is imbedded in us. var/embedded_flag diff --git a/code/modules/mob/new_player/login.dm b/code/modules/mob/new_player/login.dm index fd95d20c3b5..192471fa842 100644 --- a/code/modules/mob/new_player/login.dm +++ b/code/modules/mob/new_player/login.dm @@ -19,9 +19,6 @@ GLOBAL_DATUM_INIT(lobby_image, /obj/effect/lobby_image, new) icon_state = known_icon_states[1] . = ..() -/mob/new_player - var/client/my_client // Need to keep track of this ourselves, since by the time Logout() is called the client has already been nulled - /mob/new_player/Login() update_Login_details() //handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying @@ -37,7 +34,6 @@ GLOBAL_DATUM_INIT(lobby_image, /obj/effect/lobby_image, new) mind.current = src loc = null - my_client = client GLOB.player_list |= src new_player_panel() @@ -45,5 +41,4 @@ GLOBAL_DATUM_INIT(lobby_image, /obj/effect/lobby_image, new) if(client) handle_privacy_poll() client.playtitlemusic() - client.is_preference_enabled(/datum/client_preference/scaling_viewport) ? client.OnResize() : client.change_view(world.view) return ..() diff --git a/code/modules/multi-tile/multi-tile.dm b/code/modules/multi-tile/multi-tile.dm index 3300a9e18ea..e19320086a0 100644 --- a/code/modules/multi-tile/multi-tile.dm +++ b/code/modules/multi-tile/multi-tile.dm @@ -21,11 +21,11 @@ // check enter if(!T2.Enter(src, T)) return FALSE - + // uh oh, second loop, check exits for(var/turf/T as anything in locs) if(!(T in moving_to)) if(!T.Exit(src, get_step(T, stepdir))) return FALSE - + return TRUE diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm index 15da416aabc..2baf2c7a352 100644 --- a/code/modules/multiz/turf.dm +++ b/code/modules/multiz/turf.dm @@ -227,6 +227,7 @@ bottom_turf.plane = src.plane bottom_turf.color = below.color bottom_turf.copy_overlays(below) + // todo: zmimic bottom_turf.appearance_flags = KEEP_TOGETHER underlays = list(bottom_turf) diff --git a/code/modules/preferences/_preferences.dm b/code/modules/preferences/_preferences.dm index 407f2d43852..ca4e3175bb0 100644 --- a/code/modules/preferences/_preferences.dm +++ b/code/modules/preferences/_preferences.dm @@ -40,6 +40,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/list/character /// global data var/list/options + /// data for byond skin - checkboxes and whatnot; this is ENTIRELY synchronized by the skin system. + var/list/skin //! ## Game Preferences var/tgui_fancy = TRUE @@ -252,6 +254,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) load_path(client.ckey) if(load_preferences()) if(load_character()) + load_skin() sanitize_everything() player_setup.sanitize_setup() client.update_movement_keys() diff --git a/code/modules/preferences/data.dm b/code/modules/preferences/data.dm index f23ce4a764c..e8222a277bd 100644 --- a/code/modules/preferences/data.dm +++ b/code/modules/preferences/data.dm @@ -268,3 +268,11 @@ for(var/key in skipped) errors?.Add("Skipping key [key] - no datum found.") return TRUE + +//! Skin stuff +/datum/preferences/proc/set_skin_data(key, value) + skin[key] = value + save_skin() + +/datum/preferences/proc/get_skin_data(key) + return skin[key] diff --git a/code/modules/preferences/preference_setup/global/setting_datums.dm b/code/modules/preferences/preference_setup/global/setting_datums.dm index 24beca3aa08..54d35a75597 100644 --- a/code/modules/preferences/preference_setup/global/setting_datums.dm +++ b/code/modules/preferences/preference_setup/global/setting_datums.dm @@ -293,13 +293,6 @@ var/list/_client_preferences_by_type disabled_description = "Hide" enabled_by_default = TRUE -/datum/client_preference/scaling_viewport - description = "Scaling Viewport" - key = "scaling_viewport" - enabled_description = "Show" - disabled_description = "Hide" - enabled_by_default = TRUE - /******************** * Staff Preferences * ********************/ diff --git a/code/modules/preferences/preferences_savefile.dm b/code/modules/preferences/preferences_savefile.dm index ae69e8930b2..37f037d9be6 100644 --- a/code/modules/preferences/preferences_savefile.dm +++ b/code/modules/preferences/preferences_savefile.dm @@ -8,7 +8,7 @@ if(!path) return FALSE // check DOS guard - if(world.time < loadprefcooldown) //This is done before checking if the file exists to ensure that the server can't hang on read attempts + if(initialized && world.time < loadprefcooldown) //This is done before checking if the file exists to ensure that the server can't hang on read attempts if(istype(client)) to_chat(client, "You're attempting to load your preferences a little too fast. Wait half a second, then try again.") return FALSE @@ -48,7 +48,7 @@ if(!path) return FALSE // check DOS guard - if(world.time < saveprefcooldown) + if(initialized && world.time < saveprefcooldown) if(istype(client)) to_chat(client, "You're attempting to save your preferences a little too fast. Wait half a second, then try again.") return FALSE @@ -69,12 +69,42 @@ auto_flush_errors() return TRUE +/datum/preferences/proc/save_skin() + if(!path) + return FALSE + // heyooo no interruptions please + if(initialized) + UNTIL(world.time >= saveprefcooldown) + var/savefile/S = new /savefile(path) + if(!S) + return FALSE + S.cd = "/" + skin = sanitize_islist(skin) + WRITE_FILE(S["skin"], skin) + return TRUE + +/datum/preferences/proc/load_skin() + if(!path) + return FALSE + // heyooo no interruptions please + if(initialized) + UNTIL(world.time >= loadprefcooldown) + if(!fexists(path)) + return FALSE + var/savefile/S = new /savefile(path) + if(!S) + return FALSE + S.cd = "/" + READ_FILE(S["skin"], skin) + skin = sanitize_islist(skin) + return TRUE + /datum/preferences/proc/load_character(slot) // todo: storage handler datums... if(!path) return FALSE // check DOS guard - if(world.time < loadcharcooldown) + if(initialized && world.time < loadcharcooldown) if(istype(client)) to_chat(client, "You're attempting to load your character a little too fast. Wait half a second, then try again.") return FALSE @@ -134,7 +164,7 @@ if(!path) return FALSE // check DOS guard - if(world.time < savecharcooldown) + if(initialized && world.time < savecharcooldown) if(istype(client)) to_chat(client, "You're attempting to save your character a little too fast. Wait half a second, then try again.") return FALSE diff --git a/code/modules/preferences/preferences_toggle_procs.dm b/code/modules/preferences/preferences_toggle_procs.dm index 145e5487889..47e552c6ca6 100644 --- a/code/modules/preferences/preferences_toggle_procs.dm +++ b/code/modules/preferences/preferences_toggle_procs.dm @@ -306,19 +306,6 @@ feedback_add_details("admin_verb","TOHChat") -/client/verb/toggle_scaling_viewport() - set name = "Toggle Scaling Viewport" - set category = "Preferences" - set desc = "Enable/Disable Viewport Scaling." - - var/pref_path = /datum/client_preference/scaling_viewport - toggle_preference(pref_path) - SScharacters.queue_preferences_save(prefs) - - to_chat(src, "You will now [(is_preference_enabled(/datum/client_preference/scaling_viewport)) ? "see" : "not see"] more...") - is_preference_enabled(/datum/client_preference/scaling_viewport) ? OnResize() : change_view(world.view) - feedback_add_details("admin_verb","TOHChat") - //Toggles for Staff //Developers diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 24fb462a674..124ece8a9f3 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -430,9 +430,8 @@ var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32 //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average. - var/list/screenview = user.client? getviewsize(user.client.view) : world.view - var/screenviewX = screenview[1] * world.icon_size - var/screenviewY = screenview[2] * world.icon_size + var/screenviewX = user.client.current_viewport_width * world.icon_size + var/screenviewY = user.client.current_viewport_height * world.icon_size var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm index 601f6af4d93..edbc733d326 100644 --- a/code/modules/tgui/states.dm +++ b/code/modules/tgui/states.dm @@ -29,8 +29,8 @@ // Regular ghosts can always at least view if in range. if(user.client) - var/clientviewlist = getviewsize(user.client.view) - if(get_dist(src_object, user) < max(clientviewlist[1], clientviewlist[2])) + // todo: in view range for zooming + if(get_dist(src_object, user) < max(CEILING(user.client.current_viewport_width / 2, 1), CEILING(user.client.current_viewport_height / 2, 1))) . = max(., UI_UPDATE) // Check if the state allows interaction diff --git a/code/modules/tgui/states/default.dm b/code/modules/tgui/states/default.dm index f25f85b785f..59167784ebc 100644 --- a/code/modules/tgui/states/default.dm +++ b/code/modules/tgui/states/default.dm @@ -31,8 +31,8 @@ GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new) return // Robots can interact with anything they can see. - var/list/clientviewlist = getviewsize(client.view) - if(get_dist(src, src_object) <= min(clientviewlist[1],clientviewlist[2])) + // todo: in view range for zooming + if(get_dist(src, src_object) <= min(CEILING(client.current_viewport_width / 2, 1), CEILING(client.current_viewport_height / 2, 1))) return UI_INTERACTIVE return UI_DISABLED // Otherwise they can keep the UI open. diff --git a/code/modules/tgui/status_composers.dm b/code/modules/tgui/status_composers.dm index 73f3b4ac37d..a061b6c6208 100644 --- a/code/modules/tgui/status_composers.dm +++ b/code/modules/tgui/status_composers.dm @@ -35,8 +35,7 @@ // Regular ghosts can always at least view if in range. var/datum/client_interface/client = GET_CLIENT(user) if(client) - var/clientviewlist = getviewsize(client.view) - if(get_dist(source, user) < max(clientviewlist[1], clientviewlist[2])) + if(get_dist(source, user) < max(client.current_viewport_width, client.current_viewport_height)) return UI_UPDATE return UI_CLOSE @@ -74,8 +73,8 @@ /mob/living/silicon/robot/get_ui_access(atom/source) // Robots can interact with anything they can see. - var/list/clientviewlist = getviewsize(client.view) - if(get_dist(src, source) <= min(clientviewlist[1],clientviewlist[2])) + // todo: in view range for zooming + if(get_dist(src, source) <= min(CEILING(client.current_viewport_width / 2, 1), CEILING(client.current_viewport_height / 2, 1))) return UI_INTERACTIVE return UI_DISABLED // Otherwise they can keep the UI open. diff --git a/code/modules/tooltip/tooltip.dm b/code/modules/tooltip/tooltip.dm index 79a5c8b0556..0e07254c3ae 100644 --- a/code/modules/tooltip/tooltip.dm +++ b/code/modules/tooltip/tooltip.dm @@ -75,8 +75,7 @@ Notes: params = {"{ "cursor": "[params]", "screenLoc": "[thing.screen_loc]" }"} //Send stuff to the tooltip - var/view_size = getviewsize(owner.view) - owner << output(list2params(list(params, view_size[1] , view_size[2], "[title][content]", theme, special)), "[control]:tooltip.update") + owner << output(list2params(list(params, owner.current_viewport_width, owner.current_viewport_height, "[title][content]", theme, special)), "[control]:tooltip.update") //If a hide() was hit while we were showing, run hide() again to avoid stuck tooltips showing = 0 diff --git a/config/entries/game_options.txt b/config/entries/game_options.txt index 55729334e71..53df494473d 100644 --- a/config/entries/game_options.txt +++ b/config/entries/game_options.txt @@ -1,3 +1,10 @@ +## Max distance for clients to view with widescren mode enabled. Maximum: 35 in either dimension. +MAX_VIEWPORT_SIZE 19x15 +## Effective distance used for world.view, affects stuff like AI target aquisition. Maximum: 35 in either dimension. +## GAME_VIEWPORT_SIZE 15x19 +## AT THE CURRENT TIME, BYOND WORLD.VIEW IS READ-ONLY +## THIS CONFIGURATION IS LEFT HERE AS A TODO. + ## Set to 0 to disable holidays (you monster) ALLOW_HOLIDAYS @@ -10,7 +17,3 @@ ENGINE_SUBMAP EngineSubmap_RUST 40 ENGINE_SUBMAP EngineSubmap_Tesla 20 ## Singulo ENGINE_SUBMAP EngineSubmap_Singulo 20 - -## Max distance for clients to view with widescren mode enabled. -MAX_CLIENT_VIEW_X 21 -MAX_CLIENT_VIEW_Y 15 diff --git a/interface/menus/_menus.dm b/interface/menus/_menus.dm new file mode 100644 index 00000000000..0d766f8312b --- /dev/null +++ b/interface/menus/_menus.dm @@ -0,0 +1,334 @@ +//! skin menus +/** + * dynamic in-code menu system with blackjack and hookers + */ +/datum/skin_menu + /// id - used for both skin and savefiles + var/id + /// id of window to bind to; we will OVERWRITE ANY EXISTING MENU. + var/bind_to + /// category datum types - transformed into datums at runtime + var/list/datum/skin_menu_category/categories + /// during initial build, we will construct groups list, group name associated to button id for default + var/list/button_groups + +/datum/skin_menu/New() + init_categories() + +/datum/skin_menu/Destroy() + QDEL_LIST(categories) + button_groups = null + return ..() + +/datum/skin_menu/proc/init_categories() + button_groups = list() + for(var/i in 1 to categories.len) + if(ispath(categories[i])) + var/path = categories[i] + var/datum/skin_menu_category/C = new path(src) + categories[i] = C + // category will init in /New(), so... + for(var/datum/skin_menu_entry/E as anything in C.entries) + // again let it runtime if it's bad, we WANT it to runtime so integration tests fail + if(E.group) + button_groups |= E.group + if(E.is_default) + button_groups[E.group] = E.id + else + continue // wtf? let it runtime later + // verify + for(var/group in button_groups) + if(!button_groups[group]) + stack_trace("no default for group [group]") + +/datum/skin_menu/proc/creation_list(client/C) + . = list() + // create categories after + for(var/datum/skin_menu_category/cat as anything in categories) + . |= cat.creation_list(C) + +/datum/skin_menu/proc/setup(client/C, load = TRUE) + create_and_bind(C) + if(load) + load_settings(C) + +/datum/skin_menu/proc/all_entries() + . = list() + for(var/datum/skin_menu_category/C as anything in categories) + for(var/datum/skin_menu_entry/E as anything in C.entries) + if(istype(C, /datum/skin_menu_entry/spacer)) + continue + . += E + +/datum/skin_menu/proc/load_settings(client/C) + // let's like, not do that before they load + UNTIL(C.prefs.initialized) + // basically we need to load saved settings, and default if not + for(var/datum/skin_menu_entry/E as anything in all_entries()) + if(!E.checkbox || !E.is_saved) + continue // don't care + // don't load groups, they only load enable, not disable + if(E.group) + // we, do, however, want to load *unchecking* it. + winset(C, E.id, "is-checked=false") + continue + // load anything else + var/enabled = C.prefs.skin[E.id] + if(isnull(enabled)) + // default + enabled = E.is_default + // save it while we're at it + E.save(C, enabled) + E.load(C, enabled) + // we do need to load groups if possible + for(var/group in button_groups) + var/choice = C.prefs.skin[group] + var/datum/skin_menu_entry/E + if(isnull(choice) || !GLOB.skin_menu_entries[choice]) + // default if null or if choice doesn't exist anymore + E = GLOB.skin_menu_entries[button_groups[group]] + // save it while we're at it + E.save(C, TRUE) + else + E = GLOB.skin_menu_entries[choice] + E.load(C, TRUE) + +/datum/skin_menu/proc/create_and_bind(client/C) + // menu creation is special + // we must winclone; skin must not have a control named "menu" + winclone(C, SKIN_ID_ABSTRACT_MENU, id) + var/list/creation = creation_list(C) + for(var/id in creation) + winset(C, id, creation[id]) + winset(C, bind_to, "menu=[id]") + +//! skin menu categories +/datum/skin_menu_category + /// id - used for both skin and savefiles + var/id + /// name + var/name + /// entry datum types - transformed into datums at runtime; insert a null to create a divider + var/list/datum/skin_menu_entry/entries + /// menu we belong to + var/datum/skin_menu/menu + +/datum/skin_menu_category/New(datum/skin_menu/menu) + src.menu = menu + init_entries() + +/datum/skin_menu_category/Destroy() + QDEL_LIST(entries) + return ..() + +/datum/skin_menu_category/proc/init_entries() + for(var/i in 1 to entries.len) + var/path = entries[i] + var/datum/skin_menu_entry/entry + if(isnull(path)) + entry = new /datum/skin_menu_entry/spacer(src) + else if(ispath(path)) + entry = new path(src) + else + continue // wtf? let it runtime later + entries[i] = entry + +/datum/skin_menu_category/proc/creation_list(client/C) + . = list() + // create us first + .[id] = list2params(list( + "parent" = menu.id, + "name" = name, + )) + // create our children + for(var/datum/skin_menu_entry/E as anything in entries) + .[E.id] = E.cached_constructor + +//! skin menu entries +/// lookup for skin menu entries +GLOBAL_LIST_EMPTY(skin_menu_entries) +/datum/skin_menu_entry + /// id - used for both skin and savefiles + var/id + /// name + var/name + /** + * command to execute; \n to execute multiple. + * this datum will also inject our own command for synchronization automatically. + * generate_command() overrides this. + */ + var/command + /// do we have a button group? checkbox is required for this; we will NOT load disabled for groups! + var/group + /// default enabled for checkable? only one in a group can have this!! infact, only one in a group **MUST** have tihs!! + var/is_default = FALSE + /// checkable? + var/checkbox = FALSE + /// command to execute, if any, when loading prefs if we're checked + var/load_command_enabled + /// command to execute, if any, when loading prefs if we're unchecked + var/load_command_disabled + /// use proc for load command + var/load_command_proc = FALSE + /// default load_command_enabled to command + var/load_command_default = FALSE + /// is saved? only applicable for checkboxes + var/is_saved = TRUE + /// cached creation parameters + var/cached_constructor + /// category we belong to + var/datum/skin_menu_category/category + +/datum/skin_menu_entry/New(datum/skin_menu_category/category) + src.category = category + cache_constructor() + if(load_command_default) + load_command_enabled = generate_command(TRUE) + register() + +/datum/skin_menu_entry/Destroy() + unregister() + return ..() + +/datum/skin_menu_entry/proc/register() + if(GLOB.skin_menu_entries[id]) + stack_trace("warning: collision on [id] between [src] and [GLOB.skin_menu_entries[id]]; overwriting...") + GLOB.skin_menu_entries[id] = src + +/datum/skin_menu_entry/proc/unregister() + GLOB.skin_menu_entries -= id + +/datum/skin_menu_entry/proc/load(client/C, enabled) + if(!checkbox || !is_saved) + return // why do we care? + var/command + if(load_command_proc) + if(enabled) + command = load_command_enabled(C) + else + command = load_command_enabled(C) + else + if(enabled) + command = load_command_enabled + else + command = load_command_disabled + if(command) + C.menu_run_command(command) + if(enabled) + C.menu_buttons_checked[id] = TRUE + if(group) + C.menu_group_status[group] = id + else + C.menu_buttons_checked -= id + // we assume group is taken care of by the other item overwriting it. + winset(C, id, "is-checked=[enabled? "true" : "false"]") + +/datum/skin_menu_entry/proc/save(client/C, enabled) + if(!checkbox || !is_saved) + return // we don't care + if(group) + C.prefs.set_skin_data(group, id) + else + C.prefs.set_skin_data(id, enabled) + +/** + * called when someone presses us; using usr here is fine as this should only be called from verb anyways! + */ +/datum/skin_menu_entry/proc/pressed(client/C, new_checked) + save(C, new_checked) + if(!checkbox) + return // don't care + if(group && new_checked) + C.menu_group_status[group] = id + else + if(new_checked) + C.menu_buttons_checked[id] = TRUE + else + C.menu_buttons_checked -= id + +/datum/skin_menu_entry/proc/load_command_enabled(client/C) + CRASH("why did the proc get called without an override?") + +/datum/skin_menu_entry/proc/load_command_disabled(client/C) + CRASH("why did the proc get called without an override?") + +/** + * generates our normal command + * + * ! WARNING ! + * While loading, you CANNOT use chained commands with \n. + */ +/datum/skin_menu_entry/proc/generate_command(loading) + return command + +/** + * should omit id, that'll be set separately + */ +/datum/skin_menu_entry/proc/creation_parameters() + var/list/params = list() + params["parent"] = category.id + params["command"] = ".menutrigger \"[id]\" \[\[is-checked\]\]\n[generate_command()]" + params["name"] = name + if(group) + params["group"] = group + params["is-checked"] = is_default? "true" : "false" + params["can-check"] = checkbox? "true" : "false" + return list2params(params) + +/datum/skin_menu_entry/proc/cache_constructor() + cached_constructor = creation_parameters() + +/** + * the empty entry used as a spacer + */ +/datum/skin_menu_entry/spacer + id = null + name = null + /// next id + var/static/next_id = 0 + +/datum/skin_menu_entry/spacer/New() + ..() + id = "menubutton_spacer_[++next_id]" + +/datum/skin_menu_entry/spacer/register() + return + +/datum/skin_menu_entry/spacer/unregister() + return + +/datum/skin_menu_entry/spacer/creation_parameters() + return list2params(list( + parent = category.id, + name = "" + )) + +//! client stuff needed to make this work +/client/verb/menu_button_triggered(id as text, checked as num) + set name = ".menutrigger" + set hidden = TRUE + + var/datum/skin_menu_entry/E = GLOB.skin_menu_entries[id] + if(!E) + stack_trace("no entry found for [id], triggered by [src]") + to_chat(usr, SPAN_WARNING("error: no menu entry found; this is a bug. id: [id]")) + return + E.pressed(src, checked) + +/** + * gets which button of a group we have selected + */ +/client/proc/menu_group_query(group) + return menu_group_status[group] + +/** + * gets if a button is checked + */ +/client/proc/menu_button_checked(id) + return menu_buttons_checked[id] + +/** + * executes a command + */ +/client/proc/menu_run_command(str) + winset(src, null, list2params(list("command" = str))) diff --git a/interface/menus/main.dm b/interface/menus/main.dm new file mode 100644 index 00000000000..171a91ff18c --- /dev/null +++ b/interface/menus/main.dm @@ -0,0 +1,229 @@ +GLOBAL_DATUM_INIT(main_window_menu, /datum/skin_menu/main, new) + +/datum/skin_menu/main + id = SKIN_ID_MAIN_MENU + bind_to = SKIN_WINDOW_ID_MAIN + categories = list( + /datum/skin_menu_category/main_file, + /datum/skin_menu_category/main_zoom, + /datum/skin_menu_category/main_scaling, + /datum/skin_menu_category/main_help, + ) + +/datum/skin_menu_category/main_file + id = SKIN_ID_MENU_CATEGORY_FILE + name = "File" + entries = list( + /datum/skin_menu_entry/quick_screenshot, + /datum/skin_menu_entry/screenshot, + /datum/skin_menu_entry/ping, + /datum/skin_menu_entry/reconnect, + /datum/skin_menu_entry/quit, + ) + +/datum/skin_menu_category/main_zoom + id = SKIN_ID_MENU_CATEGORY_ZOOM + name = "Zoom" + entries = list( + /datum/skin_menu_entry/widescreen/automatic, + /datum/skin_menu_entry/widescreen/legacy, + null, + /datum/skin_menu_entry/auto_fit, + /datum/skin_menu_entry/fit_viewport, + null, + /datum/skin_menu_entry/zoom/stretch_to_fit, + /datum/skin_menu_entry/zoom/stretch_to_fill, + ) + +/datum/skin_menu_category/main_zoom/init_entries() + . = ..() + var/static/list/auto_sizes = list( + 32, + 48, + 64, + 72, + 96, + 128, + ) + for(var/size in auto_sizes) + entries += new /datum/skin_menu_entry/zoom/specific(src, size) + +/datum/skin_menu_category/main_scaling + id = SKIN_ID_MENU_CATEGORY_SCALING + name = "Scaling" + entries = list( + /datum/skin_menu_entry/scaling/sharp, + /datum/skin_menu_entry/scaling/normal, + /datum/skin_menu_entry/scaling/blur, + ) + +/datum/skin_menu_category/main_help + id = SKIN_ID_MENU_CATEGORY_HELP + name = "Help" + entries = list( + /datum/skin_menu_entry/adminhelp, + ) + +//! file +/datum/skin_menu_entry/quick_screenshot + id = SKIN_ID_MENU_BUTTON_SCREENSHOT_QUICK + name = "Quick Screenshot" + command = ".screenshot auto" + +/datum/skin_menu_entry/screenshot + id = SKIN_ID_MENU_BUTTON_SCREENSHOT + name = "Screenshot..." + command = ".screenshot" + +/datum/skin_menu_entry/quit + id = SKIN_ID_MENU_BUTTON_QUIT + name = "Quit" + command = ".quit" + +/datum/skin_menu_entry/reconnect + id = SKIN_ID_MENU_BUTTON_RECONNECT + name = "Reconnect" + command = ".reconnect" + +/datum/skin_menu_entry/ping + id = SKIN_ID_MENU_BUTTON_PING + name = "Ping (legacy chat)" + command = ".ping" + +//! zoom +/datum/skin_menu_entry/zoom + group = SKIN_BUTTON_GROUP_MAP_ZOOM + load_command_default = TRUE + checkbox = TRUE + +/datum/skin_menu_entry/zoom/pressed(client/C, new_checked) + . = ..() + if(C.is_auto_fit_viewport_enabled()) + C.request_viewport_fit() + C.request_viewport_update() + +/datum/skin_menu_entry/zoom/load(client/C, enabled) + . = ..() + if(C.is_auto_fit_viewport_enabled()) + C.request_viewport_fit() + C.request_viewport_update() + +/datum/skin_menu_entry/zoom/stretch_to_fit + name = "Stretch to Fit" + id = SKIN_ID_MENU_BUTTON_STRETCH_TO_FIT + is_default = TRUE + +/datum/skin_menu_entry/zoom/stretch_to_fit/generate_command(loading) + if(loading) + return ".winset \"[SKIN_MAP_ID_VIEWPORT].zoom=0;[SKIN_MAP_ID_VIEWPORT].letterbox=true\"" + return ".winset \"[SKIN_MAP_ID_VIEWPORT].zoom=0;[SKIN_MAP_ID_VIEWPORT].letterbox=true\"\n.update_viewport" + +/datum/skin_menu_entry/zoom/stretch_to_fill + name = "Stretch to Fill" + id = SKIN_ID_MENU_BUTTON_STRETCH_NO_LETTERBOX + +/datum/skin_menu_entry/zoom/stretch_to_fill/generate_command(loading) + if(loading) + return ".winset \"[SKIN_MAP_ID_VIEWPORT].zoom=0;[SKIN_MAP_ID_VIEWPORT].letterbox=false\"" + return ".winset \"[SKIN_MAP_ID_VIEWPORT].zoom=0;[SKIN_MAP_ID_VIEWPORT].letterbox=false\"\n.update_viewport" + +//? the normal sizes are created by main_zoom category +/datum/skin_menu_entry/zoom/specific + /// our pixel size + var/pixel_size + +/datum/skin_menu_entry/zoom/specific/New(datum/skin_menu_category/category, pixel_size) + src.pixel_size = pixel_size + src.id = SKIN_ID_MENU_BUTTON_FOR_RESOLUTION(pixel_size) + src.name = "[pixel_size]x[pixel_size]" + ..() + +/datum/skin_menu_entry/zoom/specific/generate_command(loading) + var/zoom = pixel_size / WORLD_ICON_SIZE + if(loading) + return ".winset \"[SKIN_MAP_ID_VIEWPORT].zoom=[zoom]\"" + return ".winset \"[SKIN_MAP_ID_VIEWPORT].zoom=[zoom]\"\n.update_viewport" + +//! widescreen +/datum/skin_menu_entry/widescreen + group = SKIN_BUTTON_GROUP_MAP_WIDESCREEN + checkbox = TRUE + +/datum/skin_menu_entry/widescreen/pressed(client/C, new_checked) + . = ..() + if(C.is_auto_fit_viewport_enabled()) + C.request_viewport_fit() + C.request_viewport_update() + +/datum/skin_menu_entry/widescreen/load(client/C, enabled) + . = ..() + if(C.is_auto_fit_viewport_enabled()) + C.request_viewport_fit() + C.request_viewport_update() + +/datum/skin_menu_entry/widescreen/automatic + id = SKIN_ID_MENU_BUTTON_WIDESCREEN_ENABLED + name = "Automatic Widescreen" + is_default = TRUE + +/datum/skin_menu_entry/widescreen/legacy + id = SKIN_ID_MENU_BUTTON_WIDESCREEN_DISABLED + name = "Lock to 15x15 (Legacy)" + +/client/proc/is_widescreen_enabled() + return menu_group_query(SKIN_BUTTON_GROUP_MAP_WIDESCREEN) == SKIN_ID_MENU_BUTTON_WIDESCREEN_ENABLED + +//! misc zoom +/datum/skin_menu_entry/auto_fit + id = SKIN_ID_MENU_BUTTON_AUTO_FIT_VIEWPORT + name = "Automatically Fit Viewport (EXPERIMENTAL)" + checkbox = TRUE + is_default = FALSE + +/datum/skin_menu_entry/auto_fit/load(client/C, enabled) + . = ..() + if(enabled) + C.request_viewport_fit() + +/client/proc/is_auto_fit_viewport_enabled() + return menu_button_checked(SKIN_ID_MENU_BUTTON_AUTO_FIT_VIEWPORT) + +/datum/skin_menu_entry/fit_viewport + id = SKIN_ID_MENU_BUTTON_FIT_VIEWPORT + name = "Fit Viewport" + +/datum/skin_menu_entry/fit_viewport/pressed(client/C, new_checked) + . = ..() + C.request_viewport_fit() + +//! scaling +/datum/skin_menu_entry/scaling + group = SKIN_BUTTON_GROUP_MAP_SCALING + checkbox = TRUE + load_command_default = TRUE + var/zoom_mode + +/datum/skin_menu_entry/scaling/generate_command(loading) + return ".winset \"[SKIN_MAP_ID_VIEWPORT].zoom-mode=[zoom_mode]\"" + +/datum/skin_menu_entry/scaling/sharp + id = SKIN_ID_MENU_BUTTON_NEAREST_NEIGHBOR + name = "Nearest Neighbor (Sharp)" + zoom_mode = "distort" + is_default = TRUE + +/datum/skin_menu_entry/scaling/normal + id = SKIN_ID_MENU_BUTTON_POINT_SAMPLE + name = "Point Sampling (Normal)" + zoom_mode = "normal" + +/datum/skin_menu_entry/scaling/blur + id = SKIN_ID_MENU_BUTTON_BILINEAR + name = "Bilinear (Blurry)" + zoom_mode = "blur" + +//! help +/datum/skin_menu_entry/adminhelp + id = SKIN_ID_MENU_BUTTON_ADMINHELP + name = "Admin Help (F1)" + command = "adminhelp" diff --git a/interface/skin.dmf b/interface/skin.dmf index 9395c1e4081..3b8304d6e93 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -1,284 +1,59 @@ macro "default" -menu "menu" - elem - name = "&File" - command = "" - saved-params = "is-checked" - elem - name = "&Quick screenshot\tF2" - command = ".screenshot auto" - category = "&File" - saved-params = "is-checked" - elem - name = "&Save screenshot as...\tShift+F2" - command = ".screenshot" - category = "&File" - saved-params = "is-checked" - elem - name = "" - command = "" - category = "&File" - saved-params = "is-checked" - elem - name = "&Reconnect" - command = ".reconnect" - category = "&File" - saved-params = "is-checked" - elem - name = "&Check ping" - command = ".ping" - category = "&File" - saved-params = "is-checked" - elem - name = "&Quit" - command = ".quit" - category = "&File" - saved-params = "is-checked" - elem - name = "&Size" - command = "" - saved-params = "is-checked" - elem "stretch" - name = "&Round up and lock to size" - command = ".winset \"mapwindow.map.icon-size=0\"" - category = "&Size" - is-checked = true - can-check = true - group = "size" - saved-params = "is-checked" - elem "cozyin" - name = "Get Cozy Inwards" - command = "SetCozyViewInwards 8" - category = "&Size" - can-check = false - group = "size" - saved-params = "is-checked" - elem "icon128" - name = "&128x128 (4K)" - command = "SetWindowIconSize 128" - category = "&Size" - can-check = true - group = "size" - saved-params = "is-checked" - elem "icon96" - name = "&96x96" - command = "SetWindowIconSize 96" - category = "&Size" - can-check = true - group = "size" - saved-params = "is-checked" - elem "icon72" - name = "&72x72 (Cozy 1080p)" - command = "SetWindowIconSize 72" - category = "&Size" - is-checked = true - can-check = true - group = "size" - saved-params = "is-checked" - elem "icon64" - name = "&64x64 (1080p)" - command = "SetWindowIconSize 64" - category = "&Size" - is-checked = true - can-check = true - group = "size" - saved-params = "is-checked" - elem "icon48" - name = "&48x48" - command = "SetWindowIconSize 48" - category = "&Size" - can-check = true - group = "size" - saved-params = "is-checked" - elem "icon32" - name = "&32x32" - command = "SetWindowIconSize 32" - category = "&Size" - can-check = true - group = "size" - saved-params = "is-checked" - elem - name = "&Scaling" - command = "" - saved-params = "is-checked" - elem "ZM" - name = "&Smooth Scaling" - command = ".winset \"zoommode.is-checked=true?map.zoom-mode=normal:map.zoom-mode=distort\"" - category = "&Scaling" - is-checked = true - can-check = true - group = "scale" - saved-params = "is-checked" - elem "NN" - name = "&Nearest Neighbor" - command = ".winset \"mapwindow.map.zoom-mode=distort\"" - category = "&Scaling" - can-check = true - group = "scale" - saved-params = "is-checked" - elem "PS" - name = "&Point Sampling" - command = ".winset \"mapwindow.map.zoom-mode=normal\"" - category = "&Scaling" - can-check = true - group = "scale" - saved-params = "is-checked" - elem "BL" - name = "&Bilinear" - command = ".winset \"mapwindow.map.zoom-mode=blur\"" - category = "&Scaling" - can-check = true - group = "scale" - saved-params = "is-checked" - elem - name = "&Help" - command = "" - saved-params = "is-checked" - elem - name = "&Admin help\tF1" - command = "adminhelp" - category = "&Help" - saved-params = "is-checked" - elem - name = "&Hotkeys" - command = "hotkeys-help" - category = "&Help" - saved-params = "is-checked" - - -window "Telecomms IDE" - elem "Telecomms IDE" - type = MAIN - pos = 281,0 - size = 569x582 - anchor1 = none - anchor2 = none - background-color = #ffffff - is-visible = false - saved-params = "pos;size;is-minimized;is-maximized" - title = "TCS IDE" - statusbar = false - on-close = "exittcs" - elem "button5" - type = BUTTON - pos = 209,464 - size = 70x20 - anchor1 = 37,80 - anchor2 = 49,83 - saved-params = "is-checked" - text = "Clear Memory" - command = "tcsclearmem" - elem "button4" - type = BUTTON - pos = 157,464 - size = 52x20 - anchor1 = 28,80 - anchor2 = 37,83 - saved-params = "is-checked" - text = "Revert" - command = "tcsrevert" - elem "button3" - type = BUTTON - pos = 105,464 - size = 52x20 - anchor1 = 18,80 - anchor2 = 28,83 - saved-params = "is-checked" - text = "Execute" - command = "tcsrun" - elem "tcserror" - type = OUTPUT - pos = 0,488 - size = 566x94 - anchor1 = 0,84 - anchor2 = 99,100 - font-family = "sans-serif" - font-size = 9 - saved-params = "max-lines" - elem "button2" - type = BUTTON - pos = 53,464 - size = 52x20 - anchor1 = 9,80 - anchor2 = 18,83 - saved-params = "is-checked" - text = "Compile" - command = "tcscompile" - elem "button1" - type = BUTTON - pos = 0,464 - size = 53x20 - anchor1 = 0,80 - anchor2 = 9,83 - saved-params = "is-checked" - text = "Apply" - command = "tcssave" - elem "tcscode" - type = INPUT - pos = 0,0 - size = 569x464 - anchor1 = 0,0 - anchor2 = 100,80 - font-family = "Courier" - font-size = 10 - saved-params = "" - command = "cancel" - multi-line = true - no-command = true - window "mainwindow" elem "mainwindow" type = MAIN pos = 0,0 - size = 640x440 - anchor1 = none - anchor2 = none + size = 640x480 + anchor1 = -1,-1 + anchor2 = -1,-1 + background-color = none is-default = true saved-params = "pos;size;is-minimized;is-maximized" icon = 'icons\\CS13.png' macro = "default" - menu = "menu" - on-size = "OnResize" + outer-size = 656x538 + inner-size = 640x499 elem "split" type = CHILD - pos = 3,0 - size = 640x440 + pos = 0,0 + size = 640x480 anchor1 = 0,0 anchor2 = 100,100 + background-color = none saved-params = "splitter" left = "mapwindow" right = "infowindow" is-vert = true - on-size = "OnResize" elem "asset_cache_browser" type = BROWSER pos = 0,0 size = 200x200 - anchor1 = none - anchor2 = none + anchor1 = -1,-1 + anchor2 = -1,-1 + background-color = none is-visible = false saved-params = "" elem "tooltip" type = BROWSER pos = 0,0 size = 999x999 - anchor1 = none - anchor2 = none + anchor1 = -1,-1 + anchor2 = -1,-1 + background-color = none is-visible = false saved-params = "" window "mapwindow" elem "mapwindow" type = MAIN - pos = 0,0 + pos = 281,0 size = 640x480 - anchor1 = none - anchor2 = none + anchor1 = -1,-1 + anchor2 = -1,-1 saved-params = "pos;size;is-minimized;is-maximized" is-pane = true - on-size = "OnResize" elem "map" type = MAP pos = 0,0 @@ -287,9 +62,9 @@ window "mapwindow" anchor2 = 100,100 font-family = "Arial" font-size = 7 - text-color = none is-default = true - saved-params = "zoom;letterbox;zoom-mode" + saved-params = "" + on-size = ".on_viewport [[width]] [[height]] [[zoom]] [[letterbox]]" 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; }" window "preferences_window" @@ -297,8 +72,8 @@ window "preferences_window" type = MAIN pos = 281,0 size = 1000x800 - anchor1 = none - anchor2 = none + anchor1 = -1,-1 + anchor2 = -1,-1 is-visible = false saved-params = "pos;size;is-minimized;is-maximized" statusbar = false @@ -323,20 +98,16 @@ window "infowindow" type = MAIN pos = 418,0 size = 640x480 - anchor1 = none - anchor2 = none - background-color = none + anchor1 = -1,-1 + anchor2 = -1,-1 saved-params = "pos;size;is-minimized;is-maximized" is-pane = true - outer-size = 662x561 - inner-size = 640x505 elem "mediapanel" type = BROWSER pos = 88,216 size = 200x200 - anchor1 = none - anchor2 = none - background-color = none + anchor1 = -1,-1 + anchor2 = -1,-1 is-visible = false saved-params = "" elem "info" @@ -345,7 +116,6 @@ window "infowindow" size = 640x445 anchor1 = 0,0 anchor2 = 100,100 - background-color = none saved-params = "splitter" left = "statwindow" right = "outputwindow" @@ -356,7 +126,6 @@ window "infowindow" size = 104x20 anchor1 = 3,0 anchor2 = 19,0 - background-color = none saved-params = "is-checked" text = "Changelog" command = "changelog" @@ -366,7 +135,6 @@ window "infowindow" size = 100x20 anchor1 = 19,0 anchor2 = 34,0 - background-color = none saved-params = "is-checked" text = "Rules" command = "rules" @@ -376,7 +144,6 @@ window "infowindow" size = 100x20 anchor1 = 34,0 anchor2 = 50,0 - background-color = none saved-params = "is-checked" text = "Wiki" command = "wiki" @@ -386,7 +153,6 @@ window "infowindow" size = 100x20 anchor1 = 50,0 anchor2 = 66,0 - background-color = none saved-params = "is-checked" text = "Forum" command = "forum" @@ -396,7 +162,6 @@ window "infowindow" size = 100x20 anchor1 = 66,0 anchor2 = 81,0 - background-color = none saved-params = "is-checked" text = "Github" command = "github" @@ -406,7 +171,6 @@ window "infowindow" size = 100x20 anchor1 = 81,0 anchor2 = 97,0 - background-color = none saved-params = "is-checked" text = "Report Issue" command = "report-issue" @@ -416,8 +180,8 @@ window "outputwindow" type = MAIN pos = 0,0 size = 640x480 - anchor1 = none - anchor2 = none + anchor1 = -1,-1 + anchor2 = -1,-1 saved-params = "pos;size;is-minimized;is-maximized" is-pane = true elem "browseroutput" @@ -436,7 +200,6 @@ window "outputwindow" size = 40x20 anchor1 = 100,100 anchor2 = -1,-1 - background-color = none saved-params = "is-checked" text = "Chat" command = ".winset \"saybutton.is-checked=true?input.command=\"!say \\\"\" macrobutton.is-checked=false:input.command=\"" @@ -457,7 +220,6 @@ window "outputwindow" size = 80x20 anchor1 = 100,100 anchor2 = -1,-1 - background-color = none saved-params = "" text = "Hotkey Toggle" command = ".winset \"mainwindow.macro!=macro ? mainwindow.macro=macro hotkey_toggle.is-checked=false input.focus=true : mainwindow.macro=hotkeymode hotkey_toggle.is-checked=true mapwindow.map.focus=true\"" @@ -474,8 +236,8 @@ window "outputwindow" type = BROWSER pos = 0,0 size = 200x200 - anchor1 = none - anchor2 = none + anchor1 = -1,-1 + anchor2 = -1,-1 is-visible = false saved-params = "" @@ -484,8 +246,8 @@ window "statwindow" type = MAIN pos = 372,0 size = 640x480 - anchor1 = none - anchor2 = none + anchor1 = -1,-1 + anchor2 = -1,-1 saved-params = "pos;size;is-minimized;is-maximized" is-pane = true elem "statbrowser" diff --git a/maps/submaps/_healpers.dm b/maps/submaps/_healpers.dm index 8ce99a51dae..baf6ce160c4 100644 --- a/maps/submaps/_healpers.dm +++ b/maps/submaps/_healpers.dm @@ -40,7 +40,7 @@ anchored = 1 plane = TURF_PLANE layer = ABOVE_TURF_LAYER - appearance_flags = PIXEL_SCALE|KEEP_TOGETHER + SET_APPEARANCE_FLAGS(TILE_BOUND) var/_stair_tag //Make this match another one and they'll connect! diff --git a/tgui/packages/tgui-panel/themes.js b/tgui/packages/tgui-panel/themes.js index 0bee64b7cd4..a972e051db5 100644 --- a/tgui/packages/tgui-panel/themes.js +++ b/tgui/packages/tgui-panel/themes.js @@ -132,10 +132,10 @@ export const setClientTheme = name => { // Say, OOC, me Buttons etc. 'saybutton.background-color': COLOR_DARK_BG, 'saybutton.text-color': COLOR_DARK_TEXT, - 'oocbutton.background-color': COLOR_DARK_BG, - 'oocbutton.text-color': COLOR_DARK_TEXT, - 'mebutton.background-color': COLOR_DARK_BG, - 'mebutton.text-color': COLOR_DARK_TEXT, + // 'oocbutton.background-color': COLOR_DARK_BG, + // 'oocbutton.text-color': COLOR_DARK_TEXT, + // 'mebutton.background-color': COLOR_DARK_BG, + // 'mebutton.text-color': COLOR_DARK_TEXT, 'asset_cache_browser.background-color': COLOR_DARK_BG, 'asset_cache_browser.text-color': COLOR_DARK_TEXT, 'tooltip.background-color': COLOR_DARK_BG, @@ -182,10 +182,10 @@ export const setClientTheme = name => { // Say, OOC, me Buttons etc. 'saybutton.background-color': COLOR_DARK_BG, 'saybutton.text-color': COLOR_DARK_TEXT, - 'oocbutton.background-color': COLOR_DARK_BG, - 'oocbutton.text-color': COLOR_DARK_TEXT, - 'mebutton.background-color': COLOR_DARK_BG, - 'mebutton.text-color': COLOR_DARK_TEXT, + // 'oocbutton.background-color': COLOR_DARK_BG, + // 'oocbutton.text-color': COLOR_DARK_TEXT, + // 'mebutton.background-color': COLOR_DARK_BG, + // 'mebutton.text-color': COLOR_DARK_TEXT, 'asset_cache_browser.background-color': COLOR_DARK_BG, 'asset_cache_browser.text-color': COLOR_DARK_TEXT, 'tooltip.background-color': COLOR_DARK_BG,