Files
Matt Atlas cca0dfe7b6 Ports /tg/ and CM planemasters to replace our renderers. (#21458)
Part one of our nefarious plans to destroy lag forever, and also
accomplish some cool shit.

This replaces our renderer system with plane masters, this will also be
used to turn skyboxes into backdrops and thus totally remove skybox
updating lag. Additionally, this will let us manipulate entire planes
very easily to do all sorts of zany shit. All credit goes to the
original coders, this is some seriously cool stuff.

Also fixes some bizarre smoothing behaviour, reduces updateoverlays
calls since now not every single structure in the game tries smoothing
with nothing.

<img width="1349" height="1349" alt="image"
src="https://github.com/user-attachments/assets/9b7cecd7-3c47-448b-9dd8-9b904640bf82"
/>
<img width="1349" height="1349" alt="image"
src="https://github.com/user-attachments/assets/647d75a2-1bff-45ca-ab92-0aea10631afd"
/>

---------

Co-authored-by: Matt Atlas <liermattia@gmail.com>
2025-10-16 11:39:15 +00:00

457 lines
15 KiB
Plaintext

/*
Tents
*/
/datum/large_structure/tent
stages = list("poles" = STAGE_DISASSEMBLED,
"canvas" = STAGE_DISASSEMBLED,
"guy lines" = STAGE_DISASSEMBLED,
"pegs" = STAGE_DISASSEMBLED)
component_structure = /obj/structure/component/tent_canvas
source_item_type = /obj/item/tent
/**
* The state name of an overlay in `icons/obj/item/tent_decals.dmi`
* Used for branded tents, such as the SCC base camp tent
*/
var/decal
/datum/large_structure/tent/build_structures()
. = ..()
var/list/roofs = list()
for(var/obj/structure/component/tent_canvas/C in grouped_structures)
var/perspective_fix = FALSE // Used to make E/W facing even tents have similar perspective to odd ones
if(dir & (NORTH | SOUTH))
if(C.x == x1 || C.x == x2)
C.icon_state = "canvas_[get_location(C)]"
var/mid = Mean(x1, x2)
if(C.x < mid)
C.dir = WEST
else
C.dir = EAST
else
var/width = y2 - y1
if(C.y == y1 || C.y == y2)
C.icon_state = "canvas_[get_location(C)]"
if(C.y == y2) //Upper wall
C.layer = ABOVE_TILE_LAYER
var/mid = Mean(y1, y2)
if(C.y < mid)
C.dir = SOUTH
else
if(ISODD(width) && C.y == Ceil(mid)) // `width` is actually 1 less than the width, so we need to check if this is odd
perspective_fix = TRUE
C.dir = NORTH
var/obj/structure/component/tent_canvas/roof/roof = new /obj/structure/component/tent_canvas/roof(C.loc)
roofs += roof
roof.color = color
roof.dir = C.dir
if(decal && C.x == x1 && C.y == y1)
roof.AddOverlays(overlay_image('icons/obj/item/tent_decals.dmi', decal, flags=RESET_COLOR))
roof.icon_state = "roof_[get_location(C)]"
if(perspective_fix)
roof.AddOverlays(overlay_image(roof.icon, "[roof.icon_state]_p"))
grouped_structures += roofs
/datum/large_structure/tent/structure_entered(turf/entry_point, atom/movable/entering)
. = ..()
if(!.)
return
if(!istype(entering, /mob))
return
var/mob/M = entering
var/atom/movable/screen/plane_master/roof/roof_plane = M.hud_used?.plane_masters["[ROOF_PLANE]"]
if(roof_plane)
roof_plane.alpha = 76
/datum/large_structure/tent/mob_moved(mob/mover, turf/exit_point)
. = ..()
if(!.)
var/atom/movable/screen/plane_master/roof/roof_plane = mover.hud_used?.plane_masters["[ROOF_PLANE]"]
if(roof_plane)
roof_plane.alpha = 255
/**
* Determines the state to use for each section of the tent
* Returns `edge` for structures on the edge of the tent
* Returns `entrance_top` for structures acting as an entrance, at the north/east of the tent
* Returns `entrance_bot` for structures acting as an entrance, at the south/west of the tent
* Returns `edge_entrance_top` for structures on the edge of the tent, and acting as an entrance, at the north/east of the tent
* Returns `edge_entrance_bot` for structures on the edge of the tent, and acting as an entrance, at the south/west of the tent
* Returns `mid` for structures in the exact centre of the tent, for odd number widths
* Returns `mid_entrance_top` for structures in the exact centre of the tent, for odd number widths, acting as an entrance, at the north/east of the tent
* Returns `mid_entrance_bot` for structures in the exact centre of the tent, for odd number widths, acting as an entrance, at the south/west of the tent
* Otherwise returns `norm` for other structures
*/
/datum/large_structure/tent/proc/get_location(var/obj/structure/component/tent_canvas/canvas)
var/edge1
var/edge2
var/side_coord
var/top_coord
var/top_edge
var/bot_edge
if(dir & (NORTH | SOUTH))
edge1 = x1
edge2 = x2
side_coord = canvas.x
top_edge = y2
bot_edge = y1
top_coord = canvas.y
else
edge1 = y1
edge2 = y2
side_coord = canvas.y
top_edge = x2
bot_edge = x1
top_coord = canvas.x
if(side_coord == edge1 || side_coord == edge2)
if(top_coord == top_edge)
return "edge_entrance_top"
else if(top_coord == bot_edge)
return "edge_entrance_bot"
return "edge"
else if(side_coord == Mean(edge1, edge2))
if(top_coord == top_edge)
return "mid_entrance_top"
else if(top_coord == bot_edge)
return "mid_entrance_bot"
return "mid"
else if(top_coord == top_edge)
return "entrance_top"
else if(top_coord == bot_edge)
return "entrance_bot"
return "norm"
/obj/item/tent
name = "expedition tent"
desc = "A rolled up tent, ready to be assembled to make a base camp, shelter, or just a cozy place to chat."
icon = 'icons/obj/item/camping.dmi'
icon_state = "tent"
item_state = "tent"
contained_sprite = TRUE
w_class = WEIGHT_CLASS_BULKY
color = "#58a178"
var/width = 2
var/length = 3
var/decal
var/datum/large_structure/tent/my_tent
/obj/item/tent/assembly_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Drag this to yourself to begin assembly. This will take some time, in 4 stages. Others can start working on the other stages by dragging it to themselves as well."
/obj/item/tent/Initialize()
. = ..()
w_class = min(ceil(width * length / 1.5), WEIGHT_CLASS_GIGANTIC) // 2x2 = WEIGHT_CLASS_NORMAL
desc += "\nThis one is [width] x [length] in size."
/obj/item/tent/Destroy()
if(my_tent)
my_tent.source_item = null
if(!my_tent.grouped_structures)
QDEL_NULL(my_tent)
return ..()
/obj/item/tent/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
. = ..()
if(use_check(usr) || !Adjacent(usr))
return
var/turf/T = get_turf(src)
if(istype(T))
deploy_tent(T, usr)
/obj/item/tent/proc/deploy_tent(var/turf/target, var/mob/user)
if(my_tent)
if(my_tent.origin == get_turf(src)) //Not moved
my_tent.assemble(1 SECOND, user)
return
else
QDEL_NULL(my_tent)
var/deploy_dir = get_compass_dir(user,target)
if(target == get_turf(user))
deploy_dir = user.dir
my_tent = new /datum/large_structure/tent(src)
setup_my_tent(deploy_dir, target)
my_tent.assemble(1 SECOND, user)
/obj/item/tent/proc/setup_my_tent(var/deploy_dir, var/turf/target)
my_tent.name = name
my_tent.color = color
my_tent.decal = decal
my_tent.dir = deploy_dir
my_tent.z1 = target.z
my_tent.z2 = target.z
my_tent.source_item_type = type
my_tent.source_item = src
my_tent.origin = get_turf(src)
if(deploy_dir & NORTH)
my_tent.x1 = target.x - floor((width-1)/2)
my_tent.x2 = target.x + ceil((width-1)/2)
my_tent.y1 = target.y
my_tent.y2 = target.y + (length-1)
else if(deploy_dir & SOUTH)
my_tent.x1 = target.x - ceil((width-1)/2)
my_tent.x2 = target.x + floor((width-1)/2)
my_tent.y1 = target.y - (length-1)
my_tent.y2 = target.y
else if(deploy_dir & EAST)
my_tent.x1 = target.x
my_tent.x2 = target.x + (length-1)
my_tent.y1 = target.y - ceil((width-1)/2)
my_tent.y2 = target.y + floor((width-1)/2)
else
my_tent.x1 = target.x - (length-1)
my_tent.x2 = target.x
my_tent.y1 = target.y - floor((width-1)/2)
my_tent.y2 = target.y + ceil((width-1)/2)
/obj/item/tent/big
name = "base camp tent"
color = "#2e3763"
width = 3
length = 4
/obj/item/tent/big/scc
name = "scc base camp tent"
decal = "scc"
/obj/item/tent/mining
name = "miners' tent"
color = "#8b7242"
width = 3
length = 3
/obj/structure/component/tent_canvas
name = "tent canvas"
desc = "The fabric and poles which make up the wall of a tent. Not air-tight, but able to keep out the weather, and very cozy."
icon = 'icons/obj/item/camping.dmi'
icon_state = "canvas"
item_state = "canvas"
anchored = TRUE
density = TRUE
atom_flags = ATOM_FLAG_CHECKS_BORDER
atmos_canpass = CANPASS_ALWAYS //Tents are not air tight
layer = ABOVE_HUMAN_LAYER
/obj/structure/component/tent_canvas/disassembly_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Drag this to yourself to begin disassembly. This will take some time, in 4 stages. Others can start working on the other stages by dragging it, or other sections, to themselves as well."
/obj/structure/component/tent_canvas/CanPass(atom/movable/mover, turf/target, height, air_group)
. = ..()
if(icon_state in list("canvas", "canvas_mid", "canvas_entrance_top", "canvas_entrace_bot"))
return TRUE //Non-directional, always allow passage
if(get_dir(loc, target) & dir)
return !density
return TRUE
/obj/structure/component/tent_canvas/CheckExit(atom/movable/O, turf/target)
. = ..()
if(icon_state in list("canvas", "canvas_mid", "canvas_entrance_top", "canvas_entrace_bot"))
return TRUE //Non-directional, always allow passage
if(get_dir(O.loc, target) & dir)
return !density
return TRUE
/obj/structure/component/tent_canvas/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
..()
if(use_check(usr, USE_ALLOW_NON_ADJACENT) || (get_dist(usr, src) > 1)) // use_check() can't check for adjacency due to density issues, so we check range as well
return
part_of.disassemble(2 SECONDS, usr, src)
/obj/structure/component/tent_canvas/Destroy() //When we're destroyed, make sure we return the roof plane to anyone inside
for(var/mob/M in loc)
var/atom/movable/screen/plane_master/roof/roof_plane = M.hud_used?.plane_masters["[ROOF_PLANE]"]
if(roof_plane)
roof_plane.alpha = 255
return ..()
/obj/structure/component/tent_canvas/roof
plane = ROOF_PLANE
/obj/structure/component/tent_canvas/roof/CanPass(atom/movable/mover, turf/target, height, air_group)
return TRUE
/obj/structure/component/tent_canvas/roof/CheckExit(atom/movable/O, turf/target)
return TRUE
//Pre-fabricated tents for mapping
/obj/effect/tent
name = "Pre-frabricated expedition tent"
icon = 'icons/effects/map_effects.dmi'
icon_state = "portal_side_a"
var/builds = /obj/item/tent
/obj/effect/tent/Initialize(mapload, ...)
. = ..()
var/obj/item/tent/tent_item = new builds(src)
var/datum/large_structure/tent/tent = new /datum/large_structure/tent(src)
tent_item.my_tent = tent
tent_item.setup_my_tent(dir, get_turf(src))
tent.get_target_turfs(null, TRUE)
tent.build_structures()
for(var/stage in tent.stages)
tent.stages[stage] = STAGE_ASSEMBLED
qdel(tent_item)
qdel(src)
/*
Sleeping bags
*/
/obj/item/sleeping_bag
name = "sleeping bag"
desc = "A rolled up sleeping bag, ready to be taken on a camping trip."
icon = 'icons/obj/item/camping.dmi'
icon_state = "sleepingbag"
item_state = "sleepingbag"
contained_sprite = TRUE
w_class = WEIGHT_CLASS_BULKY
/obj/item/sleeping_bag/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Left-click with this item in-hand on a turf or on yourself to unroll it."
. += "This item can be attached to a backpack."
/obj/item/sleeping_bag/Initialize(mapload, ...)
. = ..()
if(!color)
color = pick(COLOR_NAVY_BLUE, COLOR_GREEN, COLOR_MAROON, COLOR_VIOLET, COLOR_OLIVE, COLOR_SEDONA)
/obj/item/sleeping_bag/afterattack(obj/target, mob/user, proximity)
. = ..()
if(!proximity)
return
var/turf/T = target
if(istype(T))
unroll(T, user)
/obj/item/sleeping_bag/attack_self(mob/user)
. = ..()
var/turf/T = get_turf(user)
if(istype(T))
unroll(T, user)
/**
* Creates sleeping bag structure on the target turf, deleting this item in the process
*/
/obj/item/sleeping_bag/proc/unroll(var/turf/target, var/mob/user)
user.visible_message(SPAN_NOTICE("\The [user] unrolls \the [src]."))
var/obj/structure/bed/sleeping_bag/S = new /obj/structure/bed/sleeping_bag(target, MATERIAL_CLOTH)
S.color = color
qdel(src)
/obj/item/sleeping_bag/mining
color = COLOR_DARK_BROWN
/obj/structure/bed/sleeping_bag
name = "sleeping bag"
desc = "A bag for sleeping in. Great for trying to pretend you're somewhere more comfortable than you really are."
icon = 'icons/obj/item/camping.dmi'
icon_state = "sleepingbag_floor"
base_icon = "sleepingbag_floor"
density = FALSE
anchored = FALSE
buckling_sound = 'sound/items/drop/cloth.ogg'
held_item = /obj/item/sleeping_bag
can_dismantle = FALSE
can_pad = FALSE
/obj/structure/bed/sleeping_bag/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "This object can be buckled into like any standard bed."
. += "Clicking and dragging this object onto yourself will roll it back up (so long as no one is sleeping inside)."
/obj/structure/bed/sleeping_bag/update_icon()
return
/obj/structure/bed/sleeping_bag/buckle(mob/living/M)
. = ..()
var/image/I = overlay_image(icon, "[base_icon]_top", color)
M.AddOverlays(I)
/obj/structure/bed/sleeping_bag/unbuckle()
if(buckled)
buckled.update_icon()
. = ..()
/obj/structure/bed/sleeping_bag/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
. = ..()
if(use_check(usr) || !Adjacent(usr))
return
if(!ishuman(usr) && (!isrobot(usr) || isDrone(usr))) //Humans and borgs can roll, but not drones
return
if(buckled)
to_chat(usr, SPAN_WARNING("You can't roll up \the [src] while someone is sleeping inside."))
return
var/obj/item/sleeping_bag/S = new held_item(get_turf(src))
S.color = color
usr.visible_message(SPAN_NOTICE("\The [usr] rolls up \the [src]."))
qdel(src)
/*
Folding Tables
*/
/obj/item/material/folding_table
name = "folding table"
desc = "A temporary surface, for when you need a table, but only for a little while."
icon = 'icons/obj/item/camping.dmi'
icon_state = "table_folded"
item_state = "table_folded"
contained_sprite = TRUE
default_material = MATERIAL_ALUMINIUM
/obj/item/material/folding_table/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Left-click on yourself with this item in-hand to deploy it."
/obj/item/material/folding_table/attack_self(mob/user)
if(use_check(user) || !Adjacent(user))
return
deploy(get_turf(user), user.dir, user)
/obj/item/material/folding_table/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
if(use_check(user) || !user.Adjacent(target))
to_chat(usr, SPAN_WARNING("You fail to set up \the [src] in that location."))
return
if(isturf(target))
deploy(target, user.dir, user)
/obj/item/material/folding_table/proc/deploy(var/turf/T, var/direction, var/mob/user)
new /obj/structure/table/rack/folding_table(T)
user.visible_message(SPAN_NOTICE("\The [user] sets up \the [src]."))
qdel(src)
/obj/structure/table/rack/folding_table
name = "folding table"
desc = "A temporary surface, for when you need a table, but only for a little while."
icon_state = "camping_table"
table_mat = MATERIAL_ALUMINIUM
/obj/structure/table/rack/folding_table/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Clicking and dragging this object onto yourself will collapse it again."
/obj/structure/table/rack/folding_table/dismantle(obj/item/wrench/W, mob/user)
return FALSE
/obj/structure/table/rack/folding_table/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
. = ..()
if(use_check(user) || !Adjacent(user))
return
if(!ishuman(user) && (!isrobot(user) || isDrone(user))) //Humans and borgs can collapse, but not drones
return
new /obj/item/material/folding_table(get_turf(src))
user.visible_message(SPAN_NOTICE("\The [user] collapses \the [src]."))
qdel(src)
/obj/item/material/stool/chair/folding/camping
default_material = MATERIAL_ALUMINIUM