From f825c9af6bfc10f751806017e80eadec9695608b Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Sat, 23 Mar 2024 03:47:03 -0700 Subject: [PATCH] adds persistent sheet silos to mining (#6353) 65% multiplier - 10 per sheet constant loss requested by @TheObserver-sys and signed off by me mining rework will make this more balanced but vorestation has it; i think it's fine to throw mining/science a bone. --------- Co-authored-by: silicons Co-authored-by: BlueWildrose <57083662+BlueWildrose@users.noreply.github.com> --- citadel.dme | 2 + code/__DEFINES/controllers/persistence.dm | 5 + code/__DEFINES/is_helpers.dm | 13 + code/__DEFINES/materials/flags.dm | 4 + .../persistence/modules/level_objects.dm | 16 +- .../persistence/modules/spatial_metadata.dm | 2 +- code/datums/datum.dm | 11 + code/game/atoms/atom.dm | 28 +- code/game/atoms/movable/movable.dm | 50 ++- code/game/objects/objs.dm | 12 +- .../verbs/debug/reestablish_db_connection.dm | 4 + code/modules/mining/machine_processing.dm | 4 +- code/modules/mining/machine_stacking.dm | 4 +- code/modules/mining/machine_unloading.dm | 2 +- code/modules/mining/machinery/sheet_silo.dm | 129 ++++++ .../mining/machinery/sheet_silo_loader.dm | 26 ++ .../ore_redemption_machine/survey_vendor.dm | 2 +- code/modules/mob/characteristics/holder.dm | 4 +- .../file_system/computer_file.dm | 9 +- .../programs/generic/file_browser.dm | 3 +- code/modules/species/species.dm | 3 +- icons/modules/mining/machinery/sheet_silo.dmi | Bin 0 -> 1114 bytes .../mining/machinery/sheet_silo_loader.dmi | Bin 0 -> 398 bytes icons/obj/machines/mining_machines.dmi | Bin 7598 -> 9397 bytes icons/obj/machines/mining_machines_vr.dmi | Bin 19295 -> 0 bytes maps/minitest/levels/minitest.dmm | 19 +- maps/rift/levels/rift-02-underground2.dmm | 6 + maps/rift/levels/rift-08-west_deep.dmm | 279 +++++-------- maps/tether/levels/station2.dmm | 369 +++++++----------- maps/triumph/levels/deck2.dmm | 64 ++- .../tgui/interfaces/machines/SheetSilo.tsx | 65 +++ 31 files changed, 643 insertions(+), 492 deletions(-) create mode 100644 code/modules/mining/machinery/sheet_silo.dm create mode 100644 code/modules/mining/machinery/sheet_silo_loader.dm create mode 100644 icons/modules/mining/machinery/sheet_silo.dmi create mode 100644 icons/modules/mining/machinery/sheet_silo_loader.dmi delete mode 100644 icons/obj/machines/mining_machines_vr.dmi create mode 100644 tgui/packages/tgui/interfaces/machines/SheetSilo.tsx diff --git a/citadel.dme b/citadel.dme index 5c2ea9d1f9f..97f80de0530 100644 --- a/citadel.dme +++ b/citadel.dme @@ -3340,6 +3340,8 @@ #include "code\modules\mining\vertibore.dm" #include "code\modules\mining\drilling\drill.dm" #include "code\modules\mining\drilling\scanner.dm" +#include "code\modules\mining\machinery\sheet_silo.dm" +#include "code\modules\mining\machinery\sheet_silo_loader.dm" #include "code\modules\mining\ore_redemption_machine\construction.dm" #include "code\modules\mining\ore_redemption_machine\engineering_points_vendor.dm" #include "code\modules\mining\ore_redemption_machine\equipment_vendor.dm" diff --git a/code/__DEFINES/controllers/persistence.dm b/code/__DEFINES/controllers/persistence.dm index 75e64804703..4f234acbb3d 100644 --- a/code/__DEFINES/controllers/persistence.dm +++ b/code/__DEFINES/controllers/persistence.dm @@ -9,6 +9,9 @@ #define OBJ_HAS_PERSISTENCE_ENABLED(OBJ) (OBJ.persist_static_id || OBJ.persist_dynamic_id) /// check if an /obj is eligible at all for mass persistence #define OBJ_MASS_PERSIST_SANITY_CHECK(OBJ) (OBJ_HAS_PERSIST_ENABLED(OBJ) && !(OBJ.obj_persist_status & (OBJ_PERSIST_STATUS_NO_THANK_YOU))) +/// sentinel id; set this if you want something to persist but generate its own dynamic id +/// instead of setting one manually +#define PERSISTENCE_DYNAMIC_ID_AUTOSET "!autoset" //* /obj - obj_persist_status *// @@ -22,6 +25,8 @@ #define OBJ_PERSIST_STATUS_FIRST_GENERATION (1<<2) /// do not persist #define OBJ_PERSIST_STATUS_NO_THANK_YOU (1<<3) +/// do not show persistent status on examine +#define OBJ_PERSIST_STATUS_NO_EXAMINE (1<<4) //* /obj - obj_persist_dynamic_status *// diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 7447ca482ee..469155b777b 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -2,6 +2,19 @@ #define in_range(source, user) (get_dist(source, user) <= 1 && (get_step(source, 0)?:z) == (get_step(user, 0)?:z)) +//* Datatypes *// + +/** + * used to filter nans and non-numbers from player input + */ +#if DM_VERSION < 515 +#define is_safe_number(N) (isnum(N) && (N == N)) +#else +#define is_safe_number(N) (isnum(N) && !isnan(N)) +#endif + +//* Typepaths *// + #define isatom(A) (isloc(A)) #define isdatum(D) (istype(D, /datum)) diff --git a/code/__DEFINES/materials/flags.dm b/code/__DEFINES/materials/flags.dm index 4bfd28a8d66..a22e4c3388f 100644 --- a/code/__DEFINES/materials/flags.dm +++ b/code/__DEFINES/materials/flags.dm @@ -7,10 +7,14 @@ #define MATERIAL_FLAG_VULNERABLE_MOB_ARMOR (1<<0) /// utterly immune to melting, thermite or otherwise #define MATERIAL_FLAG_UNMELTABLE (1<<1) +/// too dangerous to persist +/// this is a somewhat awful flag so we'll keep it until we need to get rid of it +#define MATERIAL_FLAG_CONSIDERED_OVERPOWERED (1<<2) DEFINE_BITFIELD(material_flags, list( BITFIELD(MATERIAL_FLAG_VULNERABLE_MOB_ARMOR), BITFIELD(MATERIAL_FLAG_UNMELTABLE), + BITFIELD(MATERIAL_FLAG_CONSIDERED_OVERPOWERED), )) //* /datum/material material_constraints diff --git a/code/controllers/subsystem/persistence/modules/level_objects.dm b/code/controllers/subsystem/persistence/modules/level_objects.dm index 84f6bee8a56..43072527d78 100644 --- a/code/controllers/subsystem/persistence/modules/level_objects.dm +++ b/code/controllers/subsystem/persistence/modules/level_objects.dm @@ -27,19 +27,19 @@ "generation" = generation, "object_id" = entity.obj_persist_static_id, "level_id" = entity.obj_persist_static_bound_id || level_id, - "data" = entity.serialize(), + "data" = safe_json_encode(entity.serialize()), ), ) if(OBJ_PERSIST_STATIC_MODE_MAP) query = SSdbcore.NewQuery( - "INSERT INTO [format_table_name("persistence_static_mapl_objects")] (generation, object_id, map_id, data) \ + "INSERT INTO [format_table_name("persistence_static_map_objects")] (generation, object_id, map_id, data) \ VALUES (:generation, :object_id, :map_id, :data) ON DUPLICATE KEY UPDATE \ data = VALUES(data)", list( "generation" = generation, "object_id" = entity.obj_persist_static_id, "map_id" = entity.obj_persist_static_bound_id || map_id, - "data" = entity.serialize(), + "data" = safe_json_encode(entity.serialize()), ), ) if(OBJ_PERSIST_STATIC_MODE_GLOBAL) @@ -50,7 +50,7 @@ list( "generation" = generation, "object_id" = entity.obj_persist_static_id, - "data" = entity.serialize(), + "data" = safe_json_encode(entity.serialize()), ), ) else @@ -77,7 +77,7 @@ for(var/obj/entity as anything in entities) var/datum/db_query/query - if(entity.obj_persist_dynamic_id) + if(entity.obj_persist_dynamic_id != PERSISTENCE_DYNAMIC_ID_AUTOSET) query = SSdbcore.NewQuery( "INSERT INTO [format_table_name("persistence_dynamic_objects")] (id, generation, status, data, prototype_id, level_id, x, y) \ VALUES (:status, :data, :prototype, :level, :x, :y) ON DUPLICATE KEY UPDATE \ @@ -87,7 +87,7 @@ "id" = entity.obj_persist_dynamic_id, "generation" = generation, "status" = entity.obj_persist_dynamic_status, - "data" = entity.serialize(), + "data" = safe_json_encode(entity.serialize()), "prototype" = "[entity.type]", "level" = level_id, "x" = entity.x, @@ -101,7 +101,7 @@ VALUES (:status, :data, :prototype, :level, :x, :y)", list( "status" = entity.obj_persist_dynamic_status, - "data" = entity.serialize(), + "data" = safe_json_encode(entity.serialize()), "prototype" = "[entity.type]", "level" = level_id, "x" = entity.x, @@ -197,7 +197,7 @@ for(var/obj/entity as anything in entities) var/datum/db_query/query var/bind_id - switch(entity.obj_persist_dynamic_status) + switch(entity.obj_persist_static_mode) if(OBJ_PERSIST_STATIC_MODE_GLOBAL) query = SSdbcore.NewQuery( "SELECT data FROM [format_table_name("persistence_static_global_objects")] \ diff --git a/code/controllers/subsystem/persistence/modules/spatial_metadata.dm b/code/controllers/subsystem/persistence/modules/spatial_metadata.dm index dce020f3d31..6f6355ff490 100644 --- a/code/controllers/subsystem/persistence/modules/spatial_metadata.dm +++ b/code/controllers/subsystem/persistence/modules/spatial_metadata.dm @@ -100,7 +100,7 @@ src.generation = generation src.hours_since_saved = 0 src.rounds_since_saved = 0 - src.round_id_saved = GLOB.round_id + src.round_id_saved = GLOB.round_number SSdbcore.RunQuery( "INSERT INTO [format_table_name("persistence_level_metadata")] (saved, saved_round_id, level_id, data, generation) \ diff --git a/code/datums/datum.dm b/code/datums/datum.dm index ecbdbba3da6..e8548a27acc 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -161,6 +161,17 @@ SEND_SIGNAL(source, COMSIG_CD_RESET(index), S_TIMER_COOLDOWN_TIMELEFT(source, index)) TIMER_COOLDOWN_END(source, index) +//* Duplication *// + +/** + * makes a clone of this datum + * + * @params + * * include_contents - include semantic contents; ergo 'what we are hosting' vs 'what we are' + */ +/datum/proc/clone(include_contents) + return new type + //* Serialization *// /** diff --git a/code/game/atoms/atom.dm b/code/game/atoms/atom.dm index b40bab69778..8b2ce9012b3 100644 --- a/code/game/atoms/atom.dm +++ b/code/game/atoms/atom.dm @@ -45,7 +45,8 @@ //? Context /// open context menus by mob - var/list/context_menus + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/list/context_menus //? Economy // todo: move all this to obj level, you aren't going to sell a fucking turf. @@ -143,16 +144,24 @@ //? Materials /// combined material trait flags /// this list is at /atom level but are only used/implemented on /obj generically; anything else, e.g. walls, should implement manually for efficiency. - var/material_trait_flags = NONE + /// * this variable is a cache variable and is generated from the materials on an entity. + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/material_trait_flags = NONE /// material traits on us, associated to metadata /// this list is at /atom level but are only used/implemented on /obj generically; anything else, e.g. walls, should implement manually for efficiency. - var/list/datum/material_trait/material_traits + /// * this variable is a cache variable and is generated from the materials on an entity. + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/list/datum/material_trait/material_traits /// material trait metadata when [material_traits] is a single trait. null otherwise. - var/material_traits_data + /// * this variable is a cache variable and is generated from the materials on an entity. + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/material_traits_data /// 'stacks' of ticking /// this synchronizes the system so removing one ticking material trait doesn't fully de-tick the entity //! DO NOT FUCK WITH THIS UNLESS YOU KNOW WHAT YOU ARE DOING - var/material_ticking_counter = 0 + /// * this variable is a cache variable and is generated from the materials on an entity. + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/material_ticking_counter = 0 /// material trait relative strength /// applies to all traits globally as opposed to just one material parts, /// because this is at /atom level. @@ -170,13 +179,16 @@ /// sorted priority list of datums for handling shieldcalls with /// we use this instead of signals so we can enforce priorities /// this is horrifying. - var/list/datum/shieldcall/shieldcalls + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/list/datum/shieldcall/shieldcalls //? Overlays /// vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays. - var/list/managed_vis_overlays + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/list/managed_vis_overlays /// overlays managed by [update_overlays][/atom/proc/update_overlays] to prevent removing overlays that weren't added by the same proc. Single items are stored on their own, not in a list. - var/list/managed_overlays + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/list/managed_overlays //? Layers /// Base layer - defaults to layer. diff --git a/code/game/atoms/movable/movable.dm b/code/game/atoms/movable/movable.dm index a489d59d2e6..120758bc161 100644 --- a/code/game/atoms/movable/movable.dm +++ b/code/game/atoms/movable/movable.dm @@ -28,26 +28,36 @@ //? Movement /// Whatever we're pulling. - var/atom/movable/pulling + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/atom/movable/pulling /// Who's currently pulling us - var/atom/movable/pulledby + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/atom/movable/pulledby /// If false makes [CanPass][/atom/proc/CanPass] call [CanPassThrough][/atom/movable/proc/CanPassThrough] on this type instead of using default behaviour - var/generic_canpass = TRUE + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/generic_canpass = TRUE /// Pass flags. var/pass_flags = NONE /// movement calls we're in - var/in_move = 0 + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/in_move = 0 /// a direction, or null - var/moving_diagonally = NOT_IN_DIAG_STEP + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/moving_diagonally = NOT_IN_DIAG_STEP /// attempt to resume grab after moving instead of before. This is what atom/movable is pulling us during move-from-pulling. - var/atom/movable/moving_from_pull + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/atom/movable/moving_from_pull /// Direction of our last move. - var/last_move_dir = NONE + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/last_move_dir = NONE /// Our default glide_size. Null to use global default. var/default_glide_size /// Movement types, see [code/__DEFINES/flags/movement.dm] /// Do *not* manually edit this variable in most cases. Use the helpers in [code/game/atoms/atoms_movement.dm]. - var/movement_type = MOVEMENT_GROUND + /// todo: is there a better way to do this? what if we want to force something to be a movement type on map editor? + /// * this variable is a cache variable generated from movement type traits. + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/movement_type = MOVEMENT_GROUND //? Spacedrift /// Which direction we're drifting @@ -63,7 +73,8 @@ //? Perspectives /// our default perspective - if none, a temporary one will be generated when a mob requires it - var/datum/perspective/self_perspective + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/datum/perspective/self_perspective //? Buckling /// do we support the buckling system - if not, none of the default interactions will work, but comsigs will still fire! @@ -77,7 +88,8 @@ /// direction to set buckled mobs to. null to not do that. var/buckle_dir /// buckled mobs, associated to their semantic mode if necessary - var/list/mob/buckled_mobs + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/list/mob/buckled_mobs /// restrained default unbuckle time (NOT TIME TO UN-RESTRAIN, this is time to UNBUCKLE from us) var/buckle_restrained_resist_time = 2 MINUTES @@ -134,9 +146,11 @@ /// Either FALSE, [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE] var/blocks_emissive = FALSE /// Internal holder for emissive blocker object, do not use directly use; use blocks_emissive - var/atom/movable/emissive_blocker/em_block + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/atom/movable/emissive_blocker/em_block /// Internal holder for emissives. Definitely don't directly use, this is absolutely an insane Citadel Moment(tm). - var/atom/movable/emissive_render/em_render + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/atom/movable/emissive_render/em_render //? Icon Scale /// Used to scale icons up or down horizonally in update_transform(). @@ -433,6 +447,18 @@ /atom/movable/proc/is_avoiding_ground() return ((movement_type & MOVEMENT_TYPES) != MOVEMENT_GROUND) || throwing +//* Duplication *// + +/** + * makes a clone of this movable + * + * @params + * * location - where to clone us + * * include_contents - include semantic contents; ergo 'what we are hosting' vs 'what we are' + */ +/atom/movable/clone(atom/location, include_contents) + return ..(include_contents) + //? Perspectives /** * get perspective to use when shifting eye to us, diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 1b61869d85d..dec2d74f8d8 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -100,12 +100,16 @@ var/obj_persist_static_mode = OBJ_PERSIST_STATIC_MODE_MAP /// on static map/level bind mode, this is to determine which level/map we're bound to /// once bound, even if we go to another level, we are treated as this level. - var/obj_persist_static_bound_id + /// binding is done during load. + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/obj_persist_static_bound_id /// if set, we are currently dynamically persisting. this is our ID and must be unique for a given map level. /// this id will not collide with static id. - var/obj_persist_dynamic_id + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/obj_persist_dynamic_id /// dynamic persistence state flags - var/obj_persist_dynamic_status = NONE + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/obj_persist_dynamic_status = NONE //? Sounds /// volume when breaking out using resist process @@ -644,6 +648,8 @@ if(isnull(mat)) // 'none' option continue . += "Its [key] is made out of [mat.display_name]" + if((obj_persist_dynamic_id || obj_persist_static_id) && !(obj_persist_status & OBJ_PERSIST_STATUS_NO_EXAMINE)) + . += SPAN_BOLDNOTICE("This entity is a persistent entity; it may be preserved into future rounds.") /obj/proc/examine_integrity(mob/user) . = list() diff --git a/code/modules/admin/verbs/debug/reestablish_db_connection.dm b/code/modules/admin/verbs/debug/reestablish_db_connection.dm index 33f0e9d8524..4b9fe03c5a6 100644 --- a/code/modules/admin/verbs/debug/reestablish_db_connection.dm +++ b/code/modules/admin/verbs/debug/reestablish_db_connection.dm @@ -31,3 +31,7 @@ message_admins("Reloading client database data...") for(var/client/C in GLOB.clients) C.player?.load() + message_admins("Asserting round ID set...") + if(!isnum(text2num(GLOB.round_id))) + SSdbcore.SetRoundID() + message_admins("Round ID was not set and has now been re-set. Things might be weird this round.") diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index c155b354606..af02717eb6d 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -6,7 +6,7 @@ /obj/machinery/mineral/processing_unit_console name = "production machine console" - icon = 'icons/obj/machines/mining_machines_vr.dmi' + icon = 'icons/obj/machines/mining_machines.dmi' icon_state = "console" density = TRUE anchored = TRUE @@ -136,7 +136,7 @@ /obj/machinery/mineral/processing_unit name = "material processor" //This isn't actually a goddamn furnace, we're in space and it's processing platinum and flammable phoron... - icon = 'icons/obj/machines/mining_machines_vr.dmi' + icon = 'icons/obj/machines/mining_machines.dmi' icon_state = "furnace" density = TRUE anchored = TRUE diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index 4b85b8ff2b1..8f93efbe270 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -2,7 +2,7 @@ /obj/machinery/mineral/stacking_unit_console name = "stacking machine console" - icon = 'icons/obj/machines/mining_machines_vr.dmi' + icon = 'icons/obj/machines/mining_machines.dmi' icon_state = "console" density = 1 anchored = 1 @@ -72,7 +72,7 @@ /obj/machinery/mineral/stacking_machine name = "stacking machine" - icon = 'icons/obj/machines/mining_machines_vr.dmi' + icon = 'icons/obj/machines/mining_machines.dmi' icon_state = "stacker" density = 1 anchored = 1.0 diff --git a/code/modules/mining/machine_unloading.dm b/code/modules/mining/machine_unloading.dm index 59dea20d6b2..0f281c96052 100644 --- a/code/modules/mining/machine_unloading.dm +++ b/code/modules/mining/machine_unloading.dm @@ -2,7 +2,7 @@ /obj/machinery/mineral/unloading_machine name = "unloading machine" - icon = 'icons/obj/machines/mining_machines_vr.dmi' + icon = 'icons/obj/machines/mining_machines.dmi' icon_state = "unloader" density = 1 anchored = 1.0 diff --git a/code/modules/mining/machinery/sheet_silo.dm b/code/modules/mining/machinery/sheet_silo.dm new file mode 100644 index 00000000000..a8e964ac639 --- /dev/null +++ b/code/modules/mining/machinery/sheet_silo.dm @@ -0,0 +1,129 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/** + * persistence allowed storage system + * + * notes: + * * bluespace ore silo/sheet delivery is explicitly not to be made. any prs attempting to do so will be closed. ~silicons + */ +/obj/machinery/sheet_silo + name = "materials silo" + desc = "A reinforced materials storage silo. Inserted sheets are protected via stasis field." + icon = 'icons/modules/mining/machinery/sheet_silo.dmi' + icon_state = "silo" + density = TRUE + anchored = TRUE + integrity_flags = INTEGRITY_INDESTRUCTIBLE + + /// material id to **sheets** + var/list/sheets_by_material = list() + /// how much to multiply all ores by + var/persistence_decay_factor = 0.35 + /// how much to subtract from all ores after factor + var/persistence_decay_constant = 10 + /// ignore overpowered ore filter + var/persistence_allow_overpowered = FALSE + +/obj/machinery/sheet_silo/decay_persisted(rounds_since_saved, hours_since_saved) + . = ..() + for(var/id in sheets_by_material) + // this isn't technically mathematically accurate but honestly + // i don't care + sheets_by_material[id] = max(0, round(sheets_by_material[id] * (persistence_decay_factor ** rounds_since_saved) - persistence_decay_constant * rounds_since_saved)) + if(!sheets_by_material[id]) + sheets_by_material -= id + +/obj/machinery/sheet_silo/attackby(obj/item/I, mob/living/user, list/params, clickchain_flags, damage_multiplier) + if(istype(I, /obj/item/stack/material)) + var/obj/item/stack/material/sheets = I + if(!user.transfer_item_to_loc(sheets, src)) + to_chat(user, SPAN_WARNING("You fail to insert [sheets] into [src].")) + return CLICKCHAIN_DO_NOT_PROPAGATE + var/inserted = take_sheets(sheets) + user.visible_message( + SPAN_NOTICE("[user] inserts [I] into [src]."), + SPAN_NOTICE("You insert [inserted] sheets of [I] into [src]."), + range = MESSAGE_RANGE_INVENTORY_SOFT, + ) + return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING + return ..() + +/obj/machinery/sheet_silo/proc/take_sheets(obj/item/stack/material/sheets) + if(sheets.uses_charge) + return 0 + var/mat_id = sheets.material.id + var/mat_amount = sheets.amount + . = mat_amount + sheets.use(mat_amount) + sheets_by_material[mat_id] += mat_amount + +/obj/machinery/sheet_silo/clone(atom/location, include_contents) + var/obj/machinery/sheet_silo/clone = ..() + clone.sheets_by_material = sheets_by_material.Copy() + return clone + +/obj/machinery/sheet_silo/serialize() + . = ..() + var/list/transformed_sheets = list() + for(var/id in sheets_by_material) + var/datum/material/mat = SSmaterials.resolve_material(id) + if(isnull(mat)) + continue + if(!persistence_allow_overpowered && (mat.material_flags & MATERIAL_FLAG_CONSIDERED_OVERPOWERED)) + continue + var/amount = sheets_by_material[id] + transformed_sheets[id] = amount + .["stored"] = transformed_sheets + +/obj/machinery/sheet_silo/deserialize(list/data) + . = ..() + sheets_by_material = data["stored"] + +/obj/machinery/sheet_silo/ui_assets(mob/user) + . = ..() + . += get_asset_datum(/datum/asset/spritesheet/materials) + +/obj/machinery/sheet_silo/ui_data(mob/user, datum/tgui/ui) + . = ..() + .["stored"] = sheets_by_material + +/obj/machinery/sheet_silo/ui_static_data(mob/user, datum/tgui/ui) + . = ..() + .["materialContext"] = SSmaterials.tgui_materials_context() + +/obj/machinery/sheet_silo/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SheetSilo") + ui.open() + +/obj/machinery/sheet_silo/ui_act(action, list/params, datum/tgui/ui) + . = ..() + if(.) + return + switch(action) + if("eject") + var/amount = params["amount"] + var/id = params["id"] + if(!is_safe_number(amount)) + return TRUE + amount = clamp(amount, 0, sheets_by_material[id]) + if(!amount) + return TRUE + var/datum/material/dropping = SSmaterials.resolve_material(id) + if(isnull(dropping)) + return TRUE + // todo: ughh + var/obj/item/stack/material/casted = dropping.stack_type + amount = min(amount, initial(casted.max_amount)) + if(!amount) + return TRUE + sheets_by_material[id] -= amount + if(sheets_by_material[id] <= 0) + sheets_by_material -= id + var/obj/item/stack/material/dropped = dropping.place_sheet(get_turf(src), amount) + if(usr) + usr.put_in_hands(dropped) + usr.visible_message(SPAN_NOTICE("[usr] retrieves [amount] sheets of [dropping] from [src]."), range = MESSAGE_RANGE_INVENTORY_SOFT) + return TRUE diff --git a/code/modules/mining/machinery/sheet_silo_loader.dm b/code/modules/mining/machinery/sheet_silo_loader.dm new file mode 100644 index 00000000000..d5d21ca63f7 --- /dev/null +++ b/code/modules/mining/machinery/sheet_silo_loader.dm @@ -0,0 +1,26 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/** + * auto-loads sheets into sheet silos + */ +/obj/machinery/sheet_silo_loader + name = "materials silo loader" + desc = "An autoloader for a materials silo." + icon = 'icons/modules/mining/machinery/sheet_silo_loader.dmi' + icon_state = "loader" + anchored = TRUE + integrity_flags = INTEGRITY_INDESTRUCTIBLE + +/obj/machinery/sheet_silo_loader/process(delta_time) + // todo: lazy-ticking + var/list/obj/item/stack/material/stacks = list() + for(var/obj/item/stack/material/matstack in loc) + stacks += matstack + if(!length(stacks)) + return + var/obj/machinery/sheet_silo/silo = locate() in get_step(src, dir) + if(isnull(silo)) + return + for(var/obj/item/stack/material/matstack as anything in stacks) + silo.take_sheets(matstack) diff --git a/code/modules/mining/ore_redemption_machine/survey_vendor.dm b/code/modules/mining/ore_redemption_machine/survey_vendor.dm index 1683c98b6bc..f6e2675dc7d 100644 --- a/code/modules/mining/ore_redemption_machine/survey_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/survey_vendor.dm @@ -1,7 +1,7 @@ /obj/machinery/mineral/equipment_vendor/survey name = "exploration equipment vendor" desc = "An equipment vendor for explorers, points collected with a survey scanner can be spent here." - icon = 'icons/obj/machines/mining_machines_vr.dmi' + icon = 'icons/obj/machines/mining_machines.dmi' icon_state = "exploration" density = TRUE anchored = TRUE diff --git a/code/modules/mob/characteristics/holder.dm b/code/modules/mob/characteristics/holder.dm index 9ce0b73f00a..aa1dfca4d94 100644 --- a/code/modules/mob/characteristics/holder.dm +++ b/code/modules/mob/characteristics/holder.dm @@ -134,9 +134,9 @@ /** * clones */ -/datum/characteristics_holder/proc/clone() +/datum/characteristics_holder/clone(include_contents) RETURN_TYPE(/datum/characteristics_holder) - var/datum/characteristics_holder/cloning = new + var/datum/characteristics_holder/cloning = ..() cloning.skills = skills.Copy() cloning.stats = stats.Copy() cloning.talents = talents.Copy() diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm index ef886b871ed..51c1206bf1a 100644 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -26,15 +26,12 @@ var/global/file_uid = 0 return ..() // Returns independent copy of this file. -/datum/computer_file/proc/clone(var/rename = 0) - var/datum/computer_file/temp = new type +/datum/computer_file/clone(include_contents) + var/datum/computer_file/temp = ..() temp.unsendable = unsendable temp.undeletable = undeletable temp.size = size - if(rename) - temp.filename = filename + "(Copy)" - else - temp.filename = filename + temp.filename = filename temp.filetype = filetype return temp diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index 4d578217977..295f4aabf99 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -46,7 +46,8 @@ var/datum/computer_file/F = HDD.find_file_by_name(params["name"]) if(!F || !istype(F)) return - var/datum/computer_file/C = F.clone(1) + var/datum/computer_file/C = F.clone() + C.filename = F.filename + "(Copy)" HDD.store_file(C) return TRUE if("PRG_edit") diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index f49084ac816..483aa2255e5 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -875,9 +875,10 @@ GLOBAL_LIST_INIT(species_oxygen_tank_by_gas, list( /** * clones us into a new datum */ -/datum/species/proc/clone() +/datum/species/clone(include_contents) var/datum/species/created = new type created.copy_from(src) + return created /** * completely clones us from another species, updating the provided human in the process diff --git a/icons/modules/mining/machinery/sheet_silo.dmi b/icons/modules/mining/machinery/sheet_silo.dmi new file mode 100644 index 0000000000000000000000000000000000000000..e06d0fe2d328b926e9005b18de1de5329cfc92f1 GIT binary patch literal 1114 zcmV-g1f~0lP)QjB(N z9!qHzLSVn=|Ks%kz`(%3Q(8;_0004WQchCV=- z0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zi zl2jm5sW>wyUx|w|C9|j)$Tj5ROe;#vO@**g6vZbdmt>ZuVpnSh6iv-ZtON<`z#*5i zf~%hk*jWHy*ejSElg6U}00RO^L_t(&f$f=za@sHuhNTD@40WNPl2VFjLX*7z8+Iib zpORL1l}skXnq!9V_a0pH@0hW#68swP!#RHtLd+nfS3>3whSv%?R^;ME0Rk~rczEy4 z!1z^A6!V!?1QPW4VzFoxD3`ThKA+bulwCoHFKYqDCns2aOXEf$t^)TijuMdgRZt)a zCiAYKsuB}?U#?c`L=J+@4;NKYKm}2jg9HtC4FKm7Vx7jfT<$h53RgfWRSU`}mr2|T ze%jd0;ipmwq4tl11ix$;Ud=?MIPk;oLmD@NOI9oM0bpTyw}B`(g5w_> z8#BR7j1%~HcY-K4g46D3j+31L-owYi1U}wvAj+BE%9G{Qsc?T+{SPU?n+`F~&U$M* z_-i|G-{Z5Jpe)1OL=tD`Cw)BGI1U75slpsOOylg#wLaR|)=^$VsSF%OeAYs@q1xao z1BVWtwZ6cA2|zQVk(kdwDOq>u>c?06L8Ne~dT##n;^r zlKp>xAIST`8*x9t{6DroG<>T6Hwj|A-7hBje>mM7_x<1=9~zkdC#R45dvbon-{;C7 z^!J$x1pPfZKj!b>tH3f>1=QbXDiHSf84LLRJ-tBO-%|ti_c;p0t{>zm5WRkoqd?r> zXDAT8en1Y?-%|s-en2mP`FnB!%-@p>VE&$50J(lZFM#=bdI8MeQv>(+)WH3Hh6*h1 g@97op{yrH00bmR9ukoJ&_y7O^07*qoM6N<$g6%^MjsO4v literal 0 HcmV?d00001 diff --git a/icons/modules/mining/machinery/sheet_silo_loader.dmi b/icons/modules/mining/machinery/sheet_silo_loader.dmi new file mode 100644 index 0000000000000000000000000000000000000000..17d0f1fa360d1d9a2cf0d59b247aa112f07cde56 GIT binary patch literal 398 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 sNCzo_5V#6R6;wKjD58QirAb#I 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

InQ%wzZf;x{T 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

+ + {Object.entries(data.stored).sort( + ([k1, v1], [k2, v2]) => ( + (data.materialContext.materials[k1]?.name || "AAAAA").localeCompare( + (data.materialContext.materials[k2]?.name || "AAAAA") + ) + ) + ).map(([k, v]) => ( + + + + + {data.materialContext.materials[k]?.name || 'UNKNOWN'} - {v} + + + +
+ + + ); +};