diff --git a/code/game/machinery/pipe/pipe_recipes.dm b/code/game/machinery/pipe/pipe_recipes.dm index 0bf8f10105..14dbfc2603 100644 --- a/code/game/machinery/pipe/pipe_recipes.dm +++ b/code/game/machinery/pipe/pipe_recipes.dm @@ -4,6 +4,7 @@ var/global/list/atmos_pipe_recipes = null var/global/list/disposal_pipe_recipes = null +var/global/list/all_pipe_recipes = null // VOREStation Add /hook/startup/proc/init_pipe_recipes() global.atmos_pipe_recipes = list( @@ -68,6 +69,7 @@ var/global/list/disposal_pipe_recipes = null new /datum/pipe_recipe/disposal("Chute", DISPOSAL_PIPE_CHUTE, "intake"), ) ) + global.all_pipe_recipes = disposal_pipe_recipes + atmos_pipe_recipes // VOREStation Add return TRUE // @@ -96,6 +98,7 @@ var/global/list/disposal_pipe_recipes = null /datum/pipe_recipe/pipe var/obj/item/pipe/construction_type // The type PATH to the type of pipe fitting object the recipe makes. var/obj/machinery/atmospherics/pipe_type // The type PATH of what actual pipe the fitting becomes. + var/paintable = FALSE // If TRUE, allow the RPD to paint this pipe. // VOREStation Add /datum/pipe_recipe/pipe/New(var/label, var/obj/machinery/atmospherics/path) name = label @@ -105,6 +108,7 @@ var/global/list/disposal_pipe_recipes = null dirtype = initial(construction_type.dispenser_class) if (dirtype == PIPE_TRIN_M) icon_state_m = "[icon_state]m" + paintable = ispath(path, /obj/machinery/atmospherics/pipe) && !(ispath(path, /obj/machinery/atmospherics/pipe/vent)) // VOREStation Add // Render an HTML link to select this pipe type /datum/pipe_recipe/pipe/Render(dispenser) diff --git a/code/game/objects/items/weapons/RPD_vr.dm b/code/game/objects/items/weapons/RPD_vr.dm new file mode 100644 index 0000000000..c627956d63 --- /dev/null +++ b/code/game/objects/items/weapons/RPD_vr.dm @@ -0,0 +1,377 @@ +#define PAINT_MODE -2 +#define EATING_MODE -1 +#define ATMOS_MODE 0 +#define DISPOSALS_MODE 1 +#define TRANSIT_MODE 2 + +/obj/item/weapon/pipe_dispenser + name = "Rapid Piping Device (RPD)" + desc = "A device used to rapidly pipe things." + icon = 'icons/obj/tools_vr.dmi' + icon_state = "rpd" + item_state = "rpd" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_vr.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_vr.dmi', + ) + flags = NOBLUDGEON + force = 10 + throwforce = 10 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + matter = list(MAT_STEEL = 50000, MAT_GLASS = 25000) + var/datum/effect/effect/system/spark_spread/spark_system + var/mode = ATMOS_MODE + var/p_dir = NORTH // Next pipe will be built with this dir + var/p_flipped = FALSE // If the next pipe should be built flipped + var/paint_color = "grey" // Pipe color index for next pipe painted/built. + var/screen = ATMOS_MODE // Starts on the atmos tab. + var/piping_layer = PIPING_LAYER_DEFAULT + var/wrench_mode = FALSE + var/obj/item/weapon/tool/wrench/tool + var/datum/pipe_recipe/recipe // pipe recipie selected for display/construction + var/static/datum/pipe_recipe/first_atmos + var/static/datum/pipe_recipe/first_disposal + var/static/datum/asset/iconsheet/pipes/icon_assets + var/static/list/pipe_layers = list( + "Regular" = PIPING_LAYER_REGULAR, + "Supply" = PIPING_LAYER_SUPPLY, + "Scrubber" = PIPING_LAYER_SCRUBBER, + "Fuel" = PIPING_LAYER_FUEL, + "Aux" = PIPING_LAYER_AUX + ) + +/obj/item/weapon/pipe_dispenser/Initialize() + . = ..() + src.spark_system = new /datum/effect/effect/system/spark_spread + spark_system.set_up(5, 0, src) + spark_system.attach(src) + tool = new /obj/item/weapon/tool/wrench/cyborg(src) // RPDs have wrenches inside of them, so that they can wrench down spawned pipes without being used as superior wrenches themselves. + +/obj/item/weapon/pipe_dispenser/proc/SetupPipes() + if(!first_atmos) + first_atmos = atmos_pipe_recipes[atmos_pipe_recipes[1]][1] + recipe = first_atmos + if(!first_disposal) + first_disposal = disposal_pipe_recipes[disposal_pipe_recipes[1]][1] + +/obj/item/weapon/pipe_dispenser/Destroy() + qdel_null(spark_system) + qdel_null(tool) + return ..() + +/obj/item/weapon/pipe_dispenser/suicide_act(mob/user) + var/datum/gender/TU = gender_datums[user.get_visible_gender()] + user.visible_message("[user] points the end of the RPD down [TU.his] throat and presses a button! It looks like [TU.hes] trying to commit suicide...") + playsound(get_turf(user), 'sound/machines/click.ogg', 50, 1) + playsound(get_turf(user), 'sound/items/deconstruct.ogg', 50, 1) + return(BRUTELOSS) + +/obj/item/weapon/pipe_dispenser/attack_self(mob/user) + src.interact(user) + +// TODO - Wouldn't it be nice to have nanoui? +/obj/item/weapon/pipe_dispenser/interact(mob/user) + SetupPipes() + if(!icon_assets) + icon_assets = get_asset_datum(/datum/asset/iconsheet/pipes) + icon_assets.send(user) + + var/list/lines = list() + if(mode >= ATMOS_MODE) + lines += "

Direction:

" + switch(recipe.dirtype) + + if(PIPE_STRAIGHT) // Straight, N-S, W-E + lines += render_dir_img(recipe.icon_state,user,NORTH,"Vertical","↕") + lines += render_dir_img(recipe.icon_state,user,EAST,"Horizontal","↔") + + if(PIPE_BENDABLE) // Bent, N-W, N-E etc + lines += render_dir_img(recipe.icon_state,user,NORTH,"Vertical","↕") + lines += render_dir_img(recipe.icon_state,user,EAST,"Horizontal","↔") + lines += "
" + lines += render_dir_img(recipe.icon_state,user,NORTHWEST,"West to North","╝") + lines += render_dir_img(recipe.icon_state,user,NORTHEAST,"North to East","╚") + lines += render_dir_img(recipe.icon_state,user,SOUTHWEST,"South to West","╗") + lines += render_dir_img(recipe.icon_state,user,SOUTHEAST,"East to South","╔") + + if(PIPE_TRINARY) // Manifold + lines += render_dir_img(recipe.icon_state,user,NORTH,"West South East","╦") + lines += render_dir_img(recipe.icon_state,user,EAST,"North West South","╣") + lines += render_dir_img(recipe.icon_state,user,SOUTH,"East North West","╩") + lines += render_dir_img(recipe.icon_state,user,WEST,"South East North","╠") + + if(PIPE_TRIN_M) // Mirrored ones + //each mirror icon is 45 anticlockwise from it's real direction + lines += render_dir_img(recipe.icon_state,user,NORTH,"West South East","╦") + lines += render_dir_img(recipe.icon_state,user,EAST,"North West South","╣") + lines += render_dir_img(recipe.icon_state,user,SOUTH,"East North West","╩") + lines += render_dir_img(recipe.icon_state,user,WEST,"South East North","╠") + lines += "
" + lines += render_dir_img(recipe.icon_state_m,user,SOUTH,"West South East","╦", 1) + lines += render_dir_img(recipe.icon_state_m,user,WEST,"South East North","╠", 1) + lines += render_dir_img(recipe.icon_state_m,user,NORTH,"East North West","╩", 1) + lines += render_dir_img(recipe.icon_state_m,user,EAST,"North West South","╣", 1) + + if(PIPE_DIRECTIONAL) // Stuff with four directions - includes pumps etc. + lines += render_dir_img(recipe.icon_state,user,NORTH,"North","↑") + lines += render_dir_img(recipe.icon_state,user,EAST,"East","→") + lines += render_dir_img(recipe.icon_state,user,SOUTH,"South","↓") + lines += render_dir_img(recipe.icon_state,user,WEST,"West","←") + + if(PIPE_ONEDIR) // Single icon_state (eg 4-way manifolds) + lines += render_dir_img(recipe.icon_state,user,SOUTH,"Pipe","↕") + lines += "
" + + if(mode == ATMOS_MODE || mode == PAINT_MODE) + lines += "

Color:

" + var/i = 0 + for(var/c in pipe_colors) + ++i + lines += "[c]" + if(i == 4) + lines += "
" + i = 0 + lines += "
" + + lines += "

Mode:

" + lines += "Lay Pipes" + lines += "Eat Pipes" + lines += "Paint Pipes" + lines += "
" + + lines += "

Category:

" + lines += "Atmospherics" + lines += "Disposals" + //lines += "Transit Tube" + lines += "
Wrench Mode" + lines += "
" + + if(screen == ATMOS_MODE) + for(var/category in atmos_pipe_recipes) + lines += "

[category]:

" + + if(category == "Pipes") + lines += "
" + for(var/pipename in pipe_layers) + var/pipelayer = pipe_layers[pipename] + lines += "[pipename] " + lines += "
" + lines += "
" + for(var/i in 1 to atmos_pipe_recipes[category].len) + var/datum/pipe_recipe/PI = atmos_pipe_recipes[category][i] + lines += "
" + lines += "[PI.name]" + lines += "
" + lines += "
" + else if(screen == DISPOSALS_MODE) + for(var/category in disposal_pipe_recipes) + lines += "

[category]:

" + for(var/i in 1 to disposal_pipe_recipes[category].len) + var/datum/pipe_recipe/PI = disposal_pipe_recipes[category][i] + lines += "
" + lines += "[PI.name]" + lines += "
" + lines += "
" + + var/dat = lines.Join() + var/datum/browser/popup = new(user, "rpd", name, 300, 800, src) + popup.set_content("[dat]") + popup.add_head_content(icon_assets.css_tag()) + popup.open() + +/obj/item/weapon/pipe_dispenser/Topic(href, href_list, state = global.inventory_state) + SetupPipes() + if(..()) + return 1 + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + return 1 + var/playeffect = TRUE // Do we spark the device + var/anyclicked = FALSE // Tells us if we need to refresh the window. + if(href_list["paint_color"]) + paint_color = href_list["paint_color"] + playeffect = FALSE + anyclicked = TRUE + if(href_list["mode"]) + mode = text2num(href_list["mode"]) + anyclicked = TRUE + if(href_list["screen"]) + if(mode == screen) + mode = text2num(href_list["screen"]) + screen = text2num(href_list["screen"]) + switch(screen) + if(DISPOSALS_MODE) + recipe = first_disposal + if(ATMOS_MODE) + recipe = first_atmos + p_dir = NORTH + playeffect = FALSE + anyclicked = TRUE + if(href_list["piping_layer"]) + piping_layer = text2num(href_list["piping_layer"]) + playeffect = FALSE + anyclicked = TRUE + if(href_list["pipe_type"]) + recipe = all_pipe_recipes[href_list["category"]][text2num(href_list["pipe_type"])] + if(recipe.dirtype == PIPE_ONEDIR) // One hell of a hack for the fact that the image previews for the onedir types only show on the south, but the default pipe type is north. + p_dir = SOUTH // Did I fuck this up? Maybe. Or maybe it's just the icon files not being ready for an RPD. + else // If going to try and fix this hack, be aware the pipe dispensers might rely on pipes defaulting south instead of north. + p_dir = NORTH + p_flipped = FALSE + anyclicked = TRUE + if(href_list["dir"]) + p_dir = text2dir(href_list["dir"]) + p_flipped = text2num(href_list["flipped"]) + playeffect = FALSE + anyclicked = TRUE + if(href_list["switch_wrench"]) + wrench_mode = text2num(href_list["wrench_mode"]) + anyclicked = TRUE + if(anyclicked) + if(playeffect) + spark_system.start() + playsound(get_turf(src), 'sound/effects/pop.ogg', 50, 0) + src.interact(usr) + +/obj/item/weapon/pipe_dispenser/afterattack(atom/A, mob/user as mob, proximity) + if(!user.IsAdvancedToolUser() || istype(A, /turf/space/transit) || !proximity) + return ..() + + //So that changing the menu settings doesn't affect the pipes already being built. + var/queued_piping_layer = piping_layer + var/queued_p_dir = p_dir + var/queued_p_flipped = p_flipped + + //make sure what we're clicking is valid for the current mode + var/static/list/make_pipe_whitelist // This should probably be changed to be in line with polaris standards. Oh well. + if(!make_pipe_whitelist) + make_pipe_whitelist = typecacheof(list(/obj/structure/lattice, /obj/structure/girder, /obj/item/pipe)) + var/can_make_pipe = (isturf(A) || is_type_in_typecache(A, make_pipe_whitelist)) + + . = FALSE + switch(mode) //if we've gotten this var, the target is valid + if(PAINT_MODE) //Paint pipes + if(!istype(A, /obj/machinery/atmospherics/pipe)) + return ..() + var/obj/machinery/atmospherics/pipe/P = A + playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) + P.change_color(pipe_colors[paint_color]) + user.visible_message("[user] paints \the [P] [paint_color].", "You paint \the [P] [paint_color].") + return + + if(EATING_MODE) //Eating pipes + if(!(istype(A, /obj/item/pipe) || istype(A, /obj/item/pipe_meter) || istype(A, /obj/structure/disposalconstruct))) + return ..() + to_chat(user, "You start destroying a pipe...") + playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) + if(do_after(user, 2, target = A)) + activate() + animate_deletion(A) + + if(ATMOS_MODE) //Making pipes + if(!can_make_pipe) + return ..() + playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) + if (istype(recipe, /datum/pipe_recipe/meter)) + to_chat(user, "You start building a meter...") + if(do_after(user, 2, target = A)) + activate() + var/obj/item/pipe_meter/PM = new /obj/item/pipe_meter(get_turf(A)) + PM.setAttachLayer(queued_piping_layer) + if(wrench_mode) + do_wrench(PM, user) + else if(istype(recipe, /datum/pipe_recipe/pipe)) + var/datum/pipe_recipe/pipe/R = recipe + to_chat(user, "You start building a pipe...") + if(do_after(user, 2, target = A)) + activate() + var/obj/machinery/atmospherics/path = R.pipe_type + var/pipe_item_type = initial(path.construction_type) || /obj/item/pipe + var/obj/item/pipe/P = new pipe_item_type(get_turf(A), path, queued_p_dir) + + P.update() + P.add_fingerprint(usr) + if (R.paintable) + P.color = pipe_colors[paint_color] + P.setPipingLayer(queued_piping_layer) + if(queued_p_flipped) + P.do_a_flip() + if(wrench_mode) + do_wrench(P, user) + else + build_effect(P) + + if(DISPOSALS_MODE) //Making disposals pipes + var/datum/pipe_recipe/disposal/R = recipe + if(!istype(R) || !can_make_pipe) + return ..() + A = get_turf(A) + if(istype(A, /turf/unsimulated)) + to_chat(user, "[src]'s error light flickers; there's something in the way!") + return + to_chat(user, "You start building a disposals pipe...") + playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) + if(do_after(user, 4, target = A)) + var/obj/structure/disposalconstruct/C = new(A, R.pipe_type, queued_p_dir, queued_p_flipped, R.subtype) + + if(!C.can_place()) + to_chat(user, "There's not enough room to build that here!") + qdel(C) + return + + activate() + + C.add_fingerprint(usr) + C.update_icon() + if(wrench_mode) + do_wrench(C, user) + else + build_effect(C) + + else + return ..() + +/obj/item/weapon/pipe_dispenser/proc/build_effect(var/obj/P, var/time = 1.5) + set waitfor = FALSE + P.filters += filter(type = "angular_blur", size = 30) + animate(P.filters[P.filters.len], size = 0, time = time) + var/outline = filter(type = "outline", size = 1, color = "#22AAFF") + P.filters += outline + sleep(time) + P.filters -= outline + P.filters -= filter(type = "angular_blur", size = 0) + +/obj/item/weapon/pipe_dispenser/proc/animate_deletion(var/obj/P, var/time = 1.5) + set waitfor = FALSE + P.filters += filter(type = "angular_blur", size = 0) + animate(P.filters[P.filters.len], size = 30, time = time) + sleep(time) + if(!QDELETED(P)) + P.filters -= filter(type = "angular_blur", size = 30) + qdel(P) + +/obj/item/weapon/pipe_dispenser/proc/activate() + playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, 1) + +/obj/item/weapon/pipe_dispenser/proc/do_wrench(var/atom/target, mob/user) + var/resolved = target.attackby(tool,user) + if(!resolved && tool && target) + tool.afterattack(target,user,1) + +/obj/item/weapon/pipe_dispenser/proc/render_dir_img(icon_state, user, _dir, title, noimg, flipped = FALSE) + var/dirtext = dir2text(_dir) + var/attrs = " style=\"height:34px;width:34px;display:inline-block\"" + if(_dir == p_dir && flipped == p_flipped) + attrs += " class=\"linkOn\"" + if(icon_state) + var/img_tag = icon_assets.icon_tag(icon_state, _dir) + return "[img_tag]" + else + return "[noimg]" + + +#undef PAINT_MODE +#undef EATING_MODE +#undef ATMOS_MODE +#undef DISPOSALS_MODE diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 0206b901dc..5580c7fd98 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -334,3 +334,15 @@ You can set verify to TRUE if you want send() to sleep until the client has the send_asset_list(client, uncommon) send_asset_list(client, common) + + +// VOREStation Add Start - pipes iconsheet asset +/datum/asset/iconsheet/pipes + name = "pipes" + +/datum/asset/iconsheet/pipes/register() + var/list/sprites = list() + for (var/each in list('icons/obj/pipe-item.dmi', 'icons/obj/pipes/disposal.dmi')) + sprites += build_sprite_list(each, global.alldirs) + ..(sprites) +// VOREStation Add End diff --git a/code/modules/recycling/disposal-construction.dm b/code/modules/recycling/disposal-construction.dm index e9d4e4227d..eccc71d33c 100644 --- a/code/modules/recycling/disposal-construction.dm +++ b/code/modules/recycling/disposal-construction.dm @@ -344,3 +344,22 @@ return 1 else return 0 + +// VOREStation Add Start - Helper procs for RCD +/obj/structure/disposalconstruct/proc/is_pipe() + return (ptype != DISPOSAL_PIPE_BIN && ptype != DISPOSAL_PIPE_OUTLET && ptype != DISPOSAL_PIPE_CHUTE) + +//helper proc that makes sure you can place the construct (i.e no dense objects stacking) +/obj/structure/disposalconstruct/proc/can_place() + if(is_pipe()) + return TRUE + + for(var/obj/structure/disposalconstruct/DC in get_turf(src)) + if(DC == src) + continue + + if(!DC.is_pipe()) //there's already a chute/outlet/bin there + return FALSE + + return TRUE +// VOREStation Add End diff --git a/icons/mob/items/lefthand_vr.dmi b/icons/mob/items/lefthand_vr.dmi index c033b49117..39a6e5bdbd 100644 Binary files a/icons/mob/items/lefthand_vr.dmi and b/icons/mob/items/lefthand_vr.dmi differ diff --git a/icons/mob/items/righthand_vr.dmi b/icons/mob/items/righthand_vr.dmi index 9a136dc19d..abeedba03d 100644 Binary files a/icons/mob/items/righthand_vr.dmi and b/icons/mob/items/righthand_vr.dmi differ diff --git a/icons/obj/tools_vr.dmi b/icons/obj/tools_vr.dmi index 0b8f5f0490..53cf604156 100644 Binary files a/icons/obj/tools_vr.dmi and b/icons/obj/tools_vr.dmi differ diff --git a/vorestation.dme b/vorestation.dme index 5c3cfcf5b5..8e8861d6b4 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1185,6 +1185,7 @@ #include "code\game\objects\items\weapons\policetape.dm" #include "code\game\objects\items\weapons\RCD.dm" #include "code\game\objects\items\weapons\RCD_vr.dm" +#include "code\game\objects\items\weapons\RPD_vr.dm" #include "code\game\objects\items\weapons\RSF.dm" #include "code\game\objects\items\weapons\scrolls.dm" #include "code\game\objects\items\weapons\shields.dm"