From b1a4aee019457f27aa95fdb26650d07df99bd6a2 Mon Sep 17 00:00:00 2001 From: Chaplain Maximum Date: Fri, 21 Jun 2024 22:20:19 +0300 Subject: [PATCH 1/2] Polaris Portals --- mods/_fd/polaris_portals/README.md | 78 ++ mods/_fd/polaris_portals/_polaris_portals.dm | 4 + mods/_fd/polaris_portals/_polaris_portals.dme | 13 + mods/_fd/polaris_portals/code/map_effect.dm | 16 + mods/_fd/polaris_portals/code/portal.dm | 335 +++++ .../example/portal_showcase.dm | 4 + .../example/portal_showcase.dmm | 1188 +++++++++++++++++ .../_fd/polaris_portals/icons/map_effects.dmi | Bin 0 -> 10379 bytes 8 files changed, 1638 insertions(+) create mode 100644 mods/_fd/polaris_portals/README.md create mode 100644 mods/_fd/polaris_portals/_polaris_portals.dm create mode 100644 mods/_fd/polaris_portals/_polaris_portals.dme create mode 100644 mods/_fd/polaris_portals/code/map_effect.dm create mode 100644 mods/_fd/polaris_portals/code/portal.dm create mode 100644 mods/_fd/polaris_portals/example/portal_showcase.dm create mode 100644 mods/_fd/polaris_portals/example/portal_showcase.dmm create mode 100644 mods/_fd/polaris_portals/icons/map_effects.dmi diff --git a/mods/_fd/polaris_portals/README.md b/mods/_fd/polaris_portals/README.md new file mode 100644 index 0000000000000..6af4c996ddd80 --- /dev/null +++ b/mods/_fd/polaris_portals/README.md @@ -0,0 +1,78 @@ + +#### Список PRов: + +- https://github.com/SierraBay/SierraBay12/pull/##### + + + +## Мод-пример + +ID мода: POLARIS_PORTALS + + +### Описание мода + +https://github.com/Baystation12/Baystation12/commit/0ce2431d7948c1d5cb6c77dbf629c1075db35802 + + +### Изменения *кор кода* + +- `code/modules/mob/living.dm`: `proc/overriden_proc`, `var/overriden_var` + + +### Оверрайды + +- `mods/_master_files/sound/my_cool_sound.ogg` +- `mods/_master_files/code/my_modular_override.dm`: `proc/overriden_proc`, `var/overriden_var` + + +### Дефайны + +- `code/__defines/~mods/polaris_portals.dm`: `POLARIS_PORTALS_SPEED_MULTIPLIER`, `POLARIS_PORTALS_SPEED_BASE` + + +### Используемые файлы, не содержащиеся в модпаке + +- `mods/_master_files/icons/obj/alien.dmi` + + +### Авторы: + +Твой никнейм + diff --git a/mods/_fd/polaris_portals/_polaris_portals.dm b/mods/_fd/polaris_portals/_polaris_portals.dm new file mode 100644 index 0000000000000..28cc3de1709dd --- /dev/null +++ b/mods/_fd/polaris_portals/_polaris_portals.dm @@ -0,0 +1,4 @@ +/singleton/modpack/polaris_portals + name = "Мод-пример" + desc = "Мод, который является примером и ни в коем случае не должен быть использован." + author = "Твой никнейм" diff --git a/mods/_fd/polaris_portals/_polaris_portals.dme b/mods/_fd/polaris_portals/_polaris_portals.dme new file mode 100644 index 0000000000000..95ace06fce0f0 --- /dev/null +++ b/mods/_fd/polaris_portals/_polaris_portals.dme @@ -0,0 +1,13 @@ +#ifndef MODPACK_POLARIS_PORTALS +#define MODPACK_POLARIS_PORTALS + +#define ABOVE_AO_LAYER 2.141 + +#include "_polaris_portals.dm" + +#include "code/map_effect.dm" +#include "code/portal.dm" + +#include "example/portal_showcase.dm" + +#endif diff --git a/mods/_fd/polaris_portals/code/map_effect.dm b/mods/_fd/polaris_portals/code/map_effect.dm new file mode 100644 index 0000000000000..104e238048f7f --- /dev/null +++ b/mods/_fd/polaris_portals/code/map_effect.dm @@ -0,0 +1,16 @@ +// These are objects you can use inside special maps or for adminbuse. +// Players cannot see or interact with these. +// Ported from polaris, doesn't actually do much other than provide a definition portals can use. +/obj/map_effect + anchored = TRUE + invisibility = 99 // So a badmin can go view these by changing their see_invisible. + icon = 'mods/_fd/polaris_portals/icons/map_effects.dmi' + +/obj/map_effect/ex_act() + return + +/obj/map_effect/singularity_pull() + return + +/obj/map_effect/singularity_act() + return diff --git a/mods/_fd/polaris_portals/code/portal.dm b/mods/_fd/polaris_portals/code/portal.dm new file mode 100644 index 0000000000000..867fafa761ca0 --- /dev/null +++ b/mods/_fd/polaris_portals/code/portal.dm @@ -0,0 +1,335 @@ +GLOBAL_LIST_EMPTY(all_portal_masters) + +/* +Portal map effects allow a mapper to join two distant places together, while looking somewhat seamlessly connected. +This can allow for very strange PoIs that twist and turn in what appear to be physically impossible ways. +Portals do have some specific requirements when mapping them in; + - There must by one, and only one `/obj/effect/map_effect/portal/master` for each side of a portal. + - Both sides need to have matching `portal_id`s in order to link to each other. + - Each side must face opposite directions, e.g. if side A faces SOUTH, side B must face NORTH. + - Clarification on the above - you will be moved in the direction that the portal faces. + If Side A faces south, you will be moved south. Dirs are 1/2/4/8, 1: NORTH, 2: SOUTH, 4: EAST, 8: WEST. + To further explain: If your cave entrance is on the NORTH side of the map on ENTRY side, and SOUTH side on EXIT side: + You will need to set the ENTRY side's dir to 2, IE SOUTH, as that's the direction you will moving coming FROM the EXIT side. + IE: Directions should be set based on the direction of travel. + - Each side must have the same orientation, e.g. horizontal on both sides, or vertical on both sides. + - Portals can be made to be longer than 1x1 with `/obj/effect/map_effect/portal/line`s, + but both sides must have the same length. + - If portal lines are added, they must form a straight line and be next to a portal master or another portal line. + - If portal lines are used, both portal masters should be in the same relative position among the lines. + E.g. both being on the left most side on a horizontal row. +Portals also have some limitations to be aware of when mapping. Some of these are not an issue if you're trying to make an 'obvious' portal; + - The objects seen through portals are purely visual, which has many implications, + such as simple_mob AIs being blind to mobs on the other side of portals. + - Objects on the other side of a portal can be interacted with if the interaction has no range limitation, + or the distance between the two portal sides happens to be less than the interaction max range. Examine will probably work, + while picking up an item that appears to be next to you will fail. + - Sounds currently are not carried across portals. + - Mismatched lighting between each portal end can make the portal look obvious. + - Portals look weird when observing as a ghost, or otherwise when able to see through walls. Meson vision will also spoil the illusion. + - Walls that change icons based on neightboring walls can give away that a portal is nearby if both sides don't have a similar transition. + - Projectiles that pass through portals will generally work as intended, however aiming and firing upon someone on the other side of a portal + will likely be weird due to the click targeting the real position of the thing clicked instead of the apparent position. + Thrown objects suffer a similar fate. + - The tiles that are visually shown across a portal are determined based on visibility at the time of portal initialization, + and currently don't update, meaning that opacity changes are not reflected, e.g. a wall is deconstructed, or an airlock is opened. + - There is currently a small but somewhat noticable pause in mob movement when moving across a portal, + as a result of the mob's glide animation being inturrupted by a teleport. + - Gas is not transferred through portals, and ZAS is oblivious to them. +A lot of those limitations can potentially be solved with some more work. Otherwise, portals work best in static environments like Points of Interest, +when portals are shortly lived, or when portals are made to be obvious with special effects. +*/ + +/obj/map_effect/portal + name = "portal subtype" + invisibility = 0 + opacity = TRUE + plane = DEFAULT_PLANE + layer = ABOVE_AO_LAYER + appearance_flags = PIXEL_SCALE|KEEP_TOGETHER|TILE_BOUND // Readdded TILE_BOUND so that non visible portal's contents remain hidden from the viewer. + + var/obj/map_effect/portal/counterpart = null // The portal line or master that this is connected to, on the 'other side'. + + // Information used to apply `pixel_[x|y]` offsets so that the visuals line up. + // Set automatically by `calculate_dimensions()`. + var/total_height = 0 // Measured in tiles. + var/total_width = 0 + + var/portal_distance_x = 0 // How far the portal is from the left edge, in tiles. + var/portal_distance_y = 0 // How far the portal is from the top edge. + +/obj/map_effect/portal/Destroy() + vis_contents = null + if(counterpart) + counterpart.counterpart = null // Disconnect our counterpart from us + counterpart = null // Now disconnect us from them. + return ..() + +// Called when something touches the portal, and usually teleports them to the other side. +/obj/map_effect/portal/Crossed(atom/movable/AM) + //Todo, some check for incorporeal stuff or virtual mobs perhaps + ..() + if(!AM) + return + if(!counterpart) + return + + go_through_portal(AM) + + +/obj/map_effect/portal/proc/go_through_portal(atom/movable/AM) + var/turf/T = counterpart.get_focused_turf() + AM.forceMove(T) + if(isliving(AM)) + var/mob/living/L = AM + if(L.pulling) + var/atom/movable/AMP = L.pulling + AMP.forceMove(T) + +// 'Focused turf' is the turf directly in front of a portal, +// and it is used both as the destination when crossing, as well as the PoV for visuals. +/obj/map_effect/portal/proc/get_focused_turf() + return get_step(get_turf(src), dir) + +// Determines the size of the block of turfs inside `vis_contents`, and where the portal is in relation to that. +/obj/map_effect/portal/proc/calculate_dimensions() + var/highest_x = 0 + var/lowest_x = 0 + + var/highest_y = 0 + var/lowest_y = 0 + + // First pass is for finding the top right corner. + for(var/thing in vis_contents) + var/turf/T = thing + if(T.x > highest_x) + highest_x = T.x + if(T.y > highest_y) + highest_y = T.y + + lowest_x = highest_x + lowest_y = highest_y + + // Second one is for the bottom left corner. + for(var/thing in vis_contents) + var/turf/T = thing + if(T.x < lowest_x) + lowest_x = T.x + if(T.y < lowest_y) + lowest_y = T.y + + // Now calculate the dimensions. + total_width = (highest_x - lowest_x) + 1 + total_height = (highest_y - lowest_y) + 1 + + // Find how far the portal is from the edges. + var/turf/focused_T = counterpart.get_focused_turf() + portal_distance_x = lowest_x - focused_T.x + portal_distance_y = lowest_y - focused_T.y + + +// Portal masters manage everything else involving portals. +// This is the base type. Use `/side_a` or `/side_b` with matching IDs for actual portals. +/obj/map_effect/portal/master + name = "portal master" + var/portal_id = "test" // For a portal to be made, both the A and B sides need to share the same ID value. + var/list/portal_lines = list() + +/obj/map_effect/portal/master/Initialize() + GLOB.all_portal_masters += src + find_lines() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/map_effect/portal/master/LateInitialize() + find_counterparts() + make_visuals() + apply_offset() + +/obj/map_effect/portal/master/Destroy() + GLOB.all_portal_masters -= src + for(var/thing in portal_lines) + qdel(thing) + return ..() + +/obj/map_effect/portal/master/proc/find_lines() + var/list/dirs_to_search = list( turn(dir, 90), turn(dir, -90) ) + + for(var/dir_to_search in dirs_to_search) + var/turf/current_T = get_turf(src) + while(current_T) + current_T = get_step(current_T, dir_to_search) + var/obj/map_effect/portal/line/line = locate() in current_T + if(line) + portal_lines += line + line.my_master = src + else + break + +// Connects both sides of a portal together. +/obj/map_effect/portal/master/proc/find_counterparts() + for(var/thing in GLOB.all_portal_masters) + var/obj/map_effect/portal/master/M = thing + if(M == src) + continue + if(M.counterpart) + continue + + if(M.portal_id == src.portal_id) + counterpart = M + M.counterpart = src + if(portal_lines.len) + for(var/i = 1 to portal_lines.len) + var/obj/map_effect/portal/line/our_line = portal_lines[i] + var/obj/map_effect/portal/line/their_line = M.portal_lines[i] + our_line.counterpart = their_line + their_line.counterpart = our_line + break + + if(!counterpart) + crash_with("Portal master [type] ([x],[y],[z]) could not find another portal master with a matching portal_id ([portal_id]).") + +/obj/map_effect/portal/master/proc/make_visuals() + var/list/observed_turfs = list() + for(var/thing in portal_lines + src) + var/obj/map_effect/portal/P = thing + P.name = null + P.icon_state = null + + if(!P.counterpart) + return + + var/turf/T = P.counterpart.get_focused_turf() + P.vis_contents += T + + var/list/things = dview(world.view, T) + for(var/turf/turf in things) + if(!(get_dir(turf, T) & GLOB.reverse_dir[P.dir])) //dont grab things behind but sides should be fine + if(turf in observed_turfs) // Avoid showing the same turf twice or more for improved performance. + continue + + P.vis_contents += turf + observed_turfs += turf + + P.calculate_dimensions() + +// Shifts the portal's pixels in order to line up properly, as BYOND offsets the sprite when it holds multiple turfs inside `vis_contents`. +// This undos the shift that BYOND did. +/obj/map_effect/portal/master/proc/apply_offset() + for(var/thing in portal_lines + src) + var/obj/map_effect/portal/P = thing + + P.pixel_x = WORLD_ICON_SIZE * P.portal_distance_x + P.pixel_y = WORLD_ICON_SIZE * P.portal_distance_y + +// Allows portals to transfer emotes. +// Only portal masters do this to avoid flooding the other side with duplicate messages. +/obj/map_effect/portal/master/see_emote(mob/M, text) + if(!counterpart) + return + var/turf/T = counterpart.get_focused_turf() + var/list/mobs_to_relay = list() + var/list/objs = list() + get_mobs_and_objs_in_view_fast(T, world.view, mobs_to_relay, objs, checkghosts = FALSE) + + for(var/thing in mobs_to_relay) + var/mob/mob = thing + var/rendered = SPAN_CLASS("message", "[text]") + mob.show_message(rendered, 2) + + ..() + +// Allows portals to transfer visible messages. +/obj/map_effect/portal/master/show_message(msg, type, alt, alt_type) + if(!counterpart) + return + var/turf/T = counterpart.get_focused_turf() + var/list/mobs_to_relay = list() + var/list/objs = list() + get_mobs_and_objs_in_view_fast(T, world.view, mobs_to_relay, objs, checkghosts = FALSE) + + for(var/thing in mobs_to_relay) + var/mob/mob = thing + mob.show_message(msg, type, alt, alt_type) + + ..() + +// Allows portals to transfer speech. +/obj/map_effect/portal/master/hear_talk(mob/M, text, verb, datum/language_speaking) + if(!counterpart) + return + var/mob/living/L = M + var/turf/T = counterpart.get_focused_turf() + var/list/mobs_to_relay = list() + var/list/objs = list() + get_mobs_and_objs_in_view_fast(T, world.view, mobs_to_relay, objs, checkghosts = FALSE) + + for(var/thing in mobs_to_relay) + var/mob/mob = thing + mob.hear_say(text, verb, language_speaking, istype(L) ? L.GetVoice() : null, null, M) //clunky, unifying chat arguments would be ideal. + + ..() + +// Returns the position that an atom that's hopefully on the other side of the portal would be if it were really there. +// Z levels not taken into account. +//Commented out as bay doesnt use position datums, but could be handy in the future +// /obj/effect/map_effect/portal/master/proc/get_apparent_position(atom/A) +// if(!counterpart) +// return null + +// var/turf/true_turf = get_turf(A) +// var/obj/effect/map_effect/portal/master/other_master = counterpart + +// var/in_vis_contents = FALSE +// for(var/thing in other_master.portal_lines + other_master) +// var/obj/effect/map_effect/portal/P = thing +// if(P in true_turf.vis_locs) +// in_vis_contents = TRUE +// break + +// if(!in_vis_contents) +// return null // Not in vision of the other portal. + +// var/turf/their_focus = counterpart.get_focused_turf() +// var/turf/our_focus = get_focused_turf() + +// var/relative_x = (true_turf.x - our_focus.x) +// relative_x += SIGN(relative_x) +// var/relative_y = (true_turf.y - our_focus.y) +// relative_y += SIGN(relative_y) + +// return new /datum/position(their_focus.x + relative_x, their_focus.y + relative_y, our_focus.z) + + +/obj/map_effect/portal/master/side_a + name = "portal master A" + icon_state = "portal_side_a" +// color = "#00FF00" + +/obj/map_effect/portal/master/side_b + name = "portal master B" + icon_state = "portal_side_b" +// color = "#FF0000" + + + +// Portal lines extend out from the sides of portal masters, +// They let portals be longer than 1x1. +// Both sides MUST be the same length, meaning if side A is 1x3, side B must also be 1x3. +/obj/map_effect/portal/line + name = "portal line" + var/obj/map_effect/portal/master/my_master = null + +/obj/map_effect/portal/line/Destroy() + if(my_master) + my_master.portal_lines -= src + my_master = null + return ..() + +/obj/map_effect/portal/line/side_a + name = "portal line A" + icon_state = "portal_line_side_a" + +/obj/map_effect/portal/line/side_b + name = "portal line B" + icon_state = "portal_line_side_b" diff --git a/mods/_fd/polaris_portals/example/portal_showcase.dm b/mods/_fd/polaris_portals/example/portal_showcase.dm new file mode 100644 index 0000000000000..8a9437fb0d05f --- /dev/null +++ b/mods/_fd/polaris_portals/example/portal_showcase.dm @@ -0,0 +1,4 @@ +/datum/map_template/portal_showcase + name = "portal showcase" + id = "showcase portals" + mappaths = list("mods/_fd/polaris_portals/example/portal_showcase.dmm") diff --git a/mods/_fd/polaris_portals/example/portal_showcase.dmm b/mods/_fd/polaris_portals/example/portal_showcase.dmm new file mode 100644 index 0000000000000..89b5153042ea1 --- /dev/null +++ b/mods/_fd/polaris_portals/example/portal_showcase.dmm @@ -0,0 +1,1188 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +) +"c" = ( +/turf/simulated/floor/carpet/blue, +/area/centcom) +"d" = ( +/obj/map_effect/portal/master/side_b{ + dir = 1 + }, +/turf/simulated/floor/carpet/blue, +/area/centcom) +"f" = ( +/obj/map_effect/portal/line/side_b{ + dir = 1 + }, +/turf/simulated/floor/carpet/blue, +/area/centcom) +"i" = ( +/turf/simulated/floor/wood, +/area/centcom) +"j" = ( +/turf/space, +/area/space) +"m" = ( +/obj/map_effect/portal/master/side_a, +/turf/simulated/floor/carpet, +/area/centcom) +"n" = ( +/obj/map_effect/portal/line/side_a, +/turf/simulated/floor/carpet, +/area/centcom) +"p" = ( +/obj/map_effect/portal/master/side_a{ + dir = 4; + portal_id = "horizontal" + }, +/turf/simulated/floor/wood, +/area/centcom) +"r" = ( +/turf/simulated/floor/carpet, +/area/centcom) +"E" = ( +/obj/map_effect/portal/master/side_b{ + dir = 8; + portal_id = "horizontal" + }, +/turf/simulated/floor/wood, +/area/centcom) +"G" = ( +/obj/map_effect/portal/line/side_a{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/centcom) +"L" = ( +/obj/map_effect/portal/line/side_b{ + dir = 8 + }, +/turf/simulated/floor/wood, +/area/centcom) +"N" = ( +/turf/simulated/wall/concrete, +/area/centcom) +"V" = ( +/turf/simulated/floor/tiled/techmaint, +/area/centcom) + +(1,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +a +"} +(2,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +"} +(3,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +"} +(4,1,1) = {" +j +j +j +j +j +j +j +N +N +N +N +N +j +j +j +j +j +j +j +j +j +j +j +N +N +N +N +N +j +j +"} +(5,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +N +j +j +j +j +j +j +j +j +j +j +j +N +p +G +G +N +j +j +"} +(6,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +N +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(7,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +N +j +j +j +j +j +j +j +j +j +j +N +N +i +i +i +N +j +j +"} +(8,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +N +j +j +j +j +j +j +j +j +j +j +N +i +i +i +i +N +j +j +"} +(9,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +N +j +j +j +j +j +j +j +j +j +j +N +N +i +i +i +N +j +j +"} +(10,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +N +N +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(11,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +V +N +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(12,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +N +N +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(13,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +N +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(14,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +N +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(15,1,1) = {" +j +j +j +j +j +j +j +N +V +V +V +N +N +N +N +j +j +j +j +j +j +j +N +N +i +i +i +N +j +j +"} +(16,1,1) = {" +j +j +j +j +j +j +j +N +c +c +c +c +c +d +N +j +j +j +j +j +j +j +N +m +r +r +r +N +j +j +"} +(17,1,1) = {" +j +j +j +j +j +j +j +N +c +c +c +c +c +f +N +j +j +j +j +j +j +j +N +n +r +r +r +N +j +j +"} +(18,1,1) = {" +j +j +j +j +j +j +j +N +c +c +c +c +c +f +N +j +j +j +j +j +j +j +N +n +r +r +r +N +j +j +"} +(19,1,1) = {" +j +j +j +j +j +j +j +N +N +N +N +N +N +N +N +j +j +j +j +j +j +j +N +N +i +i +i +N +j +j +"} +(20,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(21,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(22,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(23,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(24,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(25,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(26,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(27,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(28,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +i +i +i +N +j +j +"} +(29,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +E +L +L +N +j +j +"} +(30,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +N +N +N +N +N +j +j +"} +(31,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +"} +(32,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +"} +(33,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +"} +(34,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +"} +(35,1,1) = {" +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +j +"} diff --git a/mods/_fd/polaris_portals/icons/map_effects.dmi b/mods/_fd/polaris_portals/icons/map_effects.dmi new file mode 100644 index 0000000000000000000000000000000000000000..b6bd55c67776c78092d79f1af7539b05267de870 GIT binary patch literal 10379 zcmX}S1zc0#8$Ui6-6bG38bLY)6&Q_xq)4ZPbhogL(Nd#BJ~SdKAuvi}gyfJCP+A#{ zAOnPv|M~s>f4}>BoqNx@JA0n@$@lX-=O!5$Xj4Agt&=#Aly=Nh-~kd-r3VB`$_{tNiG~of$f|2W8m7Os%*cH% zp=V~)T)P~$iD6R5mbOWaYh-$Rf}YAy5_O_Wf@mVO5~59wM}r6-=4}tkRNptG?*n(n zGN5J}J){az8l?lDI?ymWn-{M))(h;{O>e7==_$Z;6(4p~iyheOX(0q*v3ROB#!c9HzFD!(Zf3l%>Qe9t6NkQ z=fi+~^Tg&miB5rmSr6ppg}s#;)4&RL%uv71qPFXs6O{IjM0%8LPra0pnHFq(T%SPY zSG+MB?~BD2+;9e;LeMsyWiBxNe$4@aI8oG zTauL>cAeSH=;Q_90w?A@>?4D`a`a(&EB4IR`fpq7&NsWnROp__tXUJ?(g^=3CnO_6 z@d3}|p{6p>sLrRaRSv!HLVoL08v;dHTLiW@Eg9q|C`3f2X>S_lJ{??Ai+B^mys<2R zYvVA^>jCa|w>Vr_)>?ymk>REcK3MeP^1WfCCEf74C7_8;(m^(EV*EC8 zO#4534^|gT(|3$)I3Cv<>$6qp?dz@ZA*G2+*xUug2~A2+lx_1K1a38?aB1G^Ogsb}eS1#9)R zxelBPCU=aL|NK<=Tv5K-?~)*yHfw;xeF?kMe{LIqL!96Q$No5~Dj3Nte3NWL5<}ig7zc4I6#UY)96k4>T3p6NQ}huK4{1?@R4m^GSsd>X;3_XKn+>Gsv@N5 zo}lmsCtzg)4RYRG7cwY1&(qV>OGpEOZysCiy~@Xl1BMc}^RuzyV~qzb-k13^R&a;lbqgZ{&|YE0C zRd$nZO^ilRVu7#Y=3+kX0YKALTwF{nhbwF?TEJDAYK>b4Bs<7K=%+#^_0n>VXR*as zj5#@!nIg_fx28h3=1sOqldiCyZo6%m!ta_{14Rl&&Z{LQ&eu1bwa~rmbSlND3-wDW z!te0PatRCX_V`_z%4$nFYUh5`=Q?sPNj)lF*JD!`otyQFllox?nv^#jv{fI1|Gwd} z)^bSmegx3by(_rULN%%Xx43frY6|3G228& zE%9OVTWx0g9{|_<9~8HBrXq3AstuHA9MTG2HqQx69E+}=-Ju^BsiG0CqPbVK3D5Jk z%r()?$Zo7P=jVs^oPu0`t0Rgcv z#@1x0pe)tpN1Jr9^**>3xklheH!TViVscCFlXc?vBwd%1L|k9wjKn2O$2Q^y=C`+w zqwcD-+tkJPLz#>jsNAU7R2X5&uqzEyB1v@(f#$bQ#!0!t9BwcpSq&LmYxN=j?`KgAYD`txGQAUUlTj00Rbdye#dz;U&fOmnHkrYT)&m$Y zRnR6(53JHw;j;Q6+H37&_fUM|JUS`4{D+ zA}~E!QW2QMaL1T|m??YN{FS)}k}aOw4{0M_VfKMQ#RYZoHquDFhf1KL$+?n~c8=*u^0k~(Ic?xUTqQwY0KN~fdO_;{ck zT~9bOdqDisum;D1c0L?IDIRHl_@sM=V0j1KuMWCCB0iKT{kYP}qV@=zYYG`N8H}r%x_qteC-LKK#LX>!1L(TRLII= zN|J^ryKW@@1Fl5X+CWZmWxG&5D^;~jxtGs&5OI7muhSJFCFg2YCaOrQjpSL8w` zPNfny@1~o;QauvnGEl73@t2$&%0`o##MQP0WnY|^UWpzz$B_W1Gr5bY{R}sEH2uI0 z7bQ+TNE)s>Ov^6`b6cPHK8crS0ZC*?!^TqS<*etp^=JQ@~tOjM>@QZ$DerTIJ||c@&PtSh|{_ zMN_0C2UR@9Ql-jNPx$*&FIppT|DE2h2cqQdfXa-Ke*CS$O4#F^ zQR(L&o?k3XoSzkkFJEy{X7U@KPk(uTtqp!~Ye!E(m!A{cHxKo=>Hm`R9)cZliMUce z!Vw1cvLa{^+C=7(QMi_FErQZ0Lwn6v@^00}WxheY!AoSBkUCBhE24x{4x!spvbW48 zy6!((Z1MaO_u`XVo^I7cVV9&tSNOcQM$pWM`tFM^N*3|NJbMVPK2=K7>8Vt{xG+yF zFR!WwA>gi)vkIi-AD`m7>rFL@nSUULg=bP4*xrnWuwd;^))y_V~KJ8oN z4mH#o{RxYcL9>8DqI@P%CcjwkN9X9zS2?>g_Y-a|C&u&ceELE~5D#$t%8<`UluM|l z)9A`HPM+5qNjhIh!@i1em^r%#7#PNH*Yr_5u0%ka9*I~CSMwI_)GXjv7u0kv^$K_b zXF^XU>(*qtSmt^!?&1dyM~WXe3row4`cCYqo?Cc??jY-4j=3EUBH5LJ_5$7`;o@3Y zTDofTK)%+kf-?#kM4Ed3mJZBHd@31nZ~tl5w#BWk%TF6*2hG|}!s0CW;6ds<==^|^ zd^g>Jh9AIyUW+G%9*G}4*3-N5-tOc3`mW@cMx{<(Q#@utf6`ZezO+f+q%t-J=JI4y z{S?eq_-nr^IV(|Xy7A1=Y(MZU5h~7V*7UV_T}?$jBmTUDcjf zRo>#wiiC6qMYP$T9q$C`K0D3elw$Qg_X*`bfS`xsm$(c@xEJ7AtS@? z8ZR|SuNJ0J*uAH z9nUD2)Czl3IwYQC!D3UbQ)o-bDv46)-6%?l2&GEzN{SkBCjp;MI!Lxp4yc713mRaI zZd)+4^yupkBfE?2$c2cF>8h{K@*B2#f_662w2VH~hRzBwlF*@tTqIKqs5|Ak0HE3; z?l*~%01@tHN7o9ZYb}yg1;5bL$SRO?g0Q(l_>N(im_6A~kt!RWSVIk^@^TQASpiDZ zM#SA-0#2mpX=pZ8CN0uvzdAWaE+lEc68b@p@=8PzYibG`@xqBoK5dE#HWrjo_tPOW ziK~7A0XDsE%woJxWSf5y0;FN}1OeWi=_({tzq(;)ffWz*3pBk)F^XZ_p0pbY!5o)m;!dO8&9Vc$JH}V zr>Bf4W_nvjHQB^r&9TdsJNNG+Uq73gW4QI+Y2Kv{NM*FIWnNK_Skyg}G zEF}EU^x!@tw^W+iVyF=oc=s*=A*<`(6sa_+K?b#EHswEElspQO2)`*R-f0tEM%Je> z1+9tixNP)fWZY|i_!r^#Y9*e5#ax#z9Sw8;eiXyn`UD|z;Er(K*aVPl^G1j<>a2T_ z2;x7!76C*In9zyRp#af5!>~BVbRyXpCE&Nz%6@nKur~^#rm7?KCJxHP$L74FKRH@^9qos`Ox|x<+2oI|5Yw{GO8zW!Z5h_g>cj_3XpPnSwq2=yAV_*#HN4cz*rBU&& zB;=?}7@{^&ic*FAm&1gnG_3_yS#r~}uIT^x>j&)kRF{skWOo;7Y^fcLNy`O!LY;M*7oufz%NRK0Gk$+=$(#O!jPUu{zK>=P9-I{+K zF<%_9E9{Bqgzi6mlu+grK`we*7B$(VVRiTlRQ8nCwo1v^DAPwom_xDDZ_;(U{x;>- z7HK7iXK8S_(DgGyDye4Kk7B>doymIrM%D_QeKMoRNp{T->BjI?%-%oWgLc;Sc*y9v zs}ScEx88*VUYz`bJe9;o6H9e8m}#;%w>R4#ip=@wHEXx6ev5a^+7^60@CsgGi8zYP|DCLfxzH67K|)D$|7MlXq1b>4xUmeEFSpD-FKI_4Lco0+K*Q&~OKc&cGxWyJu%;EuO; z9a3T^~>R2mBg!U;KOfOt(v<>MG8A(Ysas?ep7P_|Sekp-+qJpZl3O*Yooa z@pRqhrH@ll(S{sVhUU2yTj zI<(2%$iuw+;smHD6_^Lc67)`vwV@X*%LnT6Sl`cjLOFDK{JV?w=I`qDj~ox@g<)(A z6{~}^A#rzN=~@?CqReYdBUbnK_dmV(6lN+M97xxG`p+%0CsDz3tqR>kiil8GSJMyA zr3csISnR(0aSY8`Jys(VtMbwnUKJ>xPmz+i=)N8ND<7~flrL??|%u)v?qP1nKqjg{{@R4DRGWHMmn-|@@Tosvd(-{oQVqQ0|f^}%yNLHnSZ;G3@#amT|kb@KB z%=Vj)j)lP75IwRs02P6=9#akqy}-V?cZh47KJgoOI-lhBp0Ty5tl%H9C|orqoEIBh zY=YYa^uv>xJAbp)W~k8(jh02tY2oW*e8xtw!)`bffL8gdmZ|#XRV3bN<6VnDN-i7 zF>}f>cWZ-tS)XaA$N6o2OISWlKM*(&aw7p$G59?*wh-u*&mhS93F;l%OB>=Mw?Qg( z9$dEVw=XdfHci~1BXh($2*(dIhdNXm^q6VdGAZK88lO~=#H99HPlmlIjI@RCW#Vkf zA`B@zEAG^>em3Wils$sc+cbjFYRIZbH`?I4?a_}BcY@_$% zr!SnE;kNMw*W5~KVZ}oPxIMhK0*P0}q06j{K1i>K;_9f?>b0fPw6;$p*L+G7WFAp} zW!$=o4=!!W205Ig!(5r~>Cgsm92t)EFM0ufJ5r)1Qio{rc?5D&`f9EId_3_drkarR zvw_K$l`>J}#^x%sb!$?&YC0^Q9SMeC$vB=%$FOzt-yYt)a9U&JH7HT$JUcTKkah$l z(1HY=Qp92v01gJ8VtCd*yv#$+nIq@+JH)MXt17bvnp@QRSe+tQ=j-;^hWwk?6}k^E z#bxC4afNdijZ&M1bbaUD5(MYnhxSue_^?V{s{MzkqbYt&&(RO_lEYl)WoqTNFyb~_ z$-;!Y;2`SV1&3rZhw*NyrEesYDR3g+Pi3vWl9*4??(L7|9JOK&V^wWoE3WW+4rXXp$S-8hU`8FUwPFV+11%rT}?(>1R8S=Yytk z*QR^14N33lh{kQ4QY2#Co(S)CAIjso#iN#eFyGa|h)`@IV*-8A54!ntk2aA|@>>m^ zJ`DPcfF|)jAca|yWf;lZDSn@@23G#Wd@5BPxIj$~8smW&QcV-`i<6uG`BQ|kR7hJ-xI1MmFAPw`d;fewHx1l~moR&}d@;#I!eU}x9Z70x z$qKRtXTdPE4|z9FY}yp#FIq35v9W{KZy@S5)#RVt`wvRWB7EBgv$j?9&?4 z066u1{3Ju8wS>Ru3X`C^51ZB%zPZVA)FSUZjdn%|d%zW;hGb=Xr;kb=1&ah~9L5f< zUYW5H(|)?X><4tG*!vUQgT=X)i-F>OzRLnAu#X-p+K*gPU;O>{4Xp3H8MJ-&r;MRMTl{8oW}T$%s`w`sdBi=Jz8gHifleH ztZ#`{23w!fLw5uSA1q~jl6;y%EPF5 zJTcpvH-GHg5MMJPIv5n)v(mORhi~Kom(-Sg+6>qPZn`$~a}C8umUH0OWgx`Vx*w8* z93dBQqJBdc%RW;kx(Cu$Cf2=c$v4D}cVOh;qPwdG-PKeMWj1x&daS)Vuc}9L>&q2Nlc+q2qn@`@<&3f7Fqtbvy&IzknfgavU&l&he^sE~HFS5|l|7MIrCjHQW;)9P96VN!gFoNcWu3i;CMqF? z!iw6#W%T^lUf_9yZ*6EXa0o5mw(R~{=Z$iv_9y(*Sz>+OZOC*j%XKOVFS;V%$f&^> zksxwvChSx*2R{(3Ngo!{Y+yiWYuR9*$*$KBwt9RBty~L{1@$v`S0)|>ka#RPJR&-= z`{R-t={{DGzpH?<(^$ZKr^RLr5Co)r;+DH!NKmkmNjc8`r05dBh`mZ*o^O43jllBHp41u;zt5sK~s0)cV1bO6_OLCM#VmI?1;MRwAFH@{n<^YZ5 zOZ=Ei|L=!!&8KgIE-SJmy*D~W%%gG5WAdoFBd3L3&`|Tzt?`JayyL=X@Mm0da&isl zKnDfdoQ36xLrGqOyM<+X#_U$%?Z!>IN%t6Kw!p@te*!dl9>C1hRw^gToy)@?EqYwd zBA<(5e`#%N7HHu5=T_I^w@AP55Ep9TEHhh!qn$O|4X{lb>^~1RV)u@ZB@Et20v)J5 z7aPAcC0kU@;+T^L?Uf}XuV3+uok720ZAlOU8^5s69g5erD~cY<^t83LDG70IHJ~+) zC#C|AlV7rgX4A_N3KoLQaqI7(9G2a;>xg8ZzUCZH`TYZhxC3-SIKaZz_6u(V{e!Ox zU#n@WmGFQ1)X~Cr{r#`3TaMY*j%b;Uc#i`0X*wTXrwvxaALhfJB2*R`b{y)3N?N`; zO?7A@3(QGu@IWU6S`v00$ArqMT~`j4C)TI{4&L6>=uSrmz#vKeks`f!O2-vsZvrg= zZ+F6VHH@8GpBmcS+}i^igik1-LN;yu`VJ~{(jP!9APQS4D_4Jxkyf>hc)|D!5A(nD zu>{fE-iE6!I5sJK{&dI!Qhp(GoE(&k@>E zUD~^}JXDMr3H?pF9dz)NZL~_ixJCj@2#iT38vI0nLcj6&Z0s<7S`yp&n~YAGv4y>{ zG*Ui%LCJ7oLpvk(%Gk@x%QWJ5*8dbQvZ4B}B6H3Mi>E@cI`>+C`!K%Xi-=IGKeR6? zp~W96wC}z}M)EFf5U7F3UJc4bx;h1W-@y;LY6erf&McGxTjvi8(P~)j6rQoUu`F7i zJwCIN92)Uwe))JbYr7;kd`Lm%WNag#{db_5P*r5R4Nd=LZS039iBN49xZUfo#b0)J zMK&Ki;Noz>eHcUoYm3{L&-%&Szh72aD%aO~YuByM8#0h4=G?wOtYqk3tEa5v$euNU zO7WA7b-dR%K~gVkzRhW!)(WEV5m?Iv3!CU1R0QRucN}S3-f&+MOxHp2hbp`~PM;KT zmNnkgc-Erbcq-cyFE=ogq5iS1_Lu0_w4$pr#iuxG`4}(0OjZ&HOe%O6`|7TW+1`Z4 zwmVT20_2uOmG>MA1W5U^7WM8f?9$MxC*nJ!qD89vAp7>g#OY^Xr-1&_}GERtNTrJCgtmZI`Wg#_TBY z^YyoxZ()`hXKD)V4w*YNC z(}m3Mug@(i?ed!;Q>6BR?Xx_m3wg^D<@2epuJDHrRp`hHa}up?g=PQc6a)w`(4aKm z{2y__b8uK2rNqU_nK78E-g@pZBuLK7aC52~hRy&bhDTorK*O3w(FGQhPw(R@$Wk4$ z+byTu4ePP3sSdH8aNB2Jm3zPjxW6%tqyo1Oq+Nd`olF=_FbTOYk!fk(7!1yO+R92} z)4#XH=X`+lEJ+DH{=@&Yzp$LeFB>2t{OXty!M=Q3%6Ye;g9wLt4R4^A6 zh~x|ecz~&n{MQrf9=vU?dQwcIdtQ45*`WBZxccgZR#CFeT-$Hqa|pb2o4*_+95SoH5^%09joj zEbTLh8sujm_!thG+?v{ra(2tbu3dznw0wxbD-=YMhPEJdw@_NIMgo^^rU? zv&S1p|1=Y<{)IqV!mvRwsbaoaqSoCKKc|c^h8B=kxn9{!Q2z85kx&kByF)OE?*oQ* z%3WX52dh2i+bWoEhOPJYGBy22bkuyS(GQZ$ml4jrC3Eo34Fl(a8Np06t^x^qf90n? z`7%Pi6+81Q{Yldw|B=P=@t}8{)7;P=>~@e_HsuOrbTy5YwqcF4<;r;U@-^^jg<=|n zWuU7YEbMDkeR%f4zJ9OT_NEf|X%7dmH1Vm#_%cG$ouVpEBV#Amrwo)0uGgRZ+9g7S zfmQ4G&YnROyLd(*@XGm;N=1JThXu@I1nkOq|54~$>P8g^#WSjlM8=fztL^6MRRz^K zOw<>Gx0>C+s7&bPMaCC9YaHDLGKZEPvnwBZFAXy4E5CIpANub9bk?iKG?JKx&gIYg zMz;zP8Q^T8an9YKYyOQ;>pz0{>~lOynLiMi=$N|s^1aQThI{(YNi7(I?)QMqx0-9oFIH$9 zSTJ3Pc*t+KMvP#~sTJeERPB}O)m7IV(eKgp8>NxUo~?C2%-=}&9hMhH*t7pr!Ljkt z<682F9PrL%Nf;Pqmpzox0^y0r0|`4Y;=tQ@%~w8Jh>%MO^x;nhl^#qwU#gs5<_!Bq z7vvim!0V9I=d&wYKiAGX`evr#K}d%8wZl_6PYg$EqzyYNAt&@vm!s(utn6e+wS0;} z^xw||)0l)&wueGpYbkv9F(6#8MWR7M2$=4ZceSJmG4Bk|iY+8jXZ7~ClkxODMX~Tq zCu6}H`=NCT?LTV1 zv+enwbxDvJVg1N4^TPY{eId6<#{xX{Gl)ZkY#|DSDHmkJkXskLM@E;$ZC^bz{r0C( z{U-$?6ODz51S5A^Rn!ecS!DrhJaKxdHHv?=9?&j8%){xcBHF znILtG!5ev%kv*-qD=V}J6)x>LJfE%S94;!TRBQS%|F59E-0&&{tkR-X3w+l6<7xce_Qut%-XZcr)kf4{%I)@05Z8z~tZ5>Dq$ z2M!?Qt^;sk8?P>KfU-!$7SLJe)<(Ig)B5so@fy%Y=!o~_@@Q9Y9^-Q+J`7kp{9a+B z-2QiB{r@T9A{}mkv`1e3E*T2C4ysp72Rd8e_1yf^!-;6Yi zU*O>!fSo~tY2~5^7YaWfT=3!Np-xX8pSDD^985F=tYL7)v5qqRLJ)vqm3u4wuYvFWY2bLM6 Date: Tue, 25 Jun 2024 01:21:58 +0300 Subject: [PATCH 2/2] =?UTF-8?q?=D0=94=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mods/_fd/polaris_portals/README.md | 13 ++++++------- mods/_fd/polaris_portals/_polaris_portals.dm | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/mods/_fd/polaris_portals/README.md b/mods/_fd/polaris_portals/README.md index 6af4c996ddd80..0fbaedb1d20da 100644 --- a/mods/_fd/polaris_portals/README.md +++ b/mods/_fd/polaris_portals/README.md @@ -1,7 +1,7 @@ #### Список PRов: -- https://github.com/SierraBay/SierraBay12/pull/##### +- https://github.com/RepoStash/FD-NewBay/pull/34