diff --git a/aurorastation.dme b/aurorastation.dme index 380e739331b..f6ed7fd669e 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -74,6 +74,7 @@ #include "code\__DEFINES\important_recursive_contents.dm" #include "code\__DEFINES\items_clothing.dm" #include "code\__DEFINES\jobs.dm" +#include "code\__DEFINES\large_structures.dm" #include "code\__DEFINES\layers.dm" #include "code\__DEFINES\lighting.dm" #include "code\__DEFINES\logging.dm" @@ -1074,6 +1075,7 @@ #include "code\game\objects\items\apc_frame.dm" #include "code\game\objects\items\blueprints.dm" #include "code\game\objects\items\bodybag.dm" +#include "code\game\objects\items\camping.dm" #include "code\game\objects\items\canvas.dm" #include "code\game\objects\items\contraband.dm" #include "code\game\objects\items\crayons.dm" @@ -1352,6 +1354,7 @@ #include "code\game\objects\structures\inflatable.dm" #include "code\game\objects\structures\janicart.dm" #include "code\game\objects\structures\kitchen_spike.dm" +#include "code\game\objects\structures\large.dm" #include "code\game\objects\structures\lattice.dm" #include "code\game\objects\structures\light_pole.dm" #include "code\game\objects\structures\mirror.dm" diff --git a/code/__DEFINES/large_structures.dm b/code/__DEFINES/large_structures.dm new file mode 100644 index 00000000000..dc95150c20a --- /dev/null +++ b/code/__DEFINES/large_structures.dm @@ -0,0 +1,3 @@ +#define STAGE_DISASSEMBLED 0 //Stage needs to be assembled, or has been disassembled already +#define STAGE_PROGRESS 1 //Stage is being assembled or disassembled +#define STAGE_ASSEMBLED 2 //Stage needs to be disassembled, or has been assembled already diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 5d194a938d2..27ce7dc0a83 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -138,23 +138,25 @@ #define OBSERVER_PLANE 3 -#define LIGHTING_PLANE 4 +#define ROOF_PLANE 4 + +#define LIGHTING_PLANE 5 #define LIGHTING_LAYER 1 -#define EFFECTS_ABOVE_LIGHTING_PLANE 5 +#define EFFECTS_ABOVE_LIGHTING_PLANE 6 #define EYE_GLOW_LAYER 1 #define BEAM_PROJECTILE_LAYER 2 #define SUPERMATTER_WALL_LAYER 3 #define LIGHTNING_LAYER 4 -#define FULLSCREEN_PLANE 6 +#define FULLSCREEN_PLANE 7 #define FULLSCREEN_LAYER 1 #define DAMAGE_LAYER 2 #define IMPAIRED_LAYER 3 #define BLIND_LAYER 4 #define CRIT_LAYER 5 -#define HUD_PLANE 7 +#define HUD_PLANE 8 #define UNDER_HUD_LAYER 1 #define HUD_BASE_LAYER 2 #define HUD_BELOW_ITEM_LAYER 2.9 diff --git a/code/_onclick/hud/rendering/_renderer.dm b/code/_onclick/hud/rendering/_renderer.dm index 5cd80fb2634..3d5547ba0b0 100644 --- a/code/_onclick/hud/rendering/_renderer.dm +++ b/code/_onclick/hud/rendering/_renderer.dm @@ -90,14 +90,14 @@ INITIALIZE_IMMEDIATE(/atom/movable/renderer) /mob/proc/CreateRenderers() if (!renderers) renderers = list() - for (var/atom/movable/renderer/renderer as anything in subtypesof(/atom/movable/renderer)) + for (var/renderer as anything in subtypesof(/atom/movable/renderer)) if(ispath(renderer, /atom/movable/renderer/shared)) continue - renderer = new renderer (null, src) - renderers[renderer] = renderer.plane // (renderer = plane) format for visual debugging - if (renderer.relay) - my_client.screen += renderer.relay - my_client.screen += renderer + var/atom/movable/renderer/render = new renderer (null, src) + renderers["[render.plane]"] = render // (render = plane) format for visual debugging + if (render.relay) + my_client.screen += render.relay + my_client.screen += render for (var/atom/movable/renderer/zrenderer as anything in GLOB.zmimic_renderers) if (zrenderer.relay) @@ -107,11 +107,12 @@ INITIALIZE_IMMEDIATE(/atom/movable/renderer) /// Removes the mob's renderers on /Logout() /mob/proc/RemoveRenderers() if(my_client) - for(var/atom/movable/renderer/renderer as anything in renderers) - my_client.screen -= renderer - if (renderer.relay) - my_client.screen -= renderer.relay - qdel(renderer) + for(var/renderer as anything in renderers) + var/atom/movable/renderer/render = renderers[renderer] + my_client.screen -= render + if (render.relay) + my_client.screen -= render.relay + qdel(render) for (var/atom/movable/renderer/renderer as anything in GLOB.zmimic_renderers) my_client.screen -= renderer if (renderers) @@ -181,6 +182,11 @@ GLOBAL_LIST_EMPTY(zmimic_renderers) group = RENDER_GROUP_SCENE plane = OBSERVER_PLANE +/atom/movable/renderer/roofs + name = "Roofs" + group = RENDER_GROUP_SCENE + plane = ROOF_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT /// Draws darkness effects. /atom/movable/renderer/lighting diff --git a/code/game/objects/items/camping.dm b/code/game/objects/items/camping.dm new file mode 100644 index 00000000000..5dd66b6e1be --- /dev/null +++ b/code/game/objects/items/camping.dm @@ -0,0 +1,386 @@ +/* + + 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) + if(dir & (NORTH | SOUTH)) + if(C.x == x1 || C.x == x2) + C.icon_state = "canvas_dir" + var/mid = Mean(x1, x2) + if(C.x < mid) + C.dir = WEST + else + C.dir = EAST + else + if(C.y == y1 || C.y == y2) + C.icon_state = "canvas_dir" + var/mid = Mean(y1, y2) + if(C.y < mid) + C.dir = SOUTH + else + 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_roof_type(C)]" + grouped_structures += roofs + +/datum/large_structure/tent/structure_entered(turf/entry_point, atom/movable/entering) + . = ..() + if(!.) + return + var/mob/M = entering + if(!M.renderers) + return + var/atom/movable/renderer/roofs/roof_plane = M.renderers["[ROOF_PLANE]"] + if(roof_plane) + roof_plane.alpha = 76 + +/datum/large_structure/tent/mob_moved(mob/mover, turf/exit_point) + . = ..() + if(!.) + var/atom/movable/renderer/roofs/roof_plane = mover.renderers["[ROOF_PLANE]"] + if(roof_plane) + roof_plane.alpha = 255 + +/** + * Determines roof state over each canvas structure + * Returns `edge` for structures on the edge of the tent, so they don't cover the walls + * Returns `mid` for structures in the centre of the tent, for odd number widths + * Otherwise returns `norm` for other structures + */ +/datum/large_structure/tent/proc/get_roof_type(var/obj/structure/component/tent_canvas/canvas) + var/edge1 + var/edge2 + var/axis + if(dir & (NORTH | SOUTH)) + edge1 = x1 + edge2 = x2 + axis = canvas.x + else + edge1 = y1 + edge2 = y2 + axis = canvas.y + if(axis == edge1 || axis == edge2) + return "edge" + else if(axis == Mean(edge1, edge2)) + return "mid" + 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." + desc_info = "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." + icon = 'icons/obj/item/camping.dmi' + icon_state = "tent" + item_state = "tent" + contained_sprite = TRUE + w_class = ITEMSIZE_LARGE + color = "#58a178" + var/width = 2 + var/length = 3 + var/decal + + var/datum/large_structure/tent/my_tent + +/obj/item/tent/Initialize() + . = ..() + w_class = min(ceil(width * length / 1.5), ITEMSIZE_IMMENSE) // 2x2 = ITEMSIZE_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/MouseDrop(over_object, src_location, over_location) + . = ..() + 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" + 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/CanPass(atom/movable/mover, turf/target, height, air_group) + . = ..() + if(icon_state == "canvas") + 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 == "canvas") + return TRUE //Non-directional, always allow passage + if(get_dir(O.loc, target) & dir) + return !density + return TRUE + +/obj/structure/component/tent_canvas/MouseDrop(over_object, src_location, over_location) + ..() + if(use_check(usr) || !Adjacent(usr)) + return + part_of.disassemble(2 SECONDS, usr, src) + +/obj/structure/component/tent_canvas/roof + plane = ROOF_PLANE + +//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() + 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." + desc_extended = "This item can be attached to a backpack." + icon = 'icons/obj/item/camping.dmi' + icon_state = "sleepingbag" + item_state = "sleepingbag" + contained_sprite = TRUE + w_class = ITEMSIZE_LARGE + +/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) + +/obj/item/sleeping_bag/MouseDrop(over_object, src_location, over_location) + . = ..() + if(use_check(usr) || !Adjacent(usr)) + return + var/turf/T = get_turf(src) + if(istype(T)) + unroll(T, usr) + +/** + * 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/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/MouseDrop(atom/over, src_location, over_location, src_control, over_control, 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/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) || !Adjacent(user)) + 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/dismantle(obj/item/wrench/W, mob/user) + return FALSE + +/obj/structure/table/rack/folding_table/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params) + . = ..() + if(use_check(usr) || !Adjacent(usr)) + return + if(!ishuman(usr) && (!isrobot(usr) || isDrone(usr))) //Humans and borgs can collapse, but not drones + return + new /obj/item/material/folding_table(get_turf(src)) + usr.visible_message(SPAN_NOTICE("\The [usr] collapses \the [src].")) + qdel(src) + +/obj/item/material/stool/chair/folding/camping + default_material = MATERIAL_ALUMINIUM diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index c6cf0e8b505..fbeae0dcabc 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -24,6 +24,14 @@ allow_quick_empty = TRUE empty_delay = 0.5 SECOND var/straps = FALSE // Only really used for the verb. + /** + * References a sleeping bag attached to this bag. Should convert this to use accessories later, but that means making backpacks clothing. + */ + var/obj/item/sleeping_bag/attached_bag + /** + * Suffix used for overlays with an attached sleeping bag, because satchels are at people's sides while other bags are on people's backs. + */ + var/attached_icon = "backpack" /obj/item/storage/backpack/Initialize() . = ..() @@ -31,6 +39,10 @@ alpha_mask = "normal" verbs += /obj/item/storage/backpack/proc/adjust_backpack_straps +/obj/item/storage/backpack/Destroy() + QDEL_NULL(attached_bag) + . = ..() + /obj/item/storage/backpack/proc/adjust_backpack_straps() set name = "Adjust Bag Straps" set desc = "Adjust your bag straps." @@ -79,6 +91,44 @@ return 0 return 1 +/obj/item/storage/backpack/attackby(obj/item/attacking_item, mob/user, params) + if(istype(attacking_item, /obj/item/sleeping_bag) && !attached_bag && ishuman(user)) + var/mob/living/carbon/human/H = user + attached_bag = attacking_item + update_icon() + if(H.back == src) + H.update_inv_back() // Add overlay to backpack if on back + H.drop_from_inventory(attached_bag) + attached_bag.loc = null + return + return ..() + +/obj/item/storage/backpack/update_icon() + . = ..() + ClearOverlays() + if(attached_bag) + var/image/I = overlay_image(attached_bag.icon, "[attached_bag.icon_state]_[attached_icon]", attached_bag.color) + AddOverlays(I) + +/obj/item/storage/backpack/get_mob_overlay(mob/living/carbon/human/H, mob_icon, mob_state, slot, main_call) + var/image/I = ..() + if(slot == slot_back_str && attached_bag) + var/image/over = overlay_image(attached_bag.icon, "[attached_bag.icon_state]_[attached_icon]_ba", attached_bag.color) + I.AddOverlays(over) + return I + +/obj/item/storage/backpack/AltClick(mob/usr) + if(attached_bag && ishuman(usr)) + var/mob/living/carbon/human/H = usr + H.put_in_hands(attached_bag) + attached_bag = null + update_icon() + if(H.back == src) + H.update_inv_back() + return + return ..() + + /* * Backpack Types */ @@ -333,6 +383,7 @@ icon_state = "satchel" item_state = "satchel" straps = TRUE + attached_icon = "satchel" /obj/item/storage/backpack/satchel/leather/withwallet/New() ..() @@ -927,6 +978,7 @@ contained_sprite = FALSE sprite_sheets = list(BODYTYPE_VAURCA = 'icons/mob/species/vaurca/back.dmi', BODYTYPE_VAURCA_BULWARK = 'icons/mob/species/bulwark/back.dmi') var/hooded = FALSE + var/cape_backing_state = "cape_backing" /obj/item/storage/backpack/cloak/verb/toggle_cloak_hood() set name = "Toggle Cloak Hood" @@ -945,7 +997,7 @@ /obj/item/storage/backpack/cloak/get_mob_overlay(var/mob/living/carbon/human/human, var/mob_icon, var/mob_state, var/slot) var/image/I = ..() - var/image/cape_backing = image(mob_icon, null, "[initial(icon_state)]_backing", MOB_SHADOW_LAYER) + var/image/cape_backing = image(mob_icon, null, "[icon_state]_backing", MOB_SHADOW_LAYER) I.AddOverlays(cape_backing) return I diff --git a/code/game/objects/structures/crates_lockers/closets/secure/operations.dm b/code/game/objects/structures/crates_lockers/closets/secure/operations.dm index 4384a2c5a2e..06e08e1e8cd 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/operations.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/operations.dm @@ -102,6 +102,7 @@ new /obj/item/clothing/head/hardhat/orange(src) new /obj/item/device/radio(src) new /obj/item/device/flashlight/lantern(src) + new /obj/item/sleeping_bag/mining(src) // Merchant /obj/structure/closet/secure_closet/merchant diff --git a/code/game/objects/structures/large.dm b/code/game/objects/structures/large.dm new file mode 100644 index 00000000000..8bb9d198ea3 --- /dev/null +++ b/code/game/objects/structures/large.dm @@ -0,0 +1,167 @@ +/* + * # Large Structure + * + * This datum controls a large, multi-tile structure, capable of being assembled or disassembled as a unit (i.e. tents). + */ +/datum/large_structure + /** + * Name of this structure + */ + var/name = "structure" + /** + * Turfs this structure exists on + */ + var/list/turf/target_turfs = list() + /** + * Structures that are part of this structure + */ + var/list/obj/structure/component/grouped_structures = list() + /** + * Mobs assembling or disassembling this structure + */ + var/list/mob/interacting = list() + /** + * A list of the stages required to assemble or disassemble the large structure. Format: "string" = STAGE + */ + var/list/stages = list() + /** + * The smaller structure which makes up this large one + */ + var/component_structure + /** + * The type item used to set up this structure in the first place, made on disassembly + */ + var/source_item_type + /** + * The item used to set up this structure, deleted after assembly + */ + var/obj/item/source_item + /** + * The turf this structure started from. Used to check if the source_item building this has moved. + */ + var/turf/origin + var/color + var/dir + var/x1 + var/x2 + var/y1 + var/y2 + var/z1 + var/z2 + +/** + * Returns the first stage with value `type` + */ +/datum/large_structure/proc/get_next_stage(var/type) + for(var/stage in stages) + if(stages[stage] == type) + return stage + +/datum/large_structure/proc/assemble(var/time_per_structure, var/mob/user) + if(user in interacting) + to_chat(user, SPAN_INFO("You are already working on assembling \the [src].")) + return FALSE + + var/stage_to_do = get_next_stage(STAGE_DISASSEMBLED) + + if(!stage_to_do) + to_chat(user, SPAN_INFO("There is no more work to be done assembling \the [src].")) + return FALSE + + + if(!LAZYLEN(target_turfs)) + get_target_turfs(user) + + interacting += user + + user.visible_message(SPAN_NOTICE("\The [user] begins assembling \the [src]'s [stage_to_do].")) + stages[stage_to_do] = STAGE_PROGRESS + if(!do_after(user, time_per_structure * LAZYLEN(target_turfs))) + stages[stage_to_do] = STAGE_DISASSEMBLED + return + user.visible_message(SPAN_NOTICE("\The [user] finishes assembling the [stage_to_do].")) + stages[stage_to_do] = STAGE_ASSEMBLED + + interacting -= user + + if(get_next_stage(STAGE_DISASSEMBLED)) //Still work to do + assemble(time_per_structure, user) + return FALSE + + if(get_next_stage(STAGE_PROGRESS)) //Still work being done + return FALSE + + build_structures() + QDEL_NULL(source_item) + return TRUE + +/datum/large_structure/proc/get_target_turfs(var/mob/user, var/force = FALSE) + target_turfs = block(x1,y1,z1,x2,y2,z2) + for(var/turf/T in target_turfs) + if(!(istype(T) || force)) + to_chat(user, SPAN_ALERT("You cannot set up \the [src] here. Try and find a big enough solid surface.")) + return FALSE + +/datum/large_structure/proc/build_structures() + for(var/turf/T in target_turfs) + var/obj/structure/component/C = new component_structure(T) + C.part_of = src + C.color = color + grouped_structures += C + RegisterSignal(T, COMSIG_ATOM_ENTERED, PROC_REF(structure_entered), override = TRUE) + +/datum/large_structure/proc/disassemble(var/time_per_structure, var/mob/user) + if(user in interacting) + to_chat(user, SPAN_INFO("You are already working on disassembling \the [src].")) + return FALSE + + var/stage_to_do = get_next_stage(STAGE_ASSEMBLED) + + if(!stage_to_do) + to_chat(user, SPAN_INFO("There is no more work to be done disassembling \the [src].")) + return FALSE + + interacting += user + + user.visible_message(SPAN_NOTICE("\The [user] begins disassembling \the [src]'s [stage_to_do].")) + stages[stage_to_do] = STAGE_PROGRESS + if(!do_after(user, time_per_structure * LAZYLEN(grouped_structures))) + stages[stage_to_do] = STAGE_ASSEMBLED + return FALSE + user.visible_message(SPAN_NOTICE("\The [user] finishes disassembling the [stage_to_do].")) + stages[stage_to_do] = STAGE_DISASSEMBLED + + interacting -= user + + if(get_next_stage(STAGE_ASSEMBLED)) //Still work to do + return disassemble(time_per_structure, user) + + if(get_next_stage(STAGE_PROGRESS)) //Still work being done + return + + QDEL_LIST(grouped_structures) + + var/obj/item/I = new source_item_type(get_turf(user)) + I.color = color + qdel(src) + return TRUE + +/datum/large_structure/proc/structure_entered(var/turf/entry_point, var/atom/movable/entering) + if(!isliving(entering)) + return FALSE + RegisterSignal(entering, COMSIG_MOVABLE_MOVED, PROC_REF(mob_moved), override = TRUE) + return TRUE + +/datum/large_structure/proc/mob_moved(var/mob/mover) + if(!(get_turf(mover) in target_turfs)) + UnregisterSignal(mover, COMSIG_MOVABLE_MOVED) + return FALSE + return TRUE + +/obj/structure/component + var/datum/large_structure/part_of + +/obj/structure/component/Destroy() + if(part_of) + part_of.grouped_structures -= src + return ..() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 52b1e861c62..3a551be472f 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -561,10 +561,13 @@ var/const/enterloopsanity = 100 /turf/proc/is_outside() // Can't rain inside or through solid walls. - // TODO: dense structures like full windows should probably also block weather. if(density) return OUTSIDE_NO + for(var/obj/structure/S in src) // Dense structures like full windows should probably also block weather. + if(S.density || istype(S, /obj/structure/component/tent_canvas)) + return OUTSIDE_NO + if(last_outside_check != OUTSIDE_UNCERTAIN) return last_outside_check diff --git a/code/modules/research/xenoarchaeology/misc.dm b/code/modules/research/xenoarchaeology/misc.dm index 3ce7a0d802e..f58fc6dd655 100644 --- a/code/modules/research/xenoarchaeology/misc.dm +++ b/code/modules/research/xenoarchaeology/misc.dm @@ -99,6 +99,7 @@ new /obj/item/device/hand_labeler(src) new /obj/item/ore_detector(src) new /obj/item/device/spaceflare(src) + new /obj/item/tent(src) // 2 Drills new /obj/item/pickaxe/drill(src) diff --git a/html/changelogs/camping.yml b/html/changelogs/camping.yml new file mode 100644 index 00000000000..9662d921e25 --- /dev/null +++ b/html/changelogs/camping.yml @@ -0,0 +1,8 @@ +author: Sparky_hotdog + +delete-after: True + +changes: + - rscadd: "Added tents, which can be set up individually, or as a team with greater efficiency, and provide shelter from the weather. There is one tent in each excavation tools locker, one in the Intrepid hangar, one in the Spark, and two larger tents in the Intrepid." + - rscadd: "Added sleeping bags, which can be attached to bags for ease of carrying. Four are available in the Intrepid hangar, and one in each miner's locker." + - rscadd: "Added foldable tables. Added a foldable table to the Intrepid hangar, along with four folding chairs." diff --git a/icons/obj/item/camping.dmi b/icons/obj/item/camping.dmi new file mode 100644 index 00000000000..0f3a84ea305 Binary files /dev/null and b/icons/obj/item/camping.dmi differ diff --git a/icons/obj/item/tent_decals.dmi b/icons/obj/item/tent_decals.dmi new file mode 100644 index 00000000000..6e9207338ea Binary files /dev/null and b/icons/obj/item/tent_decals.dmi differ diff --git a/icons/obj/structure/tables/table.dmi b/icons/obj/structure/tables/table.dmi index 19efabc5e48..9424edeeb41 100644 Binary files a/icons/obj/structure/tables/table.dmi and b/icons/obj/structure/tables/table.dmi differ diff --git a/maps/sccv_horizon/sccv_horizon-1_deck_1.dmm b/maps/sccv_horizon/sccv_horizon-1_deck_1.dmm index c553428ba0f..97ba6f189f3 100644 --- a/maps/sccv_horizon/sccv_horizon-1_deck_1.dmm +++ b/maps/sccv_horizon/sccv_horizon-1_deck_1.dmm @@ -15444,6 +15444,20 @@ /obj/effect/map_effect/window_spawner/full/reinforced/grille/firedoor, /turf/simulated/floor/tiled/dark/full, /area/hangar/intrepid) +"kHa" = ( +/obj/item/tent, +/obj/item/sleeping_bag, +/obj/item/sleeping_bag, +/obj/item/sleeping_bag, +/obj/item/sleeping_bag, +/obj/structure/closet/crate, +/obj/item/material/stool/chair/folding/camping, +/obj/item/material/stool/chair/folding/camping, +/obj/item/material/stool/chair/folding/camping, +/obj/item/material/stool/chair/folding/camping, +/obj/item/material/folding_table, +/turf/simulated/floor/plating, +/area/hangar/intrepid) "kHg" = ( /obj/structure/cable/green{ icon_state = "1-2" @@ -27770,6 +27784,7 @@ pixel_y = -2 }, /obj/item/clothing/mask/breath, +/obj/item/tent/big/scc, /turf/simulated/floor/lino, /area/shuttle/intrepid/quarters) "tgR" = ( @@ -29179,6 +29194,7 @@ /obj/item/material/hatchet/machete/steel, /obj/item/clothing/accessory/holster/utility/machete, /obj/item/clothing/accessory/holster/utility/machete, +/obj/item/tent/mining, /turf/simulated/floor/tiled/dark, /area/shuttle/mining) "ucG" = ( @@ -29208,6 +29224,7 @@ dir = 4 }, /obj/effect/floor_decal/industrial/outline/engineering, +/obj/item/tent/big/scc, /turf/simulated/floor/carpet/rubber, /area/shuttle/intrepid/crew_compartment) "udC" = ( @@ -56985,7 +57002,7 @@ aEl kLb qeD rPN -iRC +kHa iRC iRC iRC