diff --git a/.gitattributes b/.gitattributes index d5d501e85607..81a615f78ebe 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,6 +12,11 @@ html/changelog.html merge=union # Declare files that will always have CRLF line endings on checkout. *.dm text eol=crlf *.dmm text eol=crlf +*.dme text eol=crlf +*.py text eol=crlf *.txt text eol=crlf *.md text eol=crlf -*.yml text eol=crlf \ No newline at end of file +*.yml text eol=crlf + +# Declare files that will always have LF line endings on checkout. +*.sh text eol=lf diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 100cba7cc7d1..b1e9a6852374 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,8 +33,8 @@ jobs: - name: Run Dreamchecker run: | set -o pipefail - ~/dreamchecker 2>&1 | tee ${GITHUB_WORKSPACE}/output-annotations.txt ./test/lint-all-modpacks.sh nebula.dme + ./test/lint-each-modpack.sh nebula.dme - name: Annotate Lints uses: yogstation13/DreamAnnotate@v2 if: always() @@ -67,7 +67,7 @@ jobs: fileName: "DMCompiler_linux-x64.tar.gz" extract: true - name: Run OpenDream - run: ./DMCompiler_linux-x64/DMCompiler nebula.dme --define=UNIT_TEST --suppress-unimplemented --skip-anything-typecheck --version=${BYOND_MAJOR}.${BYOND_MINOR} | bash test/annotate_od.sh + run: ./test/compile-od-all-modpacks.sh nebula.dme Code: runs-on: ubuntu-latest steps: diff --git a/code/__defines/armor.dm b/code/__defines/armor.dm index 155ccb22f4ac..c43e7cc2f263 100644 --- a/code/__defines/armor.dm +++ b/code/__defines/armor.dm @@ -33,6 +33,7 @@ #define ARMOR_RAD_MINOR 10 #define ARMOR_RAD_SMALL 25 #define ARMOR_RAD_RESISTANT 40 +#define ARMOR_RAD_LARGE 60 #define ARMOR_RAD_SHIELDED 100 #define ARMOR_BOMB_MINOR 10 diff --git a/code/__defines/items_clothing.dm b/code/__defines/items_clothing.dm index ffd20f690f91..eaa502407f58 100644 --- a/code/__defines/items_clothing.dm +++ b/code/__defines/items_clothing.dm @@ -82,6 +82,7 @@ #define slot_r_store_str "slot_r_store" #define slot_s_store_str "slot_s_store" #define slot_in_backpack_str "slot_in_backpack" +#define slot_in_wallet_str "slot_in_wallet" // Defined here for consistency, not actually used for slots, just for species clothing offsets. #define slot_undershirt_str "slot_undershirt" diff --git a/code/__defines/lists.dm b/code/__defines/lists.dm index c29a24a164d7..9935dab6b5c7 100644 --- a/code/__defines/lists.dm +++ b/code/__defines/lists.dm @@ -32,6 +32,14 @@ // Reads L or an empty list if L is not a list. Note: Does NOT assign, L may be an expression. #define SANITIZE_LIST(L) ( islist(L) ? L : list() ) +// The above but for alists. Prefixed with A_ because inserting "A" randomly in the name just made it confusing +#define A_LAZYINITLIST(AL) if (!AL) { AL = alist(); } +#define A_UNSETEMPTY(AL) if(!length(AL)) { AL = null; } +#define A_LAZYREMOVE(AL, I) if(AL) { AL -= I; A_UNSETEMPTY(AL) } +#define A_LAZYSET(AL, A, I) if(!AL) { AL = alist(); } AL[A] = I; +#define A_LAZYCLEARLIST(AL) if(AL) { AL.Cut(); AL = null; } +#define A_LAZYLEN(AL) length(AL) + /// Passed into BINARY_INSERT to compare keys #define COMPARE_KEY __BIN_LIST[__BIN_MID] /// Passed into BINARY_INSERT to compare values diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm index 787488e127f9..b157b96d5f8a 100644 --- a/code/__defines/machinery.dm +++ b/code/__defines/machinery.dm @@ -42,28 +42,27 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called // Camera channels // Station channels -#define CAMERA_CHANNEL_PUBLIC "Public" -#define CAMERA_CHANNEL_ENGINEERING "Engineering" -#define CAMERA_CHANNEL_MEDICAL "Medical" -#define CAMERA_CHANNEL_RESEARCH "Research" -#define CAMERA_CHANNEL_SECURITY "Security" - -#define CAMERA_CHANNEL_ROBOTS "Robots" -#define CAMERA_CHANNEL_MINE "Mining" -#define CAMERA_CHANNEL_SECRET "Secret" +#define CAMERA_CHANNEL_PUBLIC "Public" +#define CAMERA_CHANNEL_ENGINEERING "Engineering" +#define CAMERA_CHANNEL_MEDICAL "Medical" +#define CAMERA_CHANNEL_RESEARCH "Research" +#define CAMERA_CHANNEL_SECURITY "Security" +#define CAMERA_CHANNEL_ROBOTS "Robots" +#define CAMERA_CHANNEL_MINE "Mining" +#define CAMERA_CHANNEL_SECRET "Secret" // Non-station channels -#define CAMERA_CHANNEL_CRESCENT "Crescent" -#define CAMERA_CHANNEL_ERT "ZeEmergencyResponseTeam" -#define CAMERA_CHANNEL_MERCENARY "MercurialNet" -#define CAMERA_CHANNEL_TELEVISION "Television" +#define CAMERA_CHANNEL_CRESCENT "Crescent" +#define CAMERA_CHANNEL_ERT "Emergency Response Team" +#define CAMERA_CHANNEL_MERCENARY "MercurialNet" +#define CAMERA_CHANNEL_TELEVISION "Television" // Alarm networks -#define NETWORK_ALARM_ATMOS "Atmosphere Alarms" +#define NETWORK_ALARM_ATMOS "Atmosphere Alarms" #define NETWORK_ALARM_CAMERA "Camera Alarms" -#define NETWORK_ALARM_FIRE "Fire Alarms" +#define NETWORK_ALARM_FIRE "Fire Alarms" #define NETWORK_ALARM_MOTION "Motion Alarms" -#define NETWORK_ALARM_POWER "Power Alarms" +#define NETWORK_ALARM_POWER "Power Alarms" //singularity defines #define STAGE_ONE 1 diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 4d5a639708f0..06d5c69bf2ae 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -87,27 +87,28 @@ #define EVENT_LEVEL_MAJOR 3 //Area flags, possibly more to come -#define AREA_FLAG_RAD_SHIELDED BITFLAG(1) // Shielded from radiation, clearly. -#define AREA_FLAG_EXTERNAL BITFLAG(2) // External as in exposed to space, not outside in a nice, green, forest. -#define AREA_FLAG_ION_SHIELDED BITFLAG(3) // Shielded from ionospheric anomalies. -#define AREA_FLAG_IS_NOT_PERSISTENT BITFLAG(4) // SSpersistence will not track values from this area. -#define AREA_FLAG_IS_BACKGROUND BITFLAG(5) // Blueprints can create areas on top of these areas. Cannot edit the name of or delete these areas. -#define AREA_FLAG_MAINTENANCE BITFLAG(6) // Area is a maintenance area. -#define AREA_FLAG_SHUTTLE BITFLAG(7) // Area is a shuttle area. -#define AREA_FLAG_HALLWAY BITFLAG(8) // Area is a public hallway suitable for event selection -#define AREA_FLAG_PRISON BITFLAG(9) // Area is a prison for the purposes of brigging objectives. -#define AREA_FLAG_HOLY BITFLAG(10) // Area is holy for the purposes of marking turfs as cult-resistant. -#define AREA_FLAG_SECURITY BITFLAG(11) // Area is security for the purposes of newscaster init. -#define AREA_FLAG_HIDE_FROM_HOLOMAP BITFLAG(12) // if we shouldn't be drawn on station holomaps +#define AREA_FLAG_RAD_SHIELDED BITFLAG(1) // Shielded from radiation, clearly. +#define AREA_FLAG_EXTERNAL BITFLAG(2) // External as in exposed to space, not outside in a nice, green, forest. +#define AREA_FLAG_ION_SHIELDED BITFLAG(3) // Shielded from ionospheric anomalies. +#define AREA_FLAG_NO_LEGACY_PERSISTENCE BITFLAG(4) // SSpersistence will not track values from this area. +#define AREA_FLAG_IS_BACKGROUND BITFLAG(5) // Blueprints can create areas on top of these areas. Cannot edit the name of or delete these areas. +#define AREA_FLAG_MAINTENANCE BITFLAG(6) // Area is a maintenance area. +#define AREA_FLAG_SHUTTLE BITFLAG(7) // Area is a shuttle area. +#define AREA_FLAG_HALLWAY BITFLAG(8) // Area is a public hallway suitable for event selection +#define AREA_FLAG_PRISON BITFLAG(9) // Area is a prison for the purposes of brigging objectives. +#define AREA_FLAG_HOLY BITFLAG(10) // Area is holy for the purposes of marking turfs as cult-resistant. +#define AREA_FLAG_SECURITY BITFLAG(11) // Area is security for the purposes of newscaster init. +#define AREA_FLAG_HIDE_FROM_HOLOMAP BITFLAG(12) // if we shouldn't be drawn on station holomaps +#define AREA_FLAG_ALLOW_LEVEL_PERSISTENCE BITFLAG(13) // Whether or not this area should pass changed turfs to SSpersistence. //Map template flags -#define TEMPLATE_FLAG_ALLOW_DUPLICATES BITFLAG(0) // Lets multiple copies of the template to be spawned -#define TEMPLATE_FLAG_SPAWN_GUARANTEED BITFLAG(1) // Makes it ignore away site budget and just spawn (only for away sites) -#define TEMPLATE_FLAG_CLEAR_CONTENTS BITFLAG(2) // if it should destroy objects it spawns on top of -#define TEMPLATE_FLAG_NO_RUINS BITFLAG(3) // if it should forbid ruins from spawning on top of it -#define TEMPLATE_FLAG_NO_RADS BITFLAG(4) // Removes all radiation from the template after spawning. -#define TEMPLATE_FLAG_TEST_DUPLICATES BITFLAG(5) // Makes unit testing attempt to spawn mutliple copies of this template. Assumes unit testing is spawning at least one copy. -#define TEMPLATE_FLAG_GENERIC_REPEATABLE BITFLAG(6) // Template can be picked repeatedly for the same level gen run. +#define TEMPLATE_FLAG_ALLOW_DUPLICATES BITFLAG(0) // Lets multiple copies of the template to be spawned +#define TEMPLATE_FLAG_SPAWN_GUARANTEED BITFLAG(1) // Makes it ignore away site budget and just spawn (only for away sites) +#define TEMPLATE_FLAG_CLEAR_CONTENTS BITFLAG(2) // if it should destroy objects it spawns on top of +#define TEMPLATE_FLAG_NO_RUINS BITFLAG(3) // if it should forbid ruins from spawning on top of it +#define TEMPLATE_FLAG_NO_RADS BITFLAG(4) // Removes all radiation from the template after spawning. +#define TEMPLATE_FLAG_TEST_DUPLICATES BITFLAG(5) // Makes unit testing attempt to spawn mutliple copies of this template. Assumes unit testing is spawning at least one copy. +#define TEMPLATE_FLAG_GENERIC_REPEATABLE BITFLAG(6) // Template can be picked repeatedly for the same level gen run. // Convoluted setup so defines can be supplied by Bay12 main server compile script. // Should still work fine for people jamming the icons into their repo. diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 75c9ac6ef8ce..77b1dae70dd5 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -160,21 +160,6 @@ // Robo Organs. #define BP_VOICE "vocal synthesiser" -//Augmetations -#define BP_AUGMENT_R_ARM "right arm augment" -#define BP_AUGMENT_L_ARM "left arm augment" -#define BP_AUGMENT_R_HAND "right hand augment" -#define BP_AUGMENT_L_HAND "left hand augment" -#define BP_AUGMENT_R_LEG "right leg augment" -#define BP_AUGMENT_L_LEG "left leg augment" -#define BP_AUGMENT_CHEST_ARMOUR "chest armor augment" -#define BP_AUGMENT_CHEST_ACTIVE "active chest augment" -#define BP_AUGMENT_HEAD "head augment" - -//Augment flags -#define AUGMENTATION_MECHANIC 1 -#define AUGMENTATION_ORGANIC 2 - // Prosthetic helpers. #define BP_IS_PROSTHETIC(org) (!QDELETED(org) && (org.organ_properties & ORGAN_PROP_PROSTHETIC)) #define BP_IS_ROBOTIC(org) (!QDELETED(org) && (org.bodytype?.is_robotic)) diff --git a/code/__defines/persistence.dm b/code/__defines/persistence.dm new file mode 100644 index 000000000000..bbd7ffbbf0c0 --- /dev/null +++ b/code/__defines/persistence.dm @@ -0,0 +1,5 @@ +// Handled elsewhere, do not let them load like vars. +var/global/list/_forbid_field_load = list( + (nameof(/datum::type)) = TRUE, + (nameof(/atom::loc)) = TRUE +) diff --git a/code/__defines/serde.dm b/code/__defines/serde.dm new file mode 100644 index 000000000000..a1e84e3fe30c --- /dev/null +++ b/code/__defines/serde.dm @@ -0,0 +1,64 @@ +#define SERDE_HINT_FINISHED 1 +#define SERDE_HINT_POSTINIT 2 + +#define SERDE_REAGENT_LIST "_reagent_list" +#define SERDE_REAGENT_VOLUME "_reagent_volume" + +#define SERIALIZE_VALUE(V, T, VAL) .[nameof(T::V)] = VAL; +#define SERIALIZE(V, T) SERIALIZE_VALUE(V, T, V) +#define SERIALIZE_IF_MODIFIED(V, T) if(V != initial(V)) { SERIALIZE_VALUE(V, T, V) } +#define SERIALIZE_TYPE_IF_MODIFIED(V, T) if(V != initial(V)) { SERIALIZE_VALUE(V, T, "[V]") } +#define SERIALIZE_DECL_IF_MODIFIED(V, T) if((isnull(V) && !isnull(initial(V))) || ((istext(V) || istype(V, /decl) || ispath(V, /decl)) && !DECLS_ARE_EQUIVALENT(V, initial(V)))) { var/decl/__D = RESOLVE_TO_DECL(V); SERIALIZE_VALUE(V, T, __D?.uid) } +#define SERIALIZE_DECL_LIST(V, T) if(islist(V)) { var/list/__decl_uids = list(); for(var/decl/__decl in V) { __decl_uids += __decl.uid }; SERIALIZE_VALUE(V, T, __decl_uids) } +#define SERIALIZE_REAGENTS(V, T, I) if(istype(V, /datum/reagents)) { \ + .[I + SERDE_REAGENT_VOLUME] = UNLINT(V.maximum_volume); \ + if(UNLINT(V.total_volume)) { \ + var/list/__compiled_reagents = list(); \ + for(var/decl/material/R in UNLINT(V.liquid_volumes)) { \ + __compiled_reagents[++__compiled_reagents.len] = list(R.uid, UNLINT(V.liquid_volumes[R]), (MAT_PHASE_LIQUID)); \ + } \ + for(var/decl/material/R in UNLINT(V.solid_volumes)) { \ + __compiled_reagents[++__compiled_reagents.len] = list(R.uid, UNLINT(V.solid_volumes[R]), (MAT_PHASE_SOLID)); \ + } \ + .[I + SERDE_REAGENT_LIST] = __compiled_reagents; \ + } else { \ + .[I + SERDE_REAGENT_LIST] = list(); \ + } \ +} else { \ + .[I + SERDE_REAGENT_LIST] = list(); \ + .[I + SERDE_REAGENT_VOLUME] = 0; \ +} + +#define DESERIALIZE_REAGENTS(V, I) if(((I + SERDE_REAGENT_LIST) in __deserialization_payload) && ((I + SERDE_REAGENT_VOLUME) in __deserialization_payload)) { \ + V = list((SERDE_REAGENT_VOLUME) = __deserialization_payload[I + SERDE_REAGENT_VOLUME], (SERDE_REAGENT_LIST) = __deserialization_payload[I + SERDE_REAGENT_LIST]); \ +} + +#define DESERIALIZE_DECL_TO_TYPE(V) if(istext(V) || ispath(V, /decl) || istype(V, /decl)) { var/decl/__D = RESOLVE_TO_DECL(V); V = __D?.type; } else { V = null; } +#define DESERIALIZE_TYPE(V) if(istext(V)) { V = text2path(V); } else if(!ispath(V)) { V = null; } +#define DESERIALIZE_DECL(V) if(istext(V) || ispath(V)) { V = RESOLVE_TO_DECL(V); } else { V = null; } + +// List cast is to avoid OpenDream complaining about V typically being typed as a reagents datum, but holding a list for serde. +#define FINALIZE_REAGENTS_SERDE_BODY(V) try { \ + if((SERDE_REAGENT_LIST in V) && (SERDE_REAGENT_VOLUME in V)) { \ + var/list/LV = V; \ + var/__serde_volume = LV[SERDE_REAGENT_VOLUME]; \ + if(__serde_volume <= 0) { \ + V = null; \ + } else { \ + var/list/__serde_reagents = LV[SERDE_REAGENT_LIST]; \ + V = new /datum/reagents(__serde_volume, src); \ + for(var/list/entry in __serde_reagents) { \ + V.add_reagent(RESOLVE_TO_DECL(entry[1]), entry[2], phase = entry[3], defer_update = TRUE); \ + } \ + V.handle_update(); \ + } \ + } else { \ + V = null; \ + } \ +} catch(var/exception/E) { \ + log_error("Exception while finalizing reagents load for [type]: [EXCEPTION_TEXT(E)]"); \ + V = null; \ +} + +#define FINALIZE_REAGENTS_SERDE(V) if(islist(V)) { FINALIZE_REAGENTS_SERDE_BODY(V); } +#define FINALIZE_REAGENTS_SERDE_AND_RETURN(V) if(islist(V)) { FINALIZE_REAGENTS_SERDE_BODY(V); return; } diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index 127f3b2d1149..87f4b099d0f5 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -19,12 +19,13 @@ // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. -#define SS_INIT_INPUT 22 -#define SS_INIT_EARLY 21 -#define SS_INIT_WEBHOOKS 20 -#define SS_INIT_MODPACKS 19 -#define SS_INIT_SECRETS 18 -#define SS_INIT_GARBAGE 17 +#define SS_INIT_INPUT 23 +#define SS_INIT_EARLY 22 +#define SS_INIT_WEBHOOKS 21 +#define SS_INIT_MODPACKS 20 +#define SS_INIT_SECRETS 19 +#define SS_INIT_GARBAGE 18 +#define SS_INIT_SERDE 17 #define SS_INIT_MATERIALS 16 #define SS_INIT_PLANTS 15 #define SS_INIT_LORE 14 diff --git a/code/_global_vars/lists/clothing.dm b/code/_global_vars/lists/clothing.dm index a819fa7c527b..c77cb035a88b 100644 --- a/code/_global_vars/lists/clothing.dm +++ b/code/_global_vars/lists/clothing.dm @@ -30,6 +30,7 @@ var/global/list/airtight_slots = list( var/global/list/abstract_inventory_slots = list( slot_in_backpack_str, + slot_in_wallet_str, slot_undershirt_str, slot_underpants_str, slot_socks_str @@ -40,7 +41,7 @@ var/global/list/vitals_sensor_equip_slots = list( ) var/global/list/headphone_slots = list( - slot_l_ear_str, - slot_r_ear_str, + slot_l_ear_str, + slot_r_ear_str, slot_head_str ) diff --git a/code/_global_vars/lists/flavor.dm b/code/_global_vars/lists/flavor.dm index 80c11924a919..7a21627335d2 100644 --- a/code/_global_vars/lists/flavor.dm +++ b/code/_global_vars/lists/flavor.dm @@ -118,6 +118,7 @@ GLOBAL_GETTER(cable_colors, /list, SetupCableColors()) . = list() var/list/valid_cable_coils = typesof(/obj/item/stack/cable_coil) - typesof( + /obj/item/stack/cable_coil/five, /obj/item/stack/cable_coil/single, /obj/item/stack/cable_coil/cut, /obj/item/stack/cable_coil/cyborg, diff --git a/code/_global_vars/lists/names.dm b/code/_global_vars/lists/names.dm index 78cd73fe25b0..741c433285d7 100644 --- a/code/_global_vars/lists/names.dm +++ b/code/_global_vars/lists/names.dm @@ -6,5 +6,6 @@ var/global/list/verbs = file2list("config/names/verbs.txt") var/global/list/adjectives = file2list("config/names/adjectives.txt") var/global/list/abstract_slot_names = list( - slot_in_backpack_str = "In Backpack" + slot_in_backpack_str = "In Backpack", + slot_in_wallet_str = "In Wallet" ) diff --git a/code/_global_vars/sound.dm b/code/_global_vars/sound.dm index bab80ae12bee..fec9867b5f09 100644 --- a/code/_global_vars/sound.dm +++ b/code/_global_vars/sound.dm @@ -128,4 +128,11 @@ var/global/list/sweeping_sound = list( 'sound/foley/sweeping5.ogg', 'sound/foley/sweeping6.ogg', 'sound/foley/sweeping7.ogg', -) \ No newline at end of file +) + +var/global/list/ricochet_sound = list( + 'sound/weapons/guns/ricochet1.ogg', + 'sound/weapons/guns/ricochet2.ogg', + 'sound/weapons/guns/ricochet3.ogg', + 'sound/weapons/guns/ricochet4.ogg' +) diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index 942d9d9807f7..e94e11f5d01c 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -179,7 +179,7 @@ var/global/log_end= world.system_type == UNIX ? ascii2text(13) : "" return "[..()] ([isnum(z) ? "[x],[y],[z]" : "0,0,0"])" /turf/get_log_info_line() - var/obj/effect/overmap/visitable/O = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/O = global.overmap_sectors[z] if(istype(O)) return "[..()] ([x],[y],[z] - [O.name]) ([loc ? loc.type : "NULL"])" else @@ -188,7 +188,7 @@ var/global/log_end= world.system_type == UNIX ? ascii2text(13) : "" /atom/movable/get_log_info_line() var/turf/t = get_turf(src) if(t) - var/obj/effect/overmap/visitable/O = global.overmap_sectors[num2text(t.z)] + var/obj/effect/overmap/visitable/O = global.overmap_sectors[t.z] if(istype(O)) return "[..()] ([t]) ([t.x],[t.y],[t.z] - [O.name]) ([t.type])" return "[..()] ([t]) ([t.x],[t.y],[t.z]) ([t.type])" diff --git a/code/_helpers/overmap.dm b/code/_helpers/overmap.dm index 296a04974a03..abac165dce80 100644 --- a/code/_helpers/overmap.dm +++ b/code/_helpers/overmap.dm @@ -1,4 +1,4 @@ -var/global/list/overmap_sectors = list() +var/global/alist/overmap_sectors = alist() var/global/list/overmaps_by_name = list() var/global/list/overmaps_by_z = list() diff --git a/code/_helpers/serde.dm b/code/_helpers/serde.dm new file mode 100644 index 000000000000..9d50f3fae347 --- /dev/null +++ b/code/_helpers/serde.dm @@ -0,0 +1,131 @@ +/proc/instantiate_serialized_data(load_z, requestor, list/instance_map, entries_decay_at, entry_decay_weight) + + var/list/nested_instances = list() + var/list/instanced_areas = list() + var/list/created_data = list() + + LAZYINITLIST(instance_map) + + to_world_log("Finalising load of [length(instance_map)] instance\s for level '[requestor]'.") + for(var/uid in instance_map) + + var/list/instance_data = instance_map[uid] + try + + var/raw_load_path = instance_data[nameof(/datum::type)] + var/load_path = ispath(raw_load_path, /datum) ? raw_load_path : text2path(raw_load_path) + if(!ispath(load_path, /datum)) + error("[requestor]: attempted to load persistent instance with invalid or non-/datum type '[raw_load_path]'") + continue + + var/datum/created_instance + + // Instance is a /datum. + // Just pass the data in and assume the datum type knows what to do with it. + if(!ispath(load_path, /atom) && ispath(load_path, /datum)) + created_instance = new load_path(instance_data) + created_data += created_instance + else + var/list/spawn_data = instance_data[nameof(/atom/movable::loc)] + if(spawn_data) + + if(isnull(spawn_data) || length(spawn_data) < 3) + error("[requestor]: attempted to load persistent instance with malformed loc.") + continue + + // Instance has a world coordinate. + if(islist(spawn_data)) + var/turf/spawn_loc = locate(spawn_data[1], spawn_data[2], isnull(load_z) ? spawn_data[3] : load_z) + if(!istype(spawn_loc)) + error("[requestor]: attempted to load persistent instance but could not find spawn loc.") + continue + if(ispath(load_path, /turf)) + if(spawn_loc.type == load_path) + created_instance = spawn_loc + else + created_instance = spawn_loc.ChangeTurf(load_path) + + // TODO: Areas will need bespoke handling for non-subtype-related persistence (blueprint renaming etc). + else if(ispath(load_path, /area)) + var/area/area = instanced_areas[load_path] + if(!area) + area = new load_path(null) + instanced_areas[load_path] = area + ChangeArea(spawn_loc, area) + + else if(ispath(load_path, /atom)) + created_instance = new load_path(spawn_loc) + spawn_loc._contents_were_modified = TRUE // ensure + else + error("[requestor]: attempted to instantiate unimplemented path '[load_path]'.") + continue + + // Instance is inside another instance; implies/requires /atom/movable + else if(istext(spawn_data)) + if(!ispath(load_path, /atom/movable)) + error("[requestor]: tried to spawn non-movable [load_path] inside an instance.") + continue + created_instance = new load_path + nested_instances[created_instance] = spawn_data + + else + error("[requestor]: attempted to load persistent instance with malformed loc.") + continue + + else + // Should we just go ahead and do this to create atoms in nullspace? + // Would we ever want to track an atom in nullspace via level persistence? + error("[requestor]: attempted to load non-/datum persistent instance with no spawn loc.") + + if(istype(created_instance)) + LAZYSET(., uid, created_instance) + if(isatom(created_instance)) + var/atom/atom = created_instance + atom.__deserialization_payload = instance_data + SSatoms.deserialized_atoms[uid] = atom + if(!isnull(entries_decay_at) && !isnull(entry_decay_weight)) + created_instance.HandlePersistentDecay(entries_decay_at, entry_decay_weight) + + catch(var/exception/E) + log_error("Exception during persistent instance load - [islist(instance_data) ? json_encode(instance_data) : "no instance data"]: [EXCEPTION_TEXT(E)]") + + // Atoms use SSatoms for this, datums don't go through SSatoms so need to do it here. + for(var/datum/instance in created_data) + instance.DeserializePostInit(.) + + // Resolve any loc references to instances. + for(var/atom/movable/atom as anything in nested_instances) + var/nested_atom_id = nested_instances[atom] + var/atom/nested_atom = .[nested_atom_id] + if(!istype(nested_atom)) + error("[requestor]: could not resolve instance ref [nested_atom_id] to instance.") + continue + atom.forceMove(nested_atom) + nested_atom.contents_were_modified() + + // Now that everything is loaded and placed, clear out anything that should not be present on the turfs we've loaded. + for(var/uid in SSatoms.deserialized_atoms) + var/turf/turf = SSatoms.deserialized_atoms[uid] + if(!istype(turf)) + continue + for(var/atom/thing in turf) + if(!thing.simulated) + continue + if(!isnull(thing.__deserialization_payload)) + continue + qdel(thing) + + to_world_log("[requestor] loaded [length(.)] persistent instance\s.") + +/proc/apply_serde_message_decay(_message, _age, _decay_weight, _decay_at) + var/static/list/decayed_chars = list(".",",","-","'","\\","/","\"",":",";") + if(_age < _decay_at || isnull(_message)) + return _message + . = "" + for(var/i = 1 to length(_message)) + var/char = copytext(_message, i, i + 1) + if(prob(round(_age * _decay_weight))) + if(prob(99)) + . += pick(decayed_chars) + else + . += char diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm index 90210911f1ef..14f373cda705 100644 --- a/code/_helpers/time.dm +++ b/code/_helpers/time.dm @@ -156,7 +156,12 @@ var/global/rollovercheck_last_timeofday = 0 if (!initial_delay) initial_delay = world.tick_lag - +// Unit tests are not the normal environemnt. The mc can get absolutely thigh crushed, and sleeping procs running for ages is much more common +// We don't want spurious hard deletes off this, so let's only sleep for the requested period of time here yeah? +#ifdef UNIT_TEST + sleep(initial_delay) + return NONUNIT_CEILING(DS2TICKS(initial_delay), 1) +#else . = 0 var/i = DS2TICKS(initial_delay) do @@ -164,6 +169,7 @@ var/global/rollovercheck_last_timeofday = 0 sleep(i * world.tick_lag * DELTA_CALC) i *= 2 while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit)) +#endif #undef DELTA_CALC diff --git a/code/_helpers/type2type.dm b/code/_helpers/type2type.dm index d5178a090939..ed43cf149ee3 100644 --- a/code/_helpers/type2type.dm +++ b/code/_helpers/type2type.dm @@ -7,9 +7,12 @@ * angle2text */ +// This proc does not support converting numerically indexed alists to assoc lists. /proc/alist2list(alist/input) . = list() for(var/k,v in input) + if(isnum(k)) + CRASH("Numeric index passed to alist2list()!") .[k] = v // Splits the text of a file at seperator and returns them in a list. diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index 51398a81da00..727108ce080a 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -52,7 +52,6 @@ #define ui_movi "RIGHT-2:24,BOTTOM:5" #define ui_attack_selector "RIGHT-2:27,BOTTOM+2:9" #define ui_zonesel "RIGHT-1:28,BOTTOM:5" -#define ui_stamina "RIGHT-2:24,BOTTOM:8" #define ui_borg_module "RIGHT-1:28,BOTTOM+1:7" diff --git a/code/_onclick/hud/action.dm b/code/_onclick/hud/action.dm index e6599e5ac2b7..70cc620cb206 100644 --- a/code/_onclick/hud/action.dm +++ b/code/_onclick/hud/action.dm @@ -162,9 +162,6 @@ if(istype(O)) O.refresh_action_button() -/datum/action/item_action/organ/augment - button_icon = 'icons/obj/augment.dmi' - #undef AB_WEST_OFFSET #undef AB_NORTH_OFFSET #undef AB_MAX_COLUMNS \ No newline at end of file diff --git a/code/_onclick/hud/hud_types/_hud.dm b/code/_onclick/hud/hud_types/_hud.dm index 9e65b258399d..5a64f04f09a9 100644 --- a/code/_onclick/hud/hud_types/_hud.dm +++ b/code/_onclick/hud/hud_types/_hud.dm @@ -92,6 +92,9 @@ var/action_buttons_hidden = FALSE var/obj/screen/action_button/hide_toggle/hide_actions_toggle + var/const/HAND_UI_PER_ROW = 4 + var/const/HAND_UI_INITIAL_Y_OFFSET = 21 + // TODO: declify these. VAR_PROTECTED/gun_mode_toggle_type VAR_PRIVATE/obj/screen/gun/mode/gun_mode_toggle @@ -351,11 +354,10 @@ qdel(inv_box) // Rebuild offsets for the hand elements. - var/const/elems_per_row = 4 - var/hand_y_offset = 21 + var/hand_y_offset = HAND_UI_INITIAL_Y_OFFSET var/list/elements = hud_elements_hands?.Copy() while(length(elements)) - var/copy_index = min(length(elements), elems_per_row)+1 + var/copy_index = min(length(elements), HAND_UI_PER_ROW)+1 var/list/sublist = elements.Copy(1, copy_index) elements.Cut(1, copy_index) var/hand_x_offset = (world.icon_size/2) * (1 - length(sublist)) @@ -389,6 +391,7 @@ if(mymob.client) mymob.client.screen |= swap_elem + refresh_element(HUD_STAMINA) update_hand_elements() return TRUE diff --git a/code/_onclick/hud/screen/screen_stamina.dm b/code/_onclick/hud/screen/screen_stamina.dm index 5d4f34b32da9..851fdf1d8682 100644 --- a/code/_onclick/hud/screen/screen_stamina.dm +++ b/code/_onclick/hud/screen/screen_stamina.dm @@ -1,24 +1,41 @@ /obj/screen/stamina name = "stamina" - icon = 'icons/effects/progressbar.dmi' - icon_state = "prog_bar_100" + icon = 'icons/effects/staminabar.dmi' + icon_state = "bar" invisibility = INVISIBILITY_MAXIMUM - screen_loc = ui_stamina use_supplied_ui_color = FALSE use_supplied_ui_alpha = FALSE use_supplied_ui_icon = FALSE requires_ui_style = FALSE layer = HUD_BASE_LAYER + 0.1 // needs to layer over the movement intent element + var/const/STAMINA_STATE_PERIOD = 5 + +/obj/screen/stamina/Initialize(mapload, mob/_owner, decl/ui_style/ui_style, ui_color, ui_alpha, ui_cat) + . = ..() + update_icon() /obj/screen/stamina/on_update_icon() . = ..() var/mob/living/owner = owner_ref?.resolve() if(!istype(owner)) set_invisibility(INVISIBILITY_MAXIMUM) + return + + var/hand_row_offset = /datum/hud::HAND_UI_INITIAL_Y_OFFSET + (ceil(length(owner.get_held_item_slots()) / /datum/hud::HAND_UI_PER_ROW) * world.icon_size) + 16 + screen_loc = "CENTER:-32,BOTTOM:[hand_row_offset]" + + var/stamina = owner.get_stamina() + cut_overlays() + if(stamina < 100) + set_invisibility(INVISIBILITY_NONE) + var/stamina_amt = floor(stamina/STAMINA_STATE_PERIOD)*STAMINA_STATE_PERIOD + var/bar_overlay_state = "bar_[stamina_amt]" + if(stamina_amt > 0 && stamina <= 25) + bar_overlay_state = "[bar_overlay_state]_fail" + var/image/bar_overlay = image(icon = icon, icon_state = bar_overlay_state) + bar_overlay.appearance_flags |= RESET_COLOR + bar_overlay.color = COLOR_WHITE + add_overlay(bar_overlay) else - var/stamina = owner.get_stamina() - if(stamina < 100) - set_invisibility(INVISIBILITY_NONE) - icon_state = "prog_bar_[floor(stamina/5)*5][(stamina >= 5) && (stamina <= 25) ? "_fail" : null]" - else - set_invisibility(INVISIBILITY_MAXIMUM) + set_invisibility(INVISIBILITY_MAXIMUM) + compile_overlays() \ No newline at end of file diff --git a/code/controllers/evacuation/evacuation.dm b/code/controllers/evacuation/evacuation.dm index 90849e6e1935..b1d41c05376b 100644 --- a/code/controllers/evacuation/evacuation.dm +++ b/code/controllers/evacuation/evacuation.dm @@ -91,10 +91,8 @@ A.readyalert() if(!skip_announce) global.using_map.emergency_shuttle_called_announcement() - else - if(!skip_announce) - priority_announcement.Announce(replacetext(replacetext(global.using_map.shuttle_called_message, "%dock_name%", "[global.using_map.dock_name]"), "%ETA%", "[round(get_eta()/60)] minute\s")) - + else if(!skip_announce && global.using_map.shuttle_called_message) + priority_announcement.Announce(replacetext(replacetext(global.using_map.shuttle_called_message, "%dock_name%", "[global.using_map.dock_name]"), "%ETA%", "[round(get_eta()/60)] minute\s")) return 1 /datum/evacuation_controller/proc/cancel_evacuation() @@ -113,12 +111,13 @@ auto_recall_time = null if(emergency_evacuation) - evac_recalled.Announce(global.using_map.emergency_shuttle_recall_message) + if(global.using_map.emergency_shuttle_recall_message) + evac_recalled.Announce(global.using_map.emergency_shuttle_recall_message) for(var/area/A in global.areas) if(istype(A) && (A.area_flags & AREA_FLAG_HALLWAY)) A.readyreset() emergency_evacuation = 0 - else + else if(global.using_map.emergency_shuttle_recall_message) priority_announcement.Announce(global.using_map.shuttle_recall_message) return 1 diff --git a/code/controllers/subsystems/atoms.dm b/code/controllers/subsystems/atoms.dm index baf065bd0fd3..68e2940bcb41 100644 --- a/code/controllers/subsystems/atoms.dm +++ b/code/controllers/subsystems/atoms.dm @@ -11,6 +11,8 @@ SUBSYSTEM_DEF(atoms) var/atom_init_stage = INITIALIZATION_INSSATOMS var/old_init_stage + /// An associative list of UIDs to atoms that were deserialized prior to flush. + var/list/deserialized_atoms = list() /// A non-associative list of lists, with the format list(list(atom, list(Initialize arguments))). var/list/created_atoms = list() /// A non-associative list of lists, with the format list(list(atom, list(LateInitialize arguments))). @@ -29,9 +31,20 @@ SUBSYSTEM_DEF(atoms) atom_init_stage = INITIALIZATION_INNEW_MAPLOAD - var/list/mapload_arg = list(TRUE) - + // Preload any atoms that have deserialized during the initial load process prior to flush. var/index = 1 + var/list/postinit_serde_atoms = list() + if(length(deserialized_atoms)) + while(index <= length(deserialized_atoms)) + var/uid = deserialized_atoms[index++] + var/atom/instance = deserialized_atoms[uid] + if(instance.Preload(deserialized_atoms) == SERDE_HINT_POSTINIT) + postinit_serde_atoms += instance + CHECK_TICK + report_progress("Deserialized [index-1] atom\s.") + index = 1 + + var/list/mapload_arg = list(TRUE) // Things can add to the end of this list while we iterate, so we can't use a for loop. while(index <= length(created_atoms)) // Don't remove from this list while we run, that's expensive. @@ -49,10 +62,10 @@ SUBSYSTEM_DEF(atoms) else InitAtom(A, mapload_arg) CHECK_TICK - - report_progress("Initialized [index] atom\s") created_atoms.Cut() + report_progress("Initialized [index-1] atom\s.") + atom_init_stage = INITIALIZATION_INNEW_REGULAR if(length(late_loaders)) @@ -65,6 +78,25 @@ SUBSYSTEM_DEF(atoms) report_progress("Late initialized [index] atom\s") late_loaders.Cut() + if(length(postinit_serde_atoms)) + index = 1 + while(index <= length(postinit_serde_atoms)) + var/atom/instance = postinit_serde_atoms[index++] + instance.DeserializePostInit(deserialized_atoms) + CHECK_TICK + postinit_serde_atoms.Cut() + + // Clear out the serde payloads now that everything should be tidied away. + if(length(deserialized_atoms)) + index = 1 + while(index <= length(deserialized_atoms)) + var/uid = deserialized_atoms[index++] + var/atom/instance = deserialized_atoms[uid] + if(istype(instance)) + instance.__deserialization_payload = null + CHECK_TICK + deserialized_atoms.Cut() + /datum/controller/subsystem/atoms/proc/InitAtom(atom/A, list/arguments) var/the_type = A.type if(QDELING(A)) diff --git a/code/controllers/subsystems/fluids.dm b/code/controllers/subsystems/fluids.dm index b05b4ed11f8a..e0248091c4ea 100644 --- a/code/controllers/subsystems/fluids.dm +++ b/code/controllers/subsystems/fluids.dm @@ -71,7 +71,10 @@ SUBSYSTEM_DEF(fluids) continue checked_targets[neighbor] = TRUE flooded_a_neighbor = TRUE - neighbor.add_to_reagents(current_fluid_holder.flooded, FLUID_MAX_DEPTH) + if(current_fluid_holder.contaminant_reagent_type && current_fluid_holder.contaminant_proportion) + neighbor.add_to_reagents_contaminated(current_fluid_holder.flooded, FLUID_MAX_DEPTH, contaminant_type = current_fluid_holder.contaminant_reagent_type, contaminant_proportion = current_fluid_holder.contaminant_proportion) + else + neighbor.add_to_reagents(current_fluid_holder.flooded, FLUID_MAX_DEPTH) if(!flooded_a_neighbor) REMOVE_ACTIVE_FLUID_SOURCE(current_fluid_holder) diff --git a/code/controllers/subsystems/initialization/persistence.dm b/code/controllers/subsystems/initialization/persistence.dm deleted file mode 100644 index c1119f8ca859..000000000000 --- a/code/controllers/subsystems/initialization/persistence.dm +++ /dev/null @@ -1,78 +0,0 @@ -SUBSYSTEM_DEF(persistence) - name = "Persistence" - init_order = SS_INIT_MISC_LATE - flags = SS_NO_FIRE | SS_NEEDS_SHUTDOWN - - var/elevator_fall_path = "data/elevator_falls_tracking.txt" - var/elevator_fall_shifts = -1 // This is snowflake, but oh well. - var/list/tracking_values = list() - -/datum/controller/subsystem/persistence/Initialize() - . = ..() - - decls_repository.get_decls_of_subtype(/decl/persistence_handler) // Initialize()s persistence categories. - - // Begin snowflake. - var/elevator_file = safe_file2text(elevator_fall_path, FALSE) - if(elevator_file) - elevator_fall_shifts = text2num(elevator_file) - else - elevator_fall_shifts = initial(elevator_fall_shifts) - if(isnull(elevator_fall_shifts)) - elevator_fall_shifts = initial(elevator_fall_shifts) - elevator_fall_shifts++ - // End snowflake. - -/datum/controller/subsystem/persistence/Shutdown() - var/list/all_persistence_datums = decls_repository.get_decls_of_subtype(/decl/persistence_handler) - for(var/thing in all_persistence_datums) - var/decl/persistence_handler/P = all_persistence_datums[thing] - P.Shutdown() - - // Refer to snowflake above. - if(fexists(elevator_fall_path)) - fdel(elevator_fall_path) - text2file("[elevator_fall_shifts]", elevator_fall_path) - -/datum/controller/subsystem/persistence/proc/track_value(var/atom/value, var/track_type) - - var/turf/T = get_turf(value) - if(!T) - return - - var/area/A = get_area(T) - if(!A || (A.area_flags & AREA_FLAG_IS_NOT_PERSISTENT)) - return - - var/datum/level_data/level = SSmapping.levels_by_z[T.z] - if(!istype(level) || !level.permit_persistence) - return - - if(!tracking_values[track_type]) - tracking_values[track_type] = list() - tracking_values[track_type] |= value - -/datum/controller/subsystem/persistence/proc/is_tracking(var/atom/value, var/track_type) - . = (value in tracking_values[track_type]) - -/datum/controller/subsystem/persistence/proc/forget_value(var/atom/value, var/track_type) - if(tracking_values[track_type]) - tracking_values[track_type] -= value - -/datum/controller/subsystem/persistence/proc/show_info(var/mob/user) - - if(!check_rights(R_INVESTIGATE, C = user)) - return - - var/list/dat = list("") - var/can_modify = check_rights(R_ADMIN, 0, user) - var/list/all_persistence_datums = decls_repository.get_decls_of_subtype(/decl/persistence_handler) - for(var/thing in all_persistence_datums) - var/decl/persistence_handler/P = all_persistence_datums[thing] - if(P.has_admin_data) - dat += P.GetAdminSummary(user, can_modify) - dat += "
" - - var/datum/browser/popup = new(user, "admin_persistence", "Persistence Data") - popup.set_content(jointext(dat, null)) - popup.open() diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index a166bb41242d..886d58c7a9c1 100644 --- a/code/controllers/subsystems/jobs.dm +++ b/code/controllers/subsystems/jobs.dm @@ -425,7 +425,7 @@ SUBSYSTEM_DEF(jobs) for(var/required in allowed_skills) if(!wearer.skill_check(required, allowed_skills[required])) return FALSE - if(whitelisted && (!(wearer.get_species()?.name in whitelisted))) + if(whitelisted && (!(wearer.get_species()?.uid in whitelisted))) return FALSE return TRUE @@ -442,7 +442,7 @@ SUBSYSTEM_DEF(jobs) if(!istype(gear)) continue if(!gear.is_permitted(H, job)) - to_chat(H, SPAN_WARNING("Your current species, job, branch, skills or whitelist status does not permit you to spawn with [thing]!")) + to_chat(H, SPAN_WARNING("Your current species, job, branch, skills or whitelist status does not permit you to spawn with [gear.name]!")) continue if(!gear.slot || !gear.spawn_on_mob(H, H.client.prefs.Gear()[gear.uid])) spawn_in_storage.Add(gear) diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index bcf8a6922368..a03221cd513b 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -149,10 +149,31 @@ SUBSYSTEM_DEF(mapping) setup_data_for_levels(min_z = old_maxz + 1) + // Now that levels are in place, preload any associated persistent data. + // This is to avoid dependencies on other atoms or any other weird ordering + // problems like we used to get with old DMMS and SSatoms. + var/list/preloaded_levels = list() + for(var/datum/level_data/level in levels_by_z) + if(level.preload_persistent_data()) + preloaded_levels += level + + // Now actually load the serde data into the map. + for(var/datum/level_data/level as anything in preloaded_levels) + level.load_persistent_data() + + // Clear our reference data for GC + // This might not be needed but it saves refs floating around I guess. + for(var/key in level_persistence_ref_map) + var/list/stale_data = global.level_persistence_ref_map[key] + stale_data.Cut() + + global.level_persistence_ref_map.Cut() + // Generate turbolifts last, since away sites may have elevators to generate too. for(var/obj/abstract/turbolift_spawner/turbolift as anything in turbolifts_to_initialize) turbolift.build_turbolift() + // With levels set up and serde complete (and levels flagged) we can do any remaining level generation. global.using_map.finalize_map_generation() . = ..() diff --git a/code/controllers/subsystems/persistence.dm b/code/controllers/subsystems/persistence.dm new file mode 100644 index 000000000000..79a994a2da04 --- /dev/null +++ b/code/controllers/subsystems/persistence.dm @@ -0,0 +1,153 @@ +/datum/admins/proc/force_persistence_save_verb() + set name = "Force Early Level Save" + set category = "Admin" + set desc = "Forces an early level save run by SSpersistence." + if(!SSpersistence) + return + if(UNLINT(SSpersistence._persistent_save_running)) + to_chat(usr, SPAN_WARNING("There is already a level save running. Please wait for it to finish.")) + return + log_admin("[key_name(usr)] has started an early level save.") + message_admins("[key_name(usr)] has started an early level save.") + SSpersistence.start_persistent_level_save() + +SUBSYSTEM_DEF(persistence) + name = "Persistence" + init_order = SS_INIT_SERDE + flags = SS_NEEDS_SHUTDOWN + wait = 60 MINUTES + + VAR_PRIVATE/const/ELEVATOR_FALL_PATH = "data/elevator_falls_tracking.txt" + var/elevator_fall_shifts = -1 // This is snowflake, but oh well. + var/list/tracking_values = list() + VAR_PRIVATE/_persistent_save_running = FALSE + var/const/save_warning_period = 30 SECONDS // How long to warn about an upcoming world save so people can get to safety, etc + var/initial_save_skip_period // Set in Initialize() + var/showing_warning = FALSE + +/datum/controller/subsystem/persistence/Initialize() + . = ..() + initial_save_skip_period = max(0, (wait - 10 MINUTES)) // Skip initial fire(), typically there's no need to save immediately after roundstart + decls_repository.get_decls_of_subtype(/decl/persistence_handler) // Initialize()s persistence categories. + + // Begin snowflake. + var/elevator_file = safe_file2text(ELEVATOR_FALL_PATH, FALSE) + if(elevator_file) + elevator_fall_shifts = text2num(elevator_file) + else + elevator_fall_shifts = initial(elevator_fall_shifts) + if(isnull(elevator_fall_shifts)) + elevator_fall_shifts = initial(elevator_fall_shifts) + elevator_fall_shifts++ + // End snowflake. + +/datum/controller/subsystem/persistence/Shutdown() + var/list/all_persistence_datums = decls_repository.get_decls_of_subtype(/decl/persistence_handler) + for(var/thing in all_persistence_datums) + var/decl/persistence_handler/P = all_persistence_datums[thing] + P.Shutdown() + + // Refer to snowflake above. + if(fexists(ELEVATOR_FALL_PATH)) + fdel(ELEVATOR_FALL_PATH) + text2file("[elevator_fall_shifts]", ELEVATOR_FALL_PATH) + + // Handle level data shutdown. + start_persistent_level_save() + while(_persistent_save_running) + sleep(1) + +/datum/controller/subsystem/persistence/fire(resumed) + if(world.time <= initial_save_skip_period) + return + do_save_with_warning() + +/datum/controller/subsystem/persistence/proc/do_save_with_warning() + set waitfor = FALSE + if(showing_warning) + return // debounce + showing_warning = TRUE + if(save_warning_period > 0) + var/remaining_delay = save_warning_period + while(remaining_delay > 10 SECONDS) + to_world(SPAN_DANGER("World save will begin in [round(remaining_delay/10)] second\s! Prepare for a server freeze!")) + remaining_delay -= 10 SECONDS + sleep(10 SECONDS) + if(remaining_delay > 0) + to_world(SPAN_DANGER("World save will begin in [round(remaining_delay/10)] second\s! Prepare for a server freeze!")) + sleep(remaining_delay) + + to_world(SPAN_DANGER("Starting world save!")) + sleep(1 SECOND) + showing_warning = FALSE + start_persistent_level_save() + to_world(SPAN_DANGER("Saved the world! Thank you for your patience, please go about your business.")) + +/datum/controller/subsystem/persistence/proc/start_persistent_level_save() + if(_persistent_save_running) + return // debounce + _persistent_save_running = TRUE // used to avoid shutting down mid-write + + var/started_run = REALTIMEOFDAY + report_progress("Starting persistent level save.") + // TODO: suspend all subsystems while the save is running + // TODO: prevent player input somehow? + try + for(var/z = 1 to length(SSmapping.levels_by_z)) + var/datum/level_data/level = SSmapping.levels_by_z[z] + level.save_persistent_data() + catch(var/exception/E) + error("Exception when running persistent level save: [EXCEPTION_TEXT(E)]") + // TODO: re-enable all subsystems + report_progress("Persistent level save finished in [(REALTIMEOFDAY-started_run)/10] second\s.") + _persistent_save_running = FALSE + +/datum/controller/subsystem/persistence/proc/track_value(var/atom/value, var/track_type) + + var/decl/persistence_handler/handler = RESOLVE_TO_DECL(track_type) + if(!istype(handler)) + return FALSE + + var/turf/T = get_turf(value) + if(!T) + return + + var/area/A = get_area(T) + if(handler.area_restricted && (!A || (A.area_flags & AREA_FLAG_NO_LEGACY_PERSISTENCE))) + return + + if(handler.station_restricted && (!T || !(T.z in SSmapping.station_levels) )) + return FALSE + + var/datum/level_data/level = SSmapping.levels_by_z[T.z] + if(!istype(level) || !level.permit_legacy_persistence) + return + + if(!tracking_values[track_type]) + tracking_values[track_type] = list() + tracking_values[track_type] |= value + +/datum/controller/subsystem/persistence/proc/is_tracking(var/atom/value, var/track_type) + . = (value in tracking_values[track_type]) + +/datum/controller/subsystem/persistence/proc/forget_value(var/atom/value, var/track_type) + if(tracking_values[track_type]) + tracking_values[track_type] -= value + +/datum/controller/subsystem/persistence/proc/show_info(var/mob/user) + + if(!check_rights(R_INVESTIGATE, C = user)) + return + + var/list/dat = list("") + var/can_modify = check_rights(R_ADMIN, 0, user) + var/list/all_persistence_datums = decls_repository.get_decls_of_subtype(/decl/persistence_handler) + for(var/thing in all_persistence_datums) + var/decl/persistence_handler/P = all_persistence_datums[thing] + if(P.has_admin_data) + dat += P.GetAdminSummary(user, can_modify) + dat += "
" + + var/datum/browser/popup = new(user, "admin_persistence", "Persistence Data") + popup.set_content(jointext(dat, null)) + popup.open() diff --git a/code/controllers/subsystems/shuttle.dm b/code/controllers/subsystems/shuttle.dm index 77f0d4083cb2..6871edd8db16 100644 --- a/code/controllers/subsystems/shuttle.dm +++ b/code/controllers/subsystems/shuttle.dm @@ -80,7 +80,7 @@ SUBSYSTEM_DEF(shuttle) try_add_landmark_tag(shuttle_landmark_tag, O) landmarks_still_needed -= shuttle_landmark_tag else if(istype(shuttle_landmark, /obj/effect/shuttle_landmark/automatic)) //These find their sector automatically - O = global.overmap_sectors[num2text(shuttle_landmark.z)] + O = global.overmap_sectors[shuttle_landmark.z] O ? O.add_landmark(shuttle_landmark, shuttle_landmark.shuttle_restricted) : (landmarks_awaiting_sector += shuttle_landmark) /datum/controller/subsystem/shuttle/proc/unregister_landmark(shuttle_landmark_tag) diff --git a/code/controllers/subsystems/skybox.dm b/code/controllers/subsystems/skybox.dm index 56f789d94d4c..31d3e3aec9a9 100644 --- a/code/controllers/subsystems/skybox.dm +++ b/code/controllers/subsystems/skybox.dm @@ -104,7 +104,7 @@ SUBSYSTEM_DEF(skybox) /datum/controller/subsystem/skybox/proc/get_skybox(z) if(!skybox_cache[num2text(z)]) skybox_cache[num2text(z)] = generate_skybox(z) - var/obj/effect/overmap/visitable/O = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/O = global.overmap_sectors[z] if(istype(O)) for(var/zlevel in O.map_z) skybox_cache["[zlevel]"] = skybox_cache[num2text(z)] @@ -123,7 +123,7 @@ SUBSYSTEM_DEF(skybox) res.overlays += base if(use_overmap_details) - var/obj/effect/overmap/visitable/O = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/O = global.overmap_sectors[z] if(istype(O)) var/image/overmap = image(skybox_icon) overmap.overlays += O.generate_skybox() diff --git a/code/controllers/subsystems/statistics.dm b/code/controllers/subsystems/statistics.dm index 32383c214d21..209d504cffa2 100644 --- a/code/controllers/subsystems/statistics.dm +++ b/code/controllers/subsystems/statistics.dm @@ -162,7 +162,7 @@ SUBSYSTEM_DEF(statistics) death.brainloss = dead.get_damage(BRAIN) death.oxyloss = dead.get_damage(OXY) death.using_map_name = global.using_map.full_name - var/obj/effect/overmap/visitable/cell = global.overmap_sectors[num2text(dead.z)] + var/obj/effect/overmap/visitable/cell = global.overmap_sectors[dead.z] death.overmap_location_name = cell?.name || "Unknown" LAZYADD(deaths, death) diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm index 5c097d91ba2b..f431bbc339c0 100644 --- a/code/controllers/subsystems/ticker.dm +++ b/code/controllers/subsystems/ticker.dm @@ -480,4 +480,14 @@ Helpers /datum/controller/subsystem/ticker/proc/print_lobby_message() to_world("[SPAN_BLUE("Welcome to the pre-game lobby!")]") - to_world("Please, setup your character and select ready. Game will start in [round(pregame_timeleft/10)] seconds") \ No newline at end of file + to_world("Please, setup your character and select ready. Game will start in [round(pregame_timeleft/10)] seconds") + + +/datum/controller/subsystem/ticker/proc/get_game_mode_options() + . = list() + . += "Respawning: [mode.deny_respawn ? "disallowed" : "allowed"]" + . += "Shuttle delay multiplier: [mode.shuttle_delay]
" + . += "Shuttle auto-recall: [mode.auto_recall_shuttle ? "enabled" : "disabled"]" + . += "" // we want a blank line here for some design reason idk + . += "Moderate event time modifier: [mode.event_delay_mod_moderate || "unset"]
" + . += "Major event time modifier: [mode.event_delay_mod_major || "unset"]
" \ No newline at end of file diff --git a/code/datums/composite_sounds/vehicle_engine.dm b/code/datums/composite_sounds/vehicle_engine.dm new file mode 100644 index 000000000000..824d0044134b --- /dev/null +++ b/code/datums/composite_sounds/vehicle_engine.dm @@ -0,0 +1,7 @@ +/datum/composite_sound/vehicle_engine + start_sound = 'sound/machines/vehicle/engine_start.ogg' + start_length = 2 + mid_sounds = list('sound/machines/vehicle/engine_mid.ogg'=1) + mid_length = 6 + end_sound = 'sound/machines/vehicle/engine_end.ogg' + play_volume = 20 diff --git a/code/datums/config/config_types/config_game_option.dm b/code/datums/config/config_types/config_game_option.dm index 330f993c1a0c..a2e0c6720a7f 100644 --- a/code/datums/config/config_types/config_game_option.dm +++ b/code/datums/config/config_types/config_game_option.dm @@ -23,7 +23,6 @@ /decl/config/num/expected_round_length, /decl/config/toggle/on/allow_diagonal_movement, /decl/config/toggle/expanded_alt_interactions, - /decl/config/toggle/ert_admin_call_only, /decl/config/toggle/ghosts_can_possess_animals, /decl/config/toggle/assistant_maint, /decl/config/toggle/ghost_interaction, @@ -143,10 +142,6 @@ uid = "expanded_alt_interactions" desc = "Determines if objects should provide expanded alt interactions when alt-clicked, such as use or grab." -/decl/config/toggle/ert_admin_call_only - uid = "ert_admin_call_only" - desc = "Restricted ERT to be only called by admins." - /decl/config/toggle/ghosts_can_possess_animals uid = "ghosts_can_possess_animals" desc = "Determines of ghosts are allowed to possess any animal." diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 881b69bb4e36..30e00f5a6573 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -9,6 +9,8 @@ var/list/active_timers /// Used to avoid unnecessary refstring creation in Destroy(). var/tmp/has_state_machine = FALSE + /// Var for holding a unique-to-this-run identifier for a serialized datum. + VAR_PRIVATE/tmp/__run_uid #ifdef REFTRACKING_ENABLED var/tmp/running_find_references @@ -42,10 +44,11 @@ qdel(timer) if(extensions) - for(var/expansion_key in extensions) - var/list/extension = extensions[expansion_key] + var/list/extension_list + for(var/expansion_key, extension in extensions) if(islist(extension)) - extension.Cut() + extension_list = extension + extension_list.Cut() else qdel(extension) extensions = null diff --git a/code/datums/datum_serde.dm b/code/datums/datum_serde.dm new file mode 100644 index 000000000000..cafee1161d64 --- /dev/null +++ b/code/datums/datum_serde.dm @@ -0,0 +1,36 @@ +// Used for saving instances via the level persistence system. +// Returns an assoc list of var name to var value. +// Expected format is: +// list("field" = "value", "so on" = "so forth")) +// Using a var name (via nameof() or manually) will automatically load the var to the field in Deserialize. +// If serializing an instance reference, use get_run_uid() to get a UID. +/datum/proc/Serialize() + SHOULD_CALL_PARENT(TRUE) + . = list((nameof(/datum::type)) = GetSerializedType()) + +/datum/proc/GetSerializedType() + return type + +/datum/proc/GetPossiblySerializableInstances() + return list(src) + +// A proc for checking preconditions on an instance to determine if it should bother serializing at all. +/datum/proc/ShouldSerialize(_age) + SHOULD_CALL_PARENT(TRUE) + return TRUE + +// Returns a UID for this instance, used for serde across rounds. +// Probably-kind-of a GUID but only for this run. +/datum/proc/get_run_uid() + if(isnull(__run_uid)) + __run_uid = "\ref[src]-[sequential_id(type)]" // Staple seq_id on there in case of \ref reuse. + return __run_uid + +// Called after Initialize()/LateInitialize() on all non-atom datums, and if an atom returns SERDE_HINT_POSTINIT to Deserialize(). +/datum/proc/DeserializePostInit(list/instance_map) + return + +// Apply cross-round degradation (graffiti decaying, etc) prior to Deserialize() and Initialize() +// Typically this means modifying __deserialization_payload +/datum/proc/HandlePersistentDecay(entries_decay_at, entry_decay_weight) + return diff --git a/code/datums/extensions/extensions.dm b/code/datums/extensions/extensions.dm index 53985ff5862d..ab28746a5581 100644 --- a/code/datums/extensions/extensions.dm +++ b/code/datums/extensions/extensions.dm @@ -38,7 +38,8 @@ source.PopulateClone(src) /datum - var/list/datum/extension/extensions + /// A lazy alist() keyed by extension base type. Values are either a list of extension init arguments for lazy-loaded extensions, or an extension datum. + var/alist/extensions //Variadic - Additional positional arguments can be given. Named arguments might not work so well /proc/set_extension(var/datum/source, var/datum/extension/extension_type) @@ -49,8 +50,7 @@ CRASH("Invalid base type: Expected /datum/extension, was [log_info_line(extension_base_type)]") if(!ispath(extension_type, extension_base_type)) CRASH("Invalid extension type: Expected [extension_base_type], was [log_info_line(extension_type)]") - if(!source.extensions) - source.extensions = list() + A_LAZYINITLIST(source.extensions) var/datum/extension/existing_extension = source.extensions[extension_base_type] if(istype(existing_extension)) qdel(existing_extension) @@ -115,7 +115,7 @@ return if(!islist(source.extensions[base_type])) qdel(source.extensions[base_type]) - LAZYREMOVE(source.extensions, base_type) + A_LAZYREMOVE(source.extensions, base_type) ///Copy the extension instance on the 'source' and put it on the 'destination'. /proc/copy_extension(var/datum/source, var/datum/destination, var/base_type) diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 308bacd0a866..138e0c1d5093 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -25,7 +25,7 @@ return isAdminLevel(tz) || isStationLevel(tz) || isContactLevel(tz) var/list/accessible_z_levels = SSmapping.get_connected_levels(oz) - var/obj/effect/overmap/sector = global.overmap_sectors[num2text(oz)] + var/obj/effect/overmap/sector = global.overmap_sectors[oz] if(sector) var/list/neighbors_to_add = list() diff --git a/code/datums/inventory_slots/_inventory_slot.dm b/code/datums/inventory_slots/_inventory_slot.dm index 086db0ce8990..3567e31a8486 100644 --- a/code/datums/inventory_slots/_inventory_slot.dm +++ b/code/datums/inventory_slots/_inventory_slot.dm @@ -19,6 +19,8 @@ var/requires_slot_flags var/requires_organ_tag var/quick_equip_priority = 0 // Higher priority means it will be checked first. If null, will not be considered for quick equip. + /// Additional slot ID(s) to add in quick equip. Will always be added at the lowest priority. + var/list/additional_quick_equip_slots /// What depth of fluid is necessary for an item in this slot to be considered submerged? var/fluid_height = FLUID_SHALLOW // we're treating FLUID_SHALLOW as waist level, basically diff --git a/code/datums/inventory_slots/slots/slot_back.dm b/code/datums/inventory_slots/slots/slot_back.dm index 1f900ae9f4d6..a2a30b9c5ba5 100644 --- a/code/datums/inventory_slots/slots/slot_back.dm +++ b/code/datums/inventory_slots/slots/slot_back.dm @@ -8,6 +8,7 @@ mob_overlay_layer = HO_BACK_LAYER quick_equip_priority = 14 fluid_height = (FLUID_SHALLOW + FLUID_OVER_MOB_HEAD) / 2 // halfway between waist and top of head, so roughly chest level + additional_quick_equip_slots = list(slot_in_backpack_str) /datum/inventory_slot/back/simple requires_organ_tag = null diff --git a/code/datums/inventory_slots/slots/slot_id.dm b/code/datums/inventory_slots/slots/slot_id.dm index 67b759c682ca..ecde7fc83dba 100644 --- a/code/datums/inventory_slots/slots/slot_id.dm +++ b/code/datums/inventory_slots/slots/slot_id.dm @@ -7,6 +7,7 @@ mob_overlay_layer = HO_ID_LAYER quick_equip_priority = 13 fluid_height = (FLUID_SHALLOW + FLUID_OVER_MOB_HEAD) / 2 // halfway between waist and top of head, so roughly chest level + additional_quick_equip_slots = list(slot_in_wallet_str) // try to go as late as possible /datum/inventory_slot/id/update_mob_equipment_overlay(var/mob/living/user, var/obj/item/prop, var/redraw_mob = TRUE) var/obj/item/clothing/clothes = user.get_equipped_item(slot_w_uniform_str) diff --git a/code/datums/outfits/outfit.dm b/code/datums/outfits/outfit.dm index fa1c3f9627b9..a3ce8adfbdd5 100644 --- a/code/datums/outfits/outfit.dm +++ b/code/datums/outfits/outfit.dm @@ -270,3 +270,7 @@ /decl/outfit/dd_SortValue() return name + +// Stub for the sake of being able to make people spawn nude. +/decl/outfit/naked + name = "Naked And Afraid" \ No newline at end of file diff --git a/code/datums/outfits/spec_op.dm b/code/datums/outfits/spec_op.dm index 5effd8115863..1e2bb91ce318 100644 --- a/code/datums/outfits/spec_op.dm +++ b/code/datums/outfits/spec_op.dm @@ -1,43 +1,3 @@ -/decl/outfit/spec_op_officer - name = "Spec Ops - Officer" - uniform = /obj/item/clothing/pants/casual/camo/outfit_combat - suit = /obj/item/clothing/suit/armor/officer - l_ear = /obj/item/radio/headset/ert - glasses = /obj/item/clothing/glasses/thermal/plain/eyepatch - mask = /obj/item/clothing/mask/smokable/cigarette/cigar/havana - head = /obj/item/clothing/head/beret - belt = /obj/item/gun/energy/pulse_pistol - back = /obj/item/backpack/satchel - shoes = /obj/item/clothing/shoes/jackboots/swat/combat - gloves = /obj/item/clothing/gloves/thick/combat - - id_slot = slot_wear_id_str - id_type = /obj/item/card/id/centcom/ERT - id_desc = "Special operations ID." - id_pda_assignment = "Special Operations Officer" - -/decl/outfit/spec_op_officer/space - name = "Spec Ops - Officer in space" - suit = /obj/item/clothing/suit/space/void/swat - back = /obj/item/tank/jetpack/oxygen - mask = /obj/item/clothing/mask/gas/swat - - outfit_flags = OUTFIT_HAS_JETPACK|OUTFIT_RESET_EQUIPMENT - -/decl/outfit/ert - name = "Spec Ops - Emergency response team" - uniform = /obj/item/clothing/pants/casual/camo/outfit_combat - shoes = /obj/item/clothing/shoes/jackboots/swat - gloves = /obj/item/clothing/gloves/thick/swat - l_ear = /obj/item/radio/headset/ert - belt = /obj/item/gun/energy/gun - glasses = /obj/item/clothing/glasses/sunglasses - back = /obj/item/backpack/satchel - pda_type = /obj/item/modular_computer/pda/ert - - id_slot = slot_wear_id_str - id_type = /obj/item/card/id/centcom/ERT - /decl/outfit/mercenary name = "Spec Ops - Mercenary" uniform = /obj/item/clothing/pants/casual/camo/outfit diff --git a/code/datums/repositories/atom_info.dm b/code/datums/repositories/atom_info.dm index a84f10736b76..2cfa4bd9ff3e 100644 --- a/code/datums/repositories/atom_info.dm +++ b/code/datums/repositories/atom_info.dm @@ -1,13 +1,14 @@ var/global/repository/atom_info/atom_info_repository = new() /repository/atom_info - var/list/matter_cache = list() + var/list/matter_cache = list() var/list/combined_worth_cache = list() - var/list/single_worth_cache = list() - var/list/name_cache = list() - var/list/description_cache = list() - var/list/matter_mult_cache = list() - var/list/origin_tech_cache = list() + var/list/single_worth_cache = list() + var/list/name_cache = list() + var/list/description_cache = list() + var/list/matter_mult_cache = list() + var/list/origin_tech_cache = list() + var/list/appearance_cache = list() /repository/atom_info/proc/create_key_for(var/_path, var/_mat, var/_amount) . = "[_path]" @@ -24,7 +25,7 @@ var/global/repository/atom_info/atom_info_repository = new() else . = new _path -/repository/atom_info/proc/update_cached_info_for(var/_path, var/_mat, var/_amount, var/key) +/repository/atom_info/proc/update_cached_info_for(var/_path, var/_mat, var/_amount, var/key, var/cache_appearance = FALSE) var/atom/instance if(!matter_cache[key]) instance = get_instance_of(_path, _mat, _amount) @@ -41,6 +42,9 @@ var/global/repository/atom_info/atom_info_repository = new() if(!description_cache[key]) instance = instance || get_instance_of(_path, _mat, _amount) description_cache[key] = instance.desc + if(cache_appearance && !appearance_cache[key]) + instance = instance || get_instance_of(_path, _mat, _amount) + appearance_cache[key] = instance.appearance if(!matter_mult_cache[key] && ispath(_path, /obj)) var/obj/obj_instance = instance || get_instance_of(_path, _mat, _amount) matter_mult_cache[key] = obj_instance.get_matter_amount_modifier() @@ -84,4 +88,10 @@ var/global/repository/atom_info/atom_info_repository = new() /repository/atom_info/proc/get_origin_tech_for(var/_path, var/_mat, var/_amount) var/key = create_key_for(_path, _mat, _amount) update_cached_info_for(_path, _mat, _amount, key) - . = origin_tech_cache[key] \ No newline at end of file + . = origin_tech_cache[key] + +// Bespoke proc; only cache appearance if and when this proc is called, not more generally. +/repository/atom_info/proc/get_appearance_of(var/_path, var/_mat, var/_amount) + var/key = create_key_for(_path, _mat, _amount) + update_cached_info_for(_path, _mat, _amount, key, cache_appearance = TRUE) + . = appearance_cache[key] diff --git a/code/datums/storage/subtypes_box.dm b/code/datums/storage/subtypes_box.dm index 3803830d60f4..b94d406d1e6b 100644 --- a/code/datums/storage/subtypes_box.dm +++ b/code/datums/storage/subtypes_box.dm @@ -18,6 +18,9 @@ /datum/storage/box/animal_cube can_hold = list(/obj/item/food/animal_cube) +/datum/storage/box/large/metal + use_sound = 'sound/effects/closet_open.ogg' + /datum/storage/box/snappop can_hold = list(/obj/item/toy/snappop) diff --git a/code/datums/supplypacks/operations.dm b/code/datums/supplypacks/operations.dm index 09c51789dc3c..0ec89477c5e1 100644 --- a/code/datums/supplypacks/operations.dm +++ b/code/datums/supplypacks/operations.dm @@ -3,13 +3,13 @@ /decl/hierarchy/supply_pack/operations/cargotrain name = "Equipment - Cargo Train Tug" - contains = list(/obj/vehicle/train/cargo/engine) + contains = list(/obj/vehicle/train/engine) containertype = /obj/structure/largecrate containername = "cargo train tug crate" /decl/hierarchy/supply_pack/operations/cargotrailer name = "Equipment - Cargo Train Trolley" - contains = list(/obj/vehicle/train/cargo/trolley) + contains = list(/obj/vehicle/train/trolley) containertype = /obj/structure/largecrate containername = "cargo train trolley crate" diff --git a/code/datums/supplypacks/science.dm b/code/datums/supplypacks/science.dm index 5505d65f4647..93c9197bbc0b 100644 --- a/code/datums/supplypacks/science.dm +++ b/code/datums/supplypacks/science.dm @@ -43,7 +43,7 @@ /obj/item/clothing/gloves/thick, /obj/item/clothing/shoes/color/black, /obj/item/scanner/gas, - /obj/item/ore, + /obj/item/ore_satchel, /obj/item/flashlight/lantern, /obj/item/tool/shovel, /obj/item/tool/pickaxe, @@ -66,12 +66,3 @@ name = "Gear - Illumination grenades" contains = list(/obj/item/grenade/light = 8) containername = "illumination grenade crate" - -/decl/hierarchy/supply_pack/science/stasis_cages - name = "Stasis Cage" - contains = list( - /obj/structure/stasis_cage = 1 - ) - containertype = /obj/structure/closet/crate/large - containername = "stasis cage crate" - access = access_research diff --git a/code/datums/supplypacks/supplypack.dm b/code/datums/supplypacks/supplypack.dm index 319d1dee4089..7544b406d35f 100644 --- a/code/datums/supplypacks/supplypack.dm +++ b/code/datums/supplypacks/supplypack.dm @@ -19,13 +19,16 @@ var/global/list/cargoprices = list() . = ..() // make sure children are set up if(is_category()) return // don't do any of this for categories + var/total_contained = 0 + for(var/entry in contains) + total_contained += max(1, contains[entry]) if(!num_contained) - for(var/entry in contains) - num_contained += max(1, contains[entry]) + num_contained = total_contained if(isnull(cost)) cost = 0 for(var/entry in contains) cost += atom_info_repository.get_combined_worth_for(entry) * max(1, contains[entry]) + cost *= num_contained / total_contained // if you get a random selection, it costs the expected value rather than the total worth. gambling! cost += containertype ? atom_info_repository.get_single_worth_for(containertype) : 0 cost = max(1, NONUNIT_CEILING((cost * WORTH_TO_SUPPLY_POINTS_CONSTANT * SSsupply.price_markup), WORTH_TO_SUPPLY_POINTS_ROUND_CONSTANT)) global.cargoprices[name] = cost diff --git a/code/datums/trading/trade_hub_overmap.dm b/code/datums/trading/trade_hub_overmap.dm index 16096eac4ebb..e9c52c310f2b 100644 --- a/code/datums/trading/trade_hub_overmap.dm +++ b/code/datums/trading/trade_hub_overmap.dm @@ -66,7 +66,7 @@ var/global/list/trading_hub_names = list() /datum/trade_hub/overmap/is_accessible_from(var/turf/check) if(istype(check)) - var/obj/effect/overmap/customer = global.overmap_sectors[num2text(check.z)] + var/obj/effect/overmap/customer = global.overmap_sectors[check.z] return customer && owner && get_turf(customer) == get_turf(owner) /datum/trade_hub/overmap/proc/update_hub(var/obj/effect/overmap/trade_hub/hub) diff --git a/code/datums/trading/traders/ai.dm b/code/datums/trading/traders/ai.dm index 51d038d968b4..4176f4556e3f 100644 --- a/code/datums/trading/traders/ai.dm +++ b/code/datums/trading/traders/ai.dm @@ -43,15 +43,14 @@ They sell generic supplies and ask for generic supplies. /obj/item/backpack/satchel/grey/withwallet = TRADER_BLACKLIST, /obj/item/backpack/satchel/syndie_kit = TRADER_BLACKLIST_ALL, /obj/item/backpack/chameleon = TRADER_BLACKLIST, - /obj/item/backpack/ert = TRADER_BLACKLIST_ALL, /obj/item/backpack/dufflebag/syndie = TRADER_BLACKLIST_SUB, /obj/item/belt/champion = TRADER_THIS_TYPE, /obj/item/briefcase = TRADER_THIS_TYPE, /obj/item/box/fancy = TRADER_SUBTYPES_ONLY, /obj/item/laundry_basket = TRADER_THIS_TYPE, /obj/item/secure_storage/briefcase = TRADER_THIS_TYPE, - /obj/item/plants = TRADER_THIS_TYPE, - /obj/item/ore = TRADER_THIS_TYPE, + /obj/item/plant_satchel = TRADER_THIS_TYPE, + /obj/item/ore_satchel = TRADER_THIS_TYPE, /obj/item/toolbox = TRADER_ALL, /obj/item/wallet = TRADER_THIS_TYPE, /obj/item/photo_album = TRADER_THIS_TYPE, diff --git a/code/datums/uplink/services.dm b/code/datums/uplink/services.dm index b81dfc3ec323..435455774852 100644 --- a/code/datums/uplink/services.dm +++ b/code/datums/uplink/services.dm @@ -86,7 +86,7 @@ if(state != AWAITING_ACTIVATION) to_chat(user, "\The [src] won't activate again.") return - var/obj/effect/overmap/visitable/O = global.overmap_sectors[num2text(get_z(src))] + var/obj/effect/overmap/visitable/O = global.overmap_sectors[get_z(src)] var/choice = alert(user, "This will only affect your current location[istype(O) ? " ([O])" : ""]. Proceed?","Confirmation", "Yes", "No") if(choice != "Yes") return diff --git a/code/game/antagonist/antagonist_add.dm b/code/game/antagonist/antagonist_add.dm index df05b8300899..60a982a81ac9 100644 --- a/code/game/antagonist/antagonist_add.dm +++ b/code/game/antagonist/antagonist_add.dm @@ -15,7 +15,7 @@ . = TRUE else report_progress("Loading map template '[base]' for [name].") - . = base.load_new_z(FALSE) // Don't skip ChangeTurf. + . = base.load_new_z() if(.) get_starting_locations() diff --git a/code/game/area/area_access.dm b/code/game/area/area_access.dm index dfd47b4726a2..1aa23a3c0868 100644 --- a/code/game/area/area_access.dm +++ b/code/game/area/area_access.dm @@ -1,6 +1,7 @@ /area var/list/req_access = list() var/secure = FALSE // unsecure areas will have doors between them use access diff; secure ones use union. + var/override_unlock = FALSE // TRUE will override area and will be always unlocked. This is e.g. for restrooms inside secure areas, surgery observation room etc. // Given two areas, find the minimal req_access needed such that (return value) + (area access) >= (other area access) and vice versa /proc/req_access_diff(area/first, area/second) diff --git a/code/game/area/area_space.dm b/code/game/area/area_space.dm index 5c2f8c444715..b8cfc6e116d5 100644 --- a/code/game/area/area_space.dm +++ b/code/game/area/area_space.dm @@ -10,7 +10,7 @@ var/global/area/space_area power_equip = 0 power_environ = 0 has_gravity = 0 - area_flags = AREA_FLAG_EXTERNAL | AREA_FLAG_IS_NOT_PERSISTENT | AREA_FLAG_IS_BACKGROUND | AREA_FLAG_HIDE_FROM_HOLOMAP + area_flags = AREA_FLAG_EXTERNAL | AREA_FLAG_NO_LEGACY_PERSISTENCE | AREA_FLAG_IS_BACKGROUND | AREA_FLAG_HIDE_FROM_HOLOMAP ambience = list('sound/ambience/ambispace1.ogg','sound/ambience/ambispace2.ogg','sound/ambience/ambispace3.ogg','sound/ambience/ambispace4.ogg','sound/ambience/ambispace5.ogg') is_outside = OUTSIDE_YES diff --git a/code/game/area/areas_serde.dm b/code/game/area/areas_serde.dm new file mode 100644 index 000000000000..d2b8270006c5 --- /dev/null +++ b/code/game/area/areas_serde.dm @@ -0,0 +1,3 @@ +/area/ShouldSerialize(_age) + SHOULD_CALL_PARENT(FALSE) + return FALSE // This is specifically if this area instance should serialize, not if the contents should. diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 28a1dc589ec6..e698467579de 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -742,17 +742,19 @@ - `post_climb_check?`: If we should check if the user can continue climbing - Return: `TRUE` if they can climb, otherwise `FALSE` */ -/atom/proc/can_climb(var/mob/living/user, post_climb_check=0) +/atom/proc/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) if (!(atom_flags & ATOM_FLAG_CLIMBABLE) || !user.can_touch(src) || (!post_climb_check && climbers && (user in climbers))) return FALSE if (!user.Adjacent(src)) - to_chat(user, "You can't climb there, the way is blocked.") + if(!silent) + to_chat(user, SPAN_WARNING("You can't climb there, the way is blocked.")) return FALSE var/obj/occupied = turf_is_crowded(user) if(occupied) - to_chat(user, "There's \a [occupied] in the way.") + if(!silent) + to_chat(user, SPAN_WARNING("There's \a [occupied] in the way.")) return FALSE return TRUE @@ -1065,3 +1067,18 @@ if(blood_color) return FONT_COLORED(blood_color, "stained") return null + +// Used to mark a turf as containing objects that are dangerous to step onto. +/atom/proc/register_dangerous_to_step() + var/turf/T = get_turf(src) + if(T) + T.register_dangerous_object(src) + +/atom/proc/unregister_dangerous_to_step() + var/turf/T = get_turf(src) + if(T) + T.unregister_dangerous_object(src) + +// Test for if stepping on a tile containing this obj is safe to do, used for things like landmines and cliffs. +/atom/proc/is_safe_to_step(mob/living/stepper) + return TRUE diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 94c8602eefee..628b5880042e 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -628,3 +628,7 @@ /atom/movable/proc/is_valid_merchant_pad_target() return simulated + +// TODO reimplement this properly. +/atom/movable/proc/is_incorporeal() + return !simulated diff --git a/code/game/atoms_movable_serde.dm b/code/game/atoms_movable_serde.dm new file mode 100644 index 000000000000..e47ee52da8fd --- /dev/null +++ b/code/game/atoms_movable_serde.dm @@ -0,0 +1,13 @@ +/atom/movable/Serialize() + . = ..() + if(isturf(loc)) + SERIALIZE_VALUE(loc, /atom/movable, list(loc.x, loc.y, loc.z)) + // The below does not handle cases where the nested instance is not itself persistent. + // In this case, if the instance tried to serialize while inside a non-persistent instance, it would + // throw a runtime on subsequent loads due to having a UID as a loc that does not map to a loaded instance. + else if(isatom(loc)) + SERIALIZE_VALUE(loc, /atom/movable, loc.get_run_uid()) + +/atom/movable/Deserialize(list/instance_map) + . = ..() + contents_were_modified() diff --git a/code/game/atoms_serde.dm b/code/game/atoms_serde.dm new file mode 100644 index 000000000000..e8212228384e --- /dev/null +++ b/code/game/atoms_serde.dm @@ -0,0 +1,81 @@ +/atom + /// Var for holding serde information when this atom was loaded from a persistent source. + var/__deserialization_payload + +/atom/Serialize() + . = ..() + if(current_health != get_max_health()) + SERIALIZE(current_health, /atom) + SERIALIZE_IF_MODIFIED(max_health, /atom) + SERIALIZE_IF_MODIFIED(dir, /atom) + if(ATOM_IS_TEMPERATURE_SENSITIVE(src)) + SERIALIZE_IF_MODIFIED(temperature, /atom) + if(istype(reagents)) + SERIALIZE_REAGENTS(reagents, /atom, "atom") + SERIALIZE_DECL_IF_MODIFIED(material, /atom) + SERIALIZE_DECL_IF_MODIFIED(reinf_material, /atom) + SERIALIZE_IF_MODIFIED(paint_color, /atom) + SERIALIZE_IF_MODIFIED(pixel_x, /atom) + SERIALIZE_IF_MODIFIED(pixel_y, /atom) + SERIALIZE_IF_MODIFIED(default_pixel_x, /atom) + SERIALIZE_IF_MODIFIED(default_pixel_y, /atom) + +// Keeping this in code for reference, but a large number of atoms generate +// name and desc at runtime, so not storing this in serde by default. +/* + SERIALIZE_IF_MODIFIED(name, /atom) + SERIALIZE_IF_MODIFIED(desc, /atom) +*/ + // TODO: serialize forensics + +/atom/proc/Deserialize(list/instance_map) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + for(var/data_key in __deserialization_payload) + if(data_key in vars) + try + if(!global._forbid_field_load[data_key] && (data_key in vars)) + vars[data_key] = __deserialization_payload[data_key] + else + PreloadKey(data_key, __deserialization_payload[data_key]) + catch(var/exception/E) + error("Failed to write [data_key] to [type] vars: [E]") + DESERIALIZE_REAGENTS(reagents, "atom") // Handled in initialize_reagents() + DESERIALIZE_DECL_TO_TYPE(material) + DESERIALIZE_DECL_TO_TYPE(reinf_material) + return SERDE_HINT_FINISHED + +/atom/ShouldSerialize(_age) + return ..() && simulated + +/atom/GetPossiblySerializableInstances() + . = ..() + var/list/contained = get_contained_external_atoms() + if(length(contained)) + . |= contained + +/atom/Exited(atom/movable/atom, atom/newloc) + . = ..() + if(simulated && atom.ShouldSerialize()) + contents_were_modified() + +/atom/Entered(atom/movable/atom, atom/old_loc) + . = ..() + if(simulated && atom.ShouldSerialize()) + contents_were_modified() + +// Called when an instance is being preloaded with information from deserialization. +/atom/proc/Preload(list/instance_map) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + var/turf/turf = get_turf(src) + if(__deserialization_payload) + try + . = Deserialize(instance_map) + catch(var/exception/E) + PRINT_STACK_TRACE("Exception when deserializing [type] at ([turf?.x || "NULL"],[turf?.y || "NULL"],[turf?.z || "NULL"]): [E]") + else + PRINT_STACK_TRACE("[type] at ([turf?.x || "NULL"],[turf?.y || "NULL"],[turf?.z || "NULL"]) tried to preload with no deserialization payload.") + +/atom/proc/PreloadKey(data_key, payload) + return diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 584abf220f3f..4ac6e53ff068 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -14,7 +14,6 @@ var/global/list/additional_antag_types = list() var/required_players = 0 // Minimum players for round to start if voted in. var/required_enemies = 0 // Minimum antagonists for round to start. var/end_on_antag_death = FALSE // Round will end when all antagonists are dead. - var/ert_disabled = FALSE // ERT cannot be called. var/deny_respawn = FALSE // Disable respawn during this round. var/list/disabled_jobs = list() // Mostly used for Malf. This check is performed in job_controller so it doesn't spawn a regular AI. @@ -38,6 +37,7 @@ var/global/list/additional_antag_types = list() var/waittime_l = 60 SECONDS // Lower bound on time before start of shift report var/waittime_h = 180 SECONDS // Upper bounds on time before start of shift report + var/tmp/rand_waittime // The actual wait time selected. //Format: list(start_animation = duration, hit_animation, miss_animation). null means animation is skipped. var/cinematic_icon_states = list( @@ -54,21 +54,24 @@ var/global/list/additional_antag_types = list() round_autoantag = TRUE . = ..() +/decl/game_mode/proc/toggle_value(key) + switch(key) + if("respawn") + deny_respawn = !deny_respawn + return TRUE + if("shuttle_recall") + auto_recall_shuttle = !auto_recall_shuttle + return TRUE + if("autotraitor") + round_autoantag = !round_autoantag + return TRUE + /decl/game_mode/Topic(href, href_list[]) if(..()) return if(href_list["toggle"]) - switch(href_list["toggle"]) - if("respawn") - deny_respawn = !deny_respawn - if("ert") - ert_disabled = !ert_disabled - announce_ert_disabled() - if("shuttle_recall") - auto_recall_shuttle = !auto_recall_shuttle - if("autotraitor") - round_autoantag = !round_autoantag - message_admins("Admin [key_name_admin(usr)] toggled game mode option '[href_list["toggle"]]'.") + if(toggle_value(href_list["toggle"])) + message_admins("Admin [key_name_admin(usr)] toggled game mode option '[href_list["toggle"]]'.") else if(href_list["set"]) var/choice = "" switch(href_list["set"]) @@ -245,13 +248,10 @@ var/global/list/additional_antag_types = list() refresh_event_modifiers() - spawn (ROUNDSTART_LOGOUT_REPORT_TIME) - display_roundstart_logout_report() + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(display_roundstart_logout_report)), ROUNDSTART_LOGOUT_REPORT_TIME) - spawn (rand(waittime_l, waittime_h)) - global.using_map.send_welcome() - sleep(rand(100,150)) - announce_ert_disabled() + rand_waittime = rand(waittime_l, waittime_h) + addtimer(CALLBACK(global.using_map, TYPE_PROC_REF(/datum/map, send_welcome)), rand_waittime) //Assign all antag types for this game mode. Any players spawned as antags earlier should have been removed from the pending list, so no need to worry about those. for(var/decl/special_role/antag in antag_templates) @@ -283,47 +283,6 @@ var/global/list/additional_antag_types = list() for(var/decl/special_role/antag in antag_templates) antag.reset_antag_selection() -/// Gets a list of default reasons for the ERT to be disabled. -/decl/game_mode/proc/possible_ert_disabled_reasons() - // This uses a static var so that modpacks can add default reasons, e.g. "supermatter dust". - var/static/list/reasons = list( - "political instability", - "quantum fluctuations", - "hostile raiders", - "derelict station debris", - "REDACTED", - "ancient alien artillery", - "solar magnetic storms", - "sentient time-travelling killbots", - "gravitational anomalies", - "wormholes to another dimension", - "a telescience mishap", - "radiation flares", - "leaks into a negative reality", - "antiparticle clouds", - "residual exotic energy", - "suspected criminal operatives", - "malfunctioning von Neumann probe swarms", - "shadowy interlopers", - "a stranded xenoform", - "haywire machine constructs", - "rogue exiles", - "artifacts of eldritch horror", - "a brain slug infestation", - "killer bugs that lay eggs in the husks of the living", - "a deserted transport carrying xenofauna specimens", - "an emissary requesting a security detail", - "radical transevolutionaries", - "classified security operations", - "a gargantuan glowing goat" - ) - return reasons - -/decl/game_mode/proc/announce_ert_disabled() - if(!ert_disabled) - return - command_announcement.Announce("The presence of [pick(possible_ert_disabled_reasons())] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission") - /decl/game_mode/proc/check_finished() if(SSevac.evacuation_controller?.round_over() || station_was_nuked) return 1 diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index 20cd9d5a395b..4acbcff8b72f 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -66,7 +66,7 @@ if(!istype(supplied_access, /list)) return FALSE - if(maint_all_access) + if(global.using_map.maint_all_access) // todo: movable -> loc -> area -> map datum, to allow separate maps? supplied_access = supplied_access.Copy() supplied_access |= access_maint_tunnels diff --git a/code/game/jobs/alt_titles.dm b/code/game/jobs/alt_titles.dm new file mode 100644 index 000000000000..cea4dafc8378 --- /dev/null +++ b/code/game/jobs/alt_titles.dm @@ -0,0 +1,23 @@ +/decl/alt_title + abstract_type = /decl/alt_title + var/name + var/desc + var/outfit + +/decl/alt_title/validate() + . = ..() + if(!name) + . += "missing name" + if(!desc) + . += "missing desc" + if(!ispath(outfit, /decl/outfit)) + . += "missing or invalid outfit: [outfit || "NULL"]" + +/datum/job/New() + if(length(alt_titles)) + for(var/title in alt_titles) + if(ispath(title, /decl/alt_title)) + var/decl/alt_title/title_data = GET_DECL(title) + alt_titles -= title + alt_titles[title_data.name] = title_data.outfit + ..() diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index 0fe1d526de06..2b278bc95e4b 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -2,11 +2,6 @@ preset_channels = list(CAMERA_CHANNEL_ENGINEERING) req_access = list(access_engine) -/obj/machinery/camera/network/ert - preset_channels = list(CAMERA_CHANNEL_ERT) - cameranet_enabled = FALSE - req_access = list(access_engine) - /obj/machinery/camera/network/medbay preset_channels = list(CAMERA_CHANNEL_MEDICAL) req_access = list(access_medical) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index fd685f5d9a8f..f08f9ac49846 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -248,7 +248,7 @@ var/newz if(prob(90)) var/list/possible_locations - var/obj/effect/overmap/visitable/O = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/O = global.overmap_sectors[z] if(istype(O)) for(var/obj/effect/overmap/visitable/OO in range(O,2)) if((OO.sector_flags & OVERMAP_SECTOR_IN_SPACE) || istype(OO,/obj/effect/overmap/visitable/sector/planetoid)) @@ -475,7 +475,7 @@ if(!usr.can_enter_cryopod(usr)) return - visible_message(emote_replace_user_tokens(emote_replace_target_tokens(on_enter_visible_message, src), usr), range = 3) + visible_message(capitalize_proper_html(emote_replace_user_tokens(emote_replace_target_tokens(on_enter_visible_message, src), usr)), range = 3) if(do_after(usr, 20, src)) diff --git a/code/game/machinery/doors/_door.dm b/code/game/machinery/doors/_door.dm index 17dfd16d50df..3b1aec756003 100644 --- a/code/game/machinery/doors/_door.dm +++ b/code/game/machinery/doors/_door.dm @@ -574,6 +574,8 @@ if (!fore && !aft) return list() + else if (fore.override_unlock || aft.override_unlock) + return list() else if (fore.secure || aft.secure) return req_access_union(fore, aft) else diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 7145a9762c51..677184076293 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -58,6 +58,7 @@ var/global/list/holopads = list() var/allow_ai = TRUE var/static/list/reachable_overmaps = list(OVERMAP_ID_SPACE) + var/static/list/used_holopad_ids = list() var/holopad_id /obj/machinery/hologram/holopad/Initialize() @@ -68,10 +69,16 @@ var/global/list/holopads = list() // Null ID means we want to use our area name. if(isnull(holopad_id)) var/area/A = get_area(src) - holopad_id = A?.proper_name || "Unknown" + var/holopad_index = 1 + var/holopad_base = A?.proper_name || "Unknown" + holopad_id = "[holopad_base] #[holopad_index]" + while(holopad_id in used_holopad_ids) + holopad_index++ + holopad_id = "[holopad_base] #[holopad_index]" + used_holopad_ids |= holopad_id // For overmap sites, always tag the sector name so we have a unique discriminator for long range calls. - var/obj/effect/overmap/visitable/sector = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/sector = global.overmap_sectors[z] if(sector) holopad_id = "[sector.name] - [holopad_id]" @@ -135,8 +142,8 @@ var/global/list/holopads = list() var/list/zlevels_long = list() if(holopadType == HOLOPAD_LONG_RANGE && length(reachable_overmaps)) - for(var/zlevel in global.overmap_sectors) - var/obj/effect/overmap/visitable/O = global.overmap_sectors[zlevel] + for(var/zlevel, sector in global.overmap_sectors) + var/obj/effect/overmap/visitable/O = sector if(!isnull(O) && (O.overmap_id in reachable_overmaps) && LAZYLEN(O.map_z)) zlevels_long |= O.map_z diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm index 3e0327cc03ad..81ea2fc50ed0 100644 --- a/code/game/machinery/holosign.dm +++ b/code/game/machinery/holosign.dm @@ -10,8 +10,9 @@ anchored = TRUE obj_flags = OBJ_FLAG_MOVES_UNSUPPORTED directional_offset = @'{"NORTH":{"y":-32}, "SOUTH":{"y":32}, "EAST":{"x":32}, "WEST":{"x":-32}}' - var/lit = 0 + var/lit = FALSE var/on_icon = "sign_on" + var/sign_light_color = COLOR_CYAN_BLUE uncreated_component_parts = list( /obj/item/stock_parts/radio/receiver, @@ -33,11 +34,11 @@ /obj/machinery/holosign/on_update_icon() if (!lit || inoperable()) - icon_state = "sign_off" + icon_state = initial(icon_state) set_light(0) else icon_state = on_icon - set_light(1, 0.5, COLOR_CYAN_BLUE) + set_light(1, 0.5, sign_light_color) /decl/public_access/public_variable/holosign_on expected_type = /obj/machinery/holosign @@ -68,6 +69,13 @@ desc = "Small wall-mounted holographic projector. This one reads SERVICE." on_icon = "service" +/obj/machinery/holosign/bar + name = "bar holosign" + desc = "Small wall-mounted holographic projector. This one reads OPEN." + icon_state = "barclosed" + on_icon = "baropen" + sign_light_color = COLOR_LIGHT_CYAN + ////////////////////SWITCH/////////////////////////////////////// /obj/machinery/button/holosign name = "holosign switch" diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 99d2fd9681f1..af1f1e4423b9 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -95,7 +95,7 @@ iconholder = 1 eprojectile = /obj/item/projectile/beam - if(/obj/item/gun/energy/captain) + if(/obj/item/gun/energy/retro/captain) iconholder = 1 if(/obj/item/gun/energy/lasercannon) diff --git a/code/game/machinery/vending/botany.dm b/code/game/machinery/vending/botany.dm index b1042ee71ea8..f001ba662632 100644 --- a/code/game/machinery/vending/botany.dm +++ b/code/game/machinery/vending/botany.dm @@ -13,7 +13,7 @@ /obj/item/chems/glass/bottle/robustharvest = 3, /obj/item/plantspray/pests = 20, /obj/item/chems/syringe = 5, - /obj/item/plants = 5, + /obj/item/plant_satchel = 5, /obj/item/chems/glass/bottle/ammonia = 10 ) idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan. diff --git a/code/game/movietitles.dm b/code/game/movietitles.dm index 90f5287221f8..6d1b954bab9f 100644 --- a/code/game/movietitles.dm +++ b/code/game/movietitles.dm @@ -128,7 +128,7 @@ var/global/list/end_titles titles += "
STAFF'S GOOD BOYS:
[english_list(goodboys)]

" var/disclaimer = "
Sponsored by [global.using_map.company_name].
All rights reserved.
\ - This motion picture is protected under the copyright laws of the Sol Central Government
and other nations throughout the galaxy.
\ + This motion picture is protected under the copyright laws of the system government
and other nations throughout the galaxy.
\ Colony of First Publication: [pick("Mars", "Luna", "Earth", "Venus", "Phobos", "Ceres", "Tiamat", "Ceti Epsilon", "Eos", "Pluto", "Ouere",\ "Lordania", "Kingston", "Cinu", "Yuklid V", "Lorriman", "Tersten", "Gaia")].
" disclaimer += pick("Use for parody prohibited. PROHIBITED.", diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index 01cc88b50e31..feebd9293847 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -27,17 +27,20 @@ var/directional_offset /obj/Initialize(mapload) - //Health should be set to max_health only if it's null. . = ..() create_matter() //Only apply directional offsets if the mappers haven't set any offsets already if(!pixel_x && !pixel_y && !pixel_w && !pixel_z) update_directional_offset() + + //Health should be set to max_health only if it's null. + var/_max_health = get_max_health() if(isnull(current_health) || current_health == INFINITY) - current_health = get_max_health() - else - current_health = min(current_health, get_max_health()) - if(!isnull(chem_volume) && chem_volume >= 0) // 0-volume holders perserved for legacy code reasons. Ideally shouldn't exist if <= 0 + current_health = _max_health + current_health = min(current_health, _max_health) + + // Initialize our reagents if they've been preloaded or we have a chem_volume + if((!isnull(chem_volume) && chem_volume >= 0) || islist(reagents)) initialize_reagents() /obj/object_shaken() @@ -264,15 +267,21 @@ return TRUE /** - * Init starting reagents and/or reagent var. Called if chem_volume > 0 in /obj/Initialize() - * populate: If set to true, we expect map load/admin spawned reagents to be set. + * Init starting reagents and/or reagent var. Called in /obj/Initialize() if volume is above 0. + * Skips populate_initialize() if reagents is null, or if it is a list, ie. we are pending deserialization. */ -/obj/proc/initialize_reagents(var/populate = TRUE) +/obj/proc/initialize_reagents() SHOULD_CALL_PARENT(TRUE) - if(REAGENT_TOTAL_VOLUME(reagents) > 0) + // Check if this is getting called twice, or we created reagents somewhere in Initialize() (bad juju) + if(istype(reagents)) log_warning("\The [src] possibly is initializing its reagents more than once!") - create_or_update_reagents(chem_volume) - if(populate) + // If preloaded from serde, handle expected list structure. + // Returns if preload is successful to skip populate_reagents() call. + FINALIZE_REAGENTS_SERDE_AND_RETURN(reagents) + // Standard non-serde reagent init behavior after this point. + if(chem_volume > 0) + create_or_update_reagents(chem_volume) + if(istype(reagents)) populate_reagents() /** @@ -493,4 +502,3 @@ if(anchored) return FALSE return ..() - diff --git a/code/game/objects/effects/_effect.dm b/code/game/objects/effects/_effect.dm index d7b2823e0d7d..11cc7b852391 100644 --- a/code/game/objects/effects/_effect.dm +++ b/code/game/objects/effects/_effect.dm @@ -1,6 +1,10 @@ /obj/effect abstract_type = /obj/effect +/obj/effect/ShouldSerialize(_age) + SHOULD_CALL_PARENT(FALSE) + return FALSE // Typically no. Specific subtypes should reimplement this (vomit etc) + /obj/effect/can_be_grabbed(var/mob/grabber, var/target_zone) return FALSE diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 421c415cb24f..48a282d2fdde 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -12,13 +12,13 @@ random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7", "dir_splatter_1", "dir_splatter_2") blood_DNA = list() generic_filth = TRUE - persistent = TRUE + use_legacy_persistence = TRUE appearance_flags = NO_CLIENT_COLOR cleanable_scent = "blood" scent_descriptor = "odour" var/base_icon = 'icons/effects/blood.dmi' - var/basecolor=COLOR_BLOOD_HUMAN // Color when wet. + var/basecolor = COLOR_BLOOD_HUMAN // Color when wet. var/amount = 5 //for 1 unit of depth in puddle (amount var) var/time_to_dry = 5 MINUTES @@ -29,6 +29,18 @@ var/list/blood_data var/chemical = /decl/material/liquid/blood +/obj/effect/decal/cleanable/blood/Serialize() + . = ..() + if(!generic_filth) // Generic filth is serialized to a type without these vars, so deserializing them will cause errors. + SERIALIZE_IF_MODIFIED(fluorescent, /obj/effect/decal/cleanable/blood) + SERIALIZE_IF_MODIFIED(basecolor, /obj/effect/decal/cleanable/blood) + SERIALIZE_IF_MODIFIED(drytime, /obj/effect/decal/cleanable/blood) + SERIALIZE_DECL_IF_MODIFIED(chemical, /obj/effect/decal/cleanable/blood) + +/obj/effect/decal/cleanable/blood/Deserialize(list/instance_map) + . = ..() + DESERIALIZE_DECL_TO_TYPE(chemical) + /obj/effect/decal/cleanable/blood/reveal_blood() if(ispath(chemical, /decl/material/liquid/blood) && !fluorescent) fluorescent = FLUORESCENT_GLOWS @@ -277,7 +289,7 @@ icon = 'icons/effects/blood.dmi' icon_state = "mucus" generic_filth = TRUE - persistent = TRUE + use_legacy_persistence = TRUE #undef BLOOD_SIZE_SMALL #undef BLOOD_SIZE_MEDIUM diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index 634c454322af..9b96d6939412 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -37,13 +37,13 @@ return TRUE /obj/effect/decal/cleanable/flour - name = "flour" - desc = "It's still good. Four second rule!" - gender = PLURAL - icon = 'icons/effects/effects.dmi' - icon_state = "flour" - persistent = TRUE - sweepable = TRUE + name = "flour" + desc = "It's still good. Four second rule!" + gender = PLURAL + icon = 'icons/effects/effects.dmi' + icon_state = "flour" + use_legacy_persistence = TRUE + sweepable = TRUE /obj/effect/decal/cleanable/cobweb name = "cobweb" @@ -55,13 +55,13 @@ sweepable = TRUE /obj/effect/decal/cleanable/molten_item - name = "gooey grey mass" - desc = "It looks like a melted... something." - icon = 'icons/effects/molten_item.dmi' - icon_state = "molten" - persistent = TRUE - generic_filth = TRUE - weather_sensitive = FALSE + name = "gooey grey mass" + desc = "It looks like a melted... something." + icon = 'icons/effects/molten_item.dmi' + icon_state = "molten" + use_legacy_persistence = TRUE + generic_filth = TRUE + weather_sensitive = FALSE /obj/effect/decal/cleanable/cobweb2 name = "cobweb" @@ -74,14 +74,14 @@ //Vomit (sorry) /obj/effect/decal/cleanable/vomit - name = "vomit" - desc = "Gosh, how unpleasant." - gender = PLURAL - icon = 'icons/effects/vomit.dmi' - icon_state = "vomit_1" - persistent = TRUE - generic_filth = TRUE - chem_volume = 30 + name = "vomit" + desc = "Gosh, how unpleasant." + gender = PLURAL + icon = 'icons/effects/vomit.dmi' + icon_state = "vomit_1" + use_legacy_persistence = TRUE + generic_filth = TRUE + chem_volume = 30 /obj/effect/decal/cleanable/vomit/Initialize(ml, _age) random_icon_states = icon_states(icon) @@ -106,40 +106,40 @@ walker.add_walking_contaminant(reagents, rand(2, 3)) /obj/effect/decal/cleanable/tomato_smudge - name = "tomato smudge" - desc = "It's red." - icon = 'icons/effects/tomatodecal.dmi' - icon_state = "tomato_floor1" - random_icon_states = list("tomato_floor1", "tomato_floor2", "tomato_floor3") - persistent = TRUE - generic_filth = TRUE + name = "tomato smudge" + desc = "It's red." + icon = 'icons/effects/tomatodecal.dmi' + icon_state = "tomato_floor1" + random_icon_states = list("tomato_floor1", "tomato_floor2", "tomato_floor3") + use_legacy_persistence = TRUE + generic_filth = TRUE /obj/effect/decal/cleanable/egg_smudge - name = "smashed egg" - desc = "Seems like this one won't hatch." - icon = 'icons/effects/tomatodecal.dmi' - icon_state = "smashed_egg1" - random_icon_states = list("smashed_egg1", "smashed_egg2", "smashed_egg3") - persistent = TRUE - generic_filth = TRUE + name = "smashed egg" + desc = "Seems like this one won't hatch." + icon = 'icons/effects/tomatodecal.dmi' + icon_state = "smashed_egg1" + random_icon_states = list("smashed_egg1", "smashed_egg2", "smashed_egg3") + use_legacy_persistence = TRUE + generic_filth = TRUE /obj/effect/decal/cleanable/pie_smudge //honk - name = "smashed pie" - desc = "It's pie cream from a cream pie." - icon = 'icons/effects/tomatodecal.dmi' - icon_state = "smashed_pie" - random_icon_states = list("smashed_pie") - persistent = TRUE - generic_filth = TRUE + name = "smashed pie" + desc = "It's pie cream from a cream pie." + icon = 'icons/effects/tomatodecal.dmi' + icon_state = "smashed_pie" + random_icon_states = list("smashed_pie") + use_legacy_persistence = TRUE + generic_filth = TRUE /obj/effect/decal/cleanable/fruit_smudge - name = "smudge" - desc = "Some kind of fruit smear." - icon = 'icons/effects/blood.dmi' - icon_state = "mfloor1" - random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") - persistent = TRUE - generic_filth = TRUE + name = "smudge" + desc = "Some kind of fruit smear." + icon = 'icons/effects/blood.dmi' + icon_state = "mfloor1" + random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") + use_legacy_persistence = TRUE + generic_filth = TRUE /obj/effect/decal/cleanable/champagne name = "champagne" diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index 036251e4ea38..aa0e391befb5 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -6,9 +6,8 @@ var/burnable = TRUE var/sweepable = FALSE var/weather_sensitive = TRUE - var/persistent = FALSE + var/use_legacy_persistence = FALSE var/generic_filth = FALSE - var/age = 0 var/list/random_icon_states var/image/hud_overlay/hud_overlay var/cleanable_scent @@ -16,14 +15,30 @@ var/scent_intensity = /decl/scent_intensity/normal var/scent_descriptor = "smell" var/scent_range = 2 + var/have_randomized_icon_state = FALSE + +/obj/effect/decal/cleanable/ShouldSerialize(_age) + return ..() && use_legacy_persistence + +/obj/effect/decal/cleanable/GetSerializedType() + return generic_filth ? /obj/effect/decal/cleanable/filth : ..() + +/obj/effect/decal/cleanable/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(icon_state, /atom) + +/obj/effect/decal/cleanable/Deserialize(list/instance_map) + . = ..() + have_randomized_icon_state = TRUE /obj/effect/decal/cleanable/Initialize(var/ml, var/_age) - if(random_icon_states && length(src.random_icon_states) > 0) - src.icon_state = pick(src.random_icon_states) + if(!have_randomized_icon_state && length(random_icon_states)) + icon_state = pick(random_icon_states) + have_randomized_icon_state = TRUE if(!ml) if(!isnull(_age)) age = _age - if(persistent) + if(use_legacy_persistence) SSpersistence.track_value(src, /decl/persistence_handler/filth) . = ..() @@ -43,7 +58,7 @@ /obj/effect/decal/cleanable/Destroy() if(weather_sensitive) SSweather_atoms.weather_atoms -= src - if(persistent) + if(use_legacy_persistence) SSpersistence.forget_value(src, /decl/persistence_handler/filth) . = ..() diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm index e7f36574fa55..7c4a8c775cb6 100644 --- a/code/game/objects/effects/decals/decal.dm +++ b/code/game/objects/effects/decals/decal.dm @@ -1,5 +1,6 @@ /obj/effect/decal layer = DECAL_LAYER + var/age = 0 /obj/effect/decal/fall_damage() return 0 @@ -11,4 +12,4 @@ . = !throwing ? ..() : FALSE /obj/effect/decal/get_examine_prefix() - return null \ No newline at end of file + return null diff --git a/code/game/objects/effects/decals/decal_serde.dm b/code/game/objects/effects/decals/decal_serde.dm new file mode 100644 index 000000000000..d598f384dcc4 --- /dev/null +++ b/code/game/objects/effects/decals/decal_serde.dm @@ -0,0 +1,10 @@ +/obj/effect/decal/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(age, /obj/effect/decal) + +/obj/effect/decal/Deserialize(list/instance_map) + . = ..() + age++ + +/obj/effect/decal/ShouldSerialize(_age) + return simulated && (isnull(_age) || age < _age) diff --git a/code/game/objects/effects/dirty_floor.dm b/code/game/objects/effects/dirty_floor.dm index 8c2df8b9f8b0..833ddaf2b0a4 100644 --- a/code/game/objects/effects/dirty_floor.dm +++ b/code/game/objects/effects/dirty_floor.dm @@ -5,10 +5,14 @@ icon = 'icons/effects/effects.dmi' icon_state = "dirt" mouse_opacity = MOUSE_OPACITY_UNCLICKABLE - persistent = TRUE + use_legacy_persistence = TRUE alpha = 0 var/dirt_amount = 0 +/obj/effect/decal/cleanable/dirt/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(dirt_amount, /obj/effect/decal/cleanable/dirt) + /obj/effect/decal/cleanable/dirt/lava_act() qdel(src) return TRUE @@ -21,10 +25,14 @@ /obj/effect/decal/cleanable/dirt/visible dirt_amount = 60 - persistent = FALSE // This is a subtype for mapping. + use_legacy_persistence = FALSE // This is a subtype for mapping. /obj/effect/decal/cleanable/dirt/Initialize() . = ..() + for(var/obj/effect/decal/cleanable/dirt/other in loc) + if(other != src) + other.dirt_amount += dirt_amount + return INITIALIZE_HINT_QDEL verbs.Cut() update_icon() diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index 52c615b4daeb..1bf090caf26e 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -280,6 +280,59 @@ steam.start() -- spawns the effect ADJ_STATUS(M, STAT_ASLEEP, 1) M.cough() +///////////////////////////////////////////// +// 'Elemental' smoke +///////////////////////////////////////////// +/obj/effect/effect/smoke/elemental + name = "cloud" + desc = "A cloud of some kind that seems really generic and boring." + opacity = FALSE + abstract_type = /obj/effect/effect/smoke/elemental + var/strength = 5 // How much damage to do inside each affect() + +/obj/effect/effect/smoke/elemental/Initialize() + START_PROCESSING(SSobj, src) + return ..() + +/obj/effect/effect/smoke/elemental/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/effect/smoke/elemental/Move(atom/old_loc, direction, forced = FALSE) + . = ..() + if(.) + for(var/mob/living/victim in range(1, src)) + affect(victim) + +/obj/effect/effect/smoke/elemental/Process() + for(var/mob/living/victim in range(1, src)) + affect(victim) + +/obj/effect/effect/smoke/elemental/proc/affect(mob/living/victim) + return + +/obj/effect/effect/smoke/elemental/fire + name = "burning cloud" + desc = "A cloud of something that is on fire." + color = COLOR_ORANGE + light_color = "#ff0000" + light_range = 2 + light_power = 5 + +/obj/effect/effect/smoke/elemental/fire/affect(mob/living/victim) + victim.take_damage(strength, BURN) + victim.ignite_fire() + +/obj/effect/effect/smoke/elemental/mist + name = "misty cloud" + desc = "A cloud filled with water vapor." + color = "#ccffff" + alpha = 128 + strength = 1 + +/obj/effect/effect/smoke/elemental/mist/affect(mob/living/victim) + victim.extinguish_fire() + ///////////////////////////////////////////// // Mustard Gas ///////////////////////////////////////////// @@ -358,6 +411,11 @@ steam.start() -- spawns the effect /datum/effect/effect/system/smoke_spread/sleepy smoke_type = /obj/effect/effect/smoke/sleepy +/datum/effect/effect/system/smoke_spread/fire + smoke_type = /obj/effect/effect/smoke/elemental/fire + +/datum/effect/effect/system/smoke_spread/mist + smoke_type = /obj/effect/effect/smoke/elemental/mist /datum/effect/effect/system/smoke_spread/mustard smoke_type = /obj/effect/effect/smoke/mustard diff --git a/code/game/objects/effects/map_effect/_map_effect.dm b/code/game/objects/effects/map_effect/_map_effect.dm new file mode 100644 index 000000000000..50cad4fa38c5 --- /dev/null +++ b/code/game/objects/effects/map_effect/_map_effect.dm @@ -0,0 +1,31 @@ +/obj/abstract/map_effect + icon = 'icons/effects/map_effects.dmi' + + // Below vars concern check_for_player_proximity() and is used to not waste effort if nobody is around to appreciate the effects. + /// If true, the game will not try to suppress this from firing if nobody is around to see it. + var/always_run = FALSE + /// How many tiles a mob with a client must be for this to run. + var/proximity_needed = 12 + /// If true, ghosts won't satisfy the above requirement. + var/ignore_ghosts = FALSE + /// If true, AFK people (5 minutes) won't satisfy it as well. + var/ignore_afk = TRUE + /// How long until we check for players again. + var/retry_delay = 5 SECONDS + /// Next time we're going to do ACTUAL WORK + var/next_attempt = 0 + +// Helper proc to optimize the use of effects by making sure they do not run if nobody is around to perceive it. +/obj/abstract/map_effect/proc/check_for_player_proximity(radius = 12, ignore_ghosts = FALSE, ignore_afk = TRUE) + if(!z) + return FALSE + for(var/mob/player as anything in player_list) + if(player.z != z) + continue + if(ignore_ghosts && isobserver(player)) + continue + if(ignore_afk && player.client && player.client.is_afk(5 MINUTES)) + continue + if(get_dist(player, src) <= radius) + return TRUE + return FALSE diff --git a/code/game/objects/effects/map_effect/interval/_interval.dm b/code/game/objects/effects/map_effect/interval/_interval.dm new file mode 100644 index 000000000000..ba898570c761 --- /dev/null +++ b/code/game/objects/effects/map_effect/interval/_interval.dm @@ -0,0 +1,32 @@ +// Base type for effects that run on variable intervals. +/obj/abstract/map_effect/interval + var/interval_lower_bound = 5 SECONDS // Lower number for how often the map_effect will trigger. + var/interval_upper_bound = 5 SECONDS // Higher number for above. + +/obj/abstract/map_effect/interval/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/abstract/map_effect/interval/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +// Override this for the specific thing to do. +/obj/abstract/map_effect/interval/proc/trigger_map_effect() + return + +// Handles the delay and making sure it doesn't run when it would be bad. +/obj/abstract/map_effect/interval/Process() + + //Not yet! + if(world.time < next_attempt) + return + + // Check to see if we're useful first. + if(!always_run && !check_for_player_proximity(proximity_needed, ignore_ghosts, ignore_afk)) + next_attempt = world.time + retry_delay + return + + // Hey there's someone nearby. + next_attempt = world.time + rand(interval_lower_bound, interval_upper_bound) + trigger_map_effect() diff --git a/code/game/objects/effects/map_effect/interval/effect_emitter.dm b/code/game/objects/effects/map_effect/interval/effect_emitter.dm new file mode 100644 index 000000000000..0daf63388e09 --- /dev/null +++ b/code/game/objects/effects/map_effect/interval/effect_emitter.dm @@ -0,0 +1,63 @@ +/obj/abstract/map_effect/interval/effect_emitter + /// Effect system attached. Set to type to create in Initialize(). + var/datum/effect/effect/system/effect_system = null + /// How many effect objects to create on each interval. Note that there's a hard cap on certain effect_systems. + var/effect_amount = 10 + /// If true, effects only move in cardinal directions. + var/effect_cardinals_only = FALSE + /// If set, effects emitted will always move in this direction. + var/effect_forced_dir + +/obj/abstract/map_effect/interval/effect_emitter/Initialize() + if(ispath(effect_system)) + effect_system = new effect_system() + if(!istype(effect_system)) + return INITIALIZE_HINT_QDEL + effect_system.attach(src) + return ..() + +/obj/abstract/map_effect/interval/effect_emitter/interval/Destroy() + QDEL_NULL(effect_system) + return ..() + + +/obj/abstract/map_effect/interval/effect_emitter/trigger_map_effect() + to_world("[type]: effect firing") + if(istype(effect_system) && !QDELETED(src)) + effect_system.set_up(effect_amount, effect_cardinals_only, src.loc, effect_forced_dir) + effect_system.start() + +// Makes sparks. +/obj/abstract/map_effect/interval/effect_emitter/sparks + name = "spark emitter" + icon_state = "spark_emitter" + effect_system = /datum/effect/effect/system/spark_spread + interval_lower_bound = 3 SECONDS + interval_upper_bound = 7 SECONDS + +// Makes ""steam"" that looks like fire extinguisher water except it does nothing. +/obj/abstract/map_effect/interval/effect_emitter/steam + name = "steam emitter" + icon_state = "smoke_emitter" + effect_system = /datum/effect/effect/system/steam_spread + +// Creates smoke clouds every so often. +/obj/abstract/map_effect/interval/effect_emitter/smoke + name = "smoke emitter" + icon_state = "smoke_emitter" + effect_system = /datum/effect/effect/system/smoke_spread + interval_lower_bound = 1 SECOND + interval_upper_bound = 1 SECOND + effect_amount = 2 + +/obj/abstract/map_effect/interval/effect_emitter/smoke/mist + name = "mist smoke emitter" + effect_system = /datum/effect/effect/system/smoke_spread/mist + +/obj/abstract/map_effect/interval/effect_emitter/smoke/bad + name = "bad smoke emitter" + effect_system = /datum/effect/effect/system/smoke_spread/bad + +/obj/abstract/map_effect/interval/effect_emitter/smoke/fire + name = "fire smoke emitter" + effect_system = /datum/effect/effect/system/smoke_spread/fire diff --git a/code/game/objects/effects/map_effect/interval/screen_shaker.dm b/code/game/objects/effects/map_effect/interval/screen_shaker.dm new file mode 100644 index 000000000000..724bb7a256df --- /dev/null +++ b/code/game/objects/effects/map_effect/interval/screen_shaker.dm @@ -0,0 +1,17 @@ +/obj/abstract/map_effect/interval/screen_shaker + name = "screen shaker" + icon_state = "screen_shaker" + interval_lower_bound = 1 SECOND + interval_upper_bound = 2 SECONDS + + /// How far the shaking effect extends to. By default it is one screen length. + var/shake_radius = 7 + /// How long the shaking lasts. + var/shake_duration = 2 + /// How much it shakes. + var/shake_strength = 1 + +/obj/abstract/map_effect/interval/screen_shaker/trigger_map_effect() + for(var/mob/player in player_list) + if(player.z == z && get_dist(src, player) <= shake_radius) + shake_camera(player, shake_duration, shake_strength) diff --git a/code/game/objects/effects/map_effect/interval/sound_emitter.dm b/code/game/objects/effects/map_effect/interval/sound_emitter.dm new file mode 100644 index 000000000000..058434882b32 --- /dev/null +++ b/code/game/objects/effects/map_effect/interval/sound_emitter.dm @@ -0,0 +1,51 @@ +/obj/abstract/map_effect/interval/sound_emitter +// Plays a sound at its location every so often. + name = "sound emitter" + icon_state = "sound_emitter" + /// How loud the sound is. 0 is silent, and 100 is loudest. Please be reasonable with the volume. Note that things like vacuum may affect the volume heard by other mobs. + var/sound_volume = 50 + /// If the sound will sound somewhat different each time. If a specific frequency is desired, sound_frequency must also be set. + var/sound_frequency_variance = TRUE + /// Set to make sounds heard from farther away than normal. + var/sound_extra_range = 0 + /// Within the 'fallout distance', the sound stays at the same volume, otherwise it attenuates. Higher numbers make the sound fade out more slowly with distance. + var/sound_fallout = 0 + /// If true, sounds will not be distorted due to the current area's 'sound environment'. It DOES NOT make the sound have a constant volume or z-level wide range, despite the misleading name. + var/sound_global = FALSE + /// Sets a specific custom frequency. sound_frequency_variance must be true as well. If sound_frequency is null, but sound_frequency_variance is true, a semi-random frequency will be chosen to the sound each time. + var/sound_frequency = null + /// Whether or not clients with the ambience preference disabled will hear this sound. + var/sound_is_ambience = TRUE + /// If false, walls will completely muffle the sound. + var/sound_ignore_walls = TRUE + +/obj/abstract/map_effect/interval/sound_emitter/proc/get_sounds_to_play() + return + +/obj/abstract/map_effect/interval/sound_emitter/trigger_map_effect() + playsound( + src, + pick(get_sounds_to_play()), + sound_volume, + sound_frequency_variance, + sound_extra_range, + sound_fallout, + sound_global, + sound_frequency, + sound_is_ambience, + sound_ignore_walls + ) + ..() + +/obj/abstract/map_effect/interval/sound_emitter/footsteps_wood + interval_lower_bound = 5 SECONDS + interval_upper_bound = 30 SECONDS + +/obj/abstract/map_effect/interval/sound_emitter/footsteps_wood/get_sounds_to_play() + var/static/list/sounds_to_play = list( + 'sound/effects/footstep/wood1.ogg', + 'sound/effects/footstep/wood5.ogg', + 'sound/effects/footstep/floor1.ogg', + 'sound/effects/footstep/floor5.ogg' + ) + return sounds_to_play diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm deleted file mode 100644 index ac59e8322d4a..000000000000 --- a/code/game/objects/effects/mines.dm +++ /dev/null @@ -1,98 +0,0 @@ -/obj/effect/mine - name = "Mine" - desc = "I Better stay away from that thing." - density = TRUE - anchored = TRUE - layer = OBJ_LAYER - icon = 'icons/obj/items/weapon/landmine.dmi' - icon_state = "uglymine" - var/triggerproc = PROC_REF(explode) // the proc that's called when the mine is triggered - var/triggered = 0 - -/obj/effect/mine/Initialize() - . = ..() - icon_state = "uglyminearmed" - -/obj/effect/mine/Crossed(atom/movable/AM) - if(!isobserver(AM)) - Bumped(AM) - -/obj/effect/mine/Bumped(mob/M) - - if(triggered) return - - if(ishuman(M)) - visible_message(SPAN_DANGER("\The [M] triggered \the [src]!")) - triggered = 1 - call(src,triggerproc)(M) - -/obj/effect/mine/proc/triggerrad(obj) - spark_at(src, cardinal_only = TRUE) - if(ismob(obj)) - var/mob/victim = obj - victim.radiation += 50 - if(ismob(obj)) - var/mob/mob = obj - mob.add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/disability))) - qdel(src) - -/obj/effect/mine/proc/triggerstun(obj) - if(ismob(obj)) - var/mob/M = obj - SET_STATUS_MAX(M, STAT_STUN, 30) - spark_at(src, cardinal_only = TRUE) - qdel(src) - -/obj/effect/mine/proc/triggern2o(obj) - //example: n2o triggerproc - //note: im lazy - - for (var/turf/target in range(1,src)) - if(target.simulated && !target.blocks_air) - target.assume_gas(/decl/material/gas/nitrous_oxide, 30) - - qdel(src) - -/obj/effect/mine/proc/triggerflame(obj) - for (var/turf/target in range(1,src)) - if(target.simulated && !target.blocks_air) - target.assume_gas(/decl/material/gas/hydrogen, 30) - target.hotspot_expose(1000, CELL_VOLUME) - - qdel(src) - -/obj/effect/mine/proc/triggerkick(obj) - spark_at(src, cardinal_only = TRUE) - if(ismob(obj)) - var/mob/victim = obj - qdel(victim.client) - qdel(src) - -/obj/effect/mine/proc/explode(obj) - explosion(loc, 0, 1, 2, 3) - qdel(src) - -/obj/effect/mine/dnascramble - name = "Radiation Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerrad) - -/obj/effect/mine/flame - name = "Incendiary Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerflame) - -/obj/effect/mine/kick - name = "Kick Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerkick) - -/obj/effect/mine/n2o - name = "N2O Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggern2o) - -/obj/effect/mine/stun - name = "Stun Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerstun) diff --git a/code/game/objects/effects/mines/_mine.dm b/code/game/objects/effects/mines/_mine.dm new file mode 100644 index 000000000000..a377a9d82869 --- /dev/null +++ b/code/game/objects/effects/mines/_mine.dm @@ -0,0 +1,202 @@ +/obj/item/mine + name = "mine" + desc = "A small landmine." + density = FALSE + anchored = FALSE + icon = 'icons/obj/mine.dmi' + icon_state = "mine" + + var/actual_name + var/actual_desc + var/actual_icon_state + var/hidden_alpha = 255 + + var/panel_open = FALSE + var/armed = FALSE + var/triggering = FALSE + var/datum/mine_payload/payload = /datum/mine_payload/explosive + +/obj/item/mine/Initialize() + . = ..() + if(ispath(payload)) + payload = new payload + register_dangerous_to_step() + + // We store and hide our appearance if we're armed, to avoid people gaming mines via desc. + actual_name = name + actual_desc = desc + actual_icon_state = icon_state + update_icon() + +/obj/item/mine/Destroy() + if(istype(payload)) + QDEL_NULL(payload) + unregister_dangerous_to_step() + return ..() + +/obj/item/mine/on_update_icon() + . = ..() + alpha = initial(alpha) + cut_overlays() + if(panel_open) + add_overlay("[icon_state]_open") + else if(armed) + add_overlay("[icon_state]_armed") + alpha = hidden_alpha + else + add_overlay("[icon_state]_safe") + +/obj/item/mine/attack_self(mob/user) // You do not want to move or throw a land mine while priming it... Explosives + Sudden Movement = Bad Times + if(armed) + to_chat(user, SPAN_WARNING("\The [src] is already armed!")) + return TRUE + add_fingerprint(user) + msg_admin_attack("[key_name_admin(user)] armed \the [src]") + user.visible_message( + SPAN_DANGER("\The [user] starts arming \the [src]."), + SPAN_DANGER("You start arming \the [src]. Hold still!") + ) + + if(user.do_skilled(10 SECONDS, SKILL_DEVICES, src)) + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + prime(user) + else if(prob(user.skill_fail_chance(SKILL_DEVICES, 50, SKILL_ADEPT))) + visible_message( + SPAN_DANGER("\The [user] accidentally triggers \the [src]!"), + SPAN_DANGER("You accidentally trigger \the [src]!") + ) + prime(user) + trigger_payload(user) + else + to_chat(user, SPAN_WARNING("You fumble with \the [src], but thankfully manage not to set it off prematurely.")) + return TRUE + +// debug proc, replace with proper disarm minigame +/obj/item/mine/proc/disarm() + armed = FALSE + triggering = FALSE + anchored = FALSE + name = actual_name + desc = actual_desc + icon_state = actual_icon_state + hidden_alpha = 255 + update_icon() + +/obj/item/mine/attack_hand(mob/living/user) + if(armed) + trigger_payload() + return TRUE + return ..() + +/obj/item/mine/attackby(obj/item/W, mob/living/user) + + if(IS_SCREWDRIVER(W)) + if(W.do_tool_interaction(TOOL_SCREWDRIVER, user, src, 15 SECONDS, start_message = "carefully adjusting \the [src]'s casing", check_skill = SKILL_DEVICES)) + panel_open = !panel_open + visible_message(SPAN_NOTICE("\The [user] carefully [(panel_open ? "opens" : "closes")] the casing of \the [src].")) + update_icon() + else if(armed) + if(prob(user.skill_fail_chance(SKILL_DEVICES, 75, SKILL_PROF))) + to_chat(user, SPAN_DANGER("You set off \the [src]!")) + trigger_payload(user) + else + to_chat(user, SPAN_WARNING("You fumble with \the [src], but thankfully manage not to set it off prematurely.")) + return TRUE + + if(armed) + if(panel_open && IS_WIRECUTTER(W)) + if(W.do_tool_interaction(TOOL_WIRECUTTERS, user, src, 30 SECONDS, start_message = "painstakingly disarming \the [src]", check_skill = SKILL_DEVICES)) + visible_message(SPAN_NOTICE("\The [user] disarms \the [src]!")) + disarm() + return TRUE + if(armed) // checking again in case the do_after() stacks + if(prob(user.skill_fail_chance(SKILL_DEVICES, 75, SKILL_PROF))) + to_chat(user, SPAN_DANGER("You set off \the [src]!")) + trigger_payload(user) + else + to_chat(user, SPAN_WARNING("You fumble with \the [src], but thankfully manage not to set it off prematurely.")) + return TRUE + + return ..() + +/obj/item/mine/proc/prime(mob/user) + + if(armed) + return + + if(user) + visible_message(SPAN_NOTICE("\The [src] beeps as the priming sequence completes.")) + user.drop_from_inventory(src, get_turf(user)) + add_fingerprint(user) + + anchored = TRUE + armed = TRUE + + if(istype(loc, /turf/floor) && prob(65)) + var/turf/floor/floor = loc + var/decl/flooring/flooring = floor.get_topmost_flooring() + if(flooring.can_conceal_hazards) + hidden_alpha = pick(50, 90, 120) + + name = "mine" + desc = "A small landmine." + icon_state = "mine" + update_icon() + +/obj/item/mine/forceMove() + var/turf/old_turf = get_turf(loc) + . = ..() + if(.) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf?.unregister_dangerous_object(src) + new_turf?.register_dangerous_object(src) + +/obj/item/mine/Move() + var/turf/old_turf = get_turf(loc) + . = ..() + if(.) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf?.unregister_dangerous_object(src) + new_turf?.register_dangerous_object(src) + +/obj/item/mine/proc/trigger_payload(var/mob/living/M) + if(!triggering && payload && armed) + triggering = TRUE + if(ismob(loc)) + var/mob/holder = loc + holder.drop_from_inventory(src) + visible_message("\The [src] goes off!") + payload.trigger_payload(src, M) + disarm() // the mine can be reused if the payload doesn't destroy it. + return TRUE + return FALSE + +/obj/item/mine/bullet_act() + if(prob(50)) + trigger_payload() + if(!QDELETED(src)) + ..() + +/obj/item/mine/explosion_act(severity) + if(severity <= 2 || prob(50)) + trigger_payload() + if(!QDELETED(src)) + . = ..() + +/obj/item/mine/Crossed(atom/movable/AM) + . = ..() + if(istype(AM) && !AM.is_incorporeal()) + Bumped(AM) + +/obj/item/mine/Bumped(atom/movable/AM) + . = ..() + if(armed && !QDELETED(src) && !is_safe_to_step(AM)) + trigger_payload(AM) + +// This tells AI mobs to not be dumb and step on mines willingly. +/obj/item/mine/is_safe_to_step(mob/living/stepper) + if(!armed) + return TRUE + return !armed || stepper.can_overcome_gravity() diff --git a/code/game/objects/effects/mines/_mine_payload.dm b/code/game/objects/effects/mines/_mine_payload.dm new file mode 100644 index 000000000000..99139be493d7 --- /dev/null +++ b/code/game/objects/effects/mines/_mine_payload.dm @@ -0,0 +1,22 @@ +/datum/mine_payload + var/do_sparks = TRUE + var/destroy_self_on_trigger = TRUE + +/datum/mine_payload/proc/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + if(do_sparks) + var/datum/effect/effect/system/spark_spread/s = new + s.set_up(3, 1, owner) + s.start() + if(destroy_self_on_trigger) + if(!QDELETED(owner)) + QDEL_IN(owner, 1) + else + owner.disarm() // some mines can be reused + +/datum/mine_payload/proc/remove_from_mine() + return + +/datum/mine_payload/explosive/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner] detonates!") + explosion(owner.loc, 0, 2, 3, 4) //land mines are dangerous, folks. diff --git a/code/game/objects/effects/mines/mine_assembly.dm b/code/game/objects/effects/mines/mine_assembly.dm new file mode 100644 index 000000000000..6a40fa0ae30d --- /dev/null +++ b/code/game/objects/effects/mines/mine_assembly.dm @@ -0,0 +1,67 @@ +/obj/item/mine/assembly + name = "mine assembly" + desc = "A small pressure-triggered device. Accepts grenades and tank transfer valves." + payload = null + + var/static/list/accepts_items = list( + /obj/item/transfer_valve = /datum/mine_payload/assembly/tank_transfer_valve, + /obj/item/grenade = /datum/mine_payload/assembly/grenade + ) + +/obj/item/mine/assembly/mapped + armed = TRUE + +/obj/item/mine/assembly/attackby(obj/item/W, mob/living/user) + if(!armed && !triggering) + var/datum/mine_payload/assembly/attached_payload = payload + if(attached_payload?.attached) + if(IS_SCREWDRIVER(W)) + to_chat(user, "You disconnect \the [src]'s [attached_payload.attached.name] and remove it.") + attached_payload.attached.forceMove(get_turf(user)) + payload.remove_from_mine() + QDEL_NULL(payload) + else + for(var/loadtype in accepts_items) + if(istype(W, loadtype)) + user.drop_from_inventory(W) + W.forceMove(src) + var/payload_type = accepts_items[loadtype] + attached_payload = new payload_type + attached_payload.attached = W + payload = attached_payload + return TRUE + return ..() + +/datum/mine_payload/assembly + var/obj/item/attached + +/datum/mine_payload/assembly/New(var/obj/item/_attaching) + ..() + attached = _attaching + +/datum/mine_payload/assembly/Destroy() + QDEL_NULL(attached) + . = ..() + +/datum/mine_payload/assembly/remove_from_mine() + attached = null + +/datum/mine_payload/assembly/tank_transfer_valve/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(istype(attached, /obj/item/transfer_valve)) + var/obj/item/transfer_valve/ttv = attached + ttv.forceMove(get_turf(owner)) + ttv.toggle_valve() + remove_from_mine() + +/datum/mine_payload/assembly/grenade/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(istype(attached, /obj/item/grenade)) + var/obj/item/grenade/grenade = attached + grenade.forceMove(get_turf(owner)) + if(ismob(trigger)) + var/mob/victim = trigger + if(victim.ckey) + msg_admin_attack("[key_name_admin(victim)] stepped on \a [owner], triggering [grenade]") + grenade.activate() + remove_from_mine() diff --git a/code/game/objects/effects/mines/mine_emp.dm b/code/game/objects/effects/mines/mine_emp.dm new file mode 100644 index 000000000000..5f8a54ad12f8 --- /dev/null +++ b/code/game/objects/effects/mines/mine_emp.dm @@ -0,0 +1,12 @@ +/obj/item/mine/emp + name = "\improper EMP mine" + desc = "A small explosive mine with a lightning bolt symbol on the side." + payload = /datum/mine_payload/emp + +/obj/item/mine/emp/mapped + armed = TRUE + +/datum/mine_payload/emp/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner] flashes violently before disintegrating!") + empulse(owner.loc, 2, 4, 7, 10, 1) // As strong as an EMP grenade diff --git a/code/game/objects/effects/mines/mine_frag.dm b/code/game/objects/effects/mines/mine_frag.dm new file mode 100644 index 000000000000..de11402ca5a2 --- /dev/null +++ b/code/game/objects/effects/mines/mine_frag.dm @@ -0,0 +1,23 @@ +/obj/item/mine/frag + name = "fragmentation mine" + desc = "A small explosive mine with 'FRAG' and a grenade symbol on the side." + payload = /datum/mine_payload/frag + +/obj/item/mine/frag/mapped + armed = TRUE + +/datum/mine_payload/frag + var/fragment_types = list(/obj/item/projectile/bullet/pellet/fragment) + var/num_fragments = 20 //total number of fragments produced by the grenade + //The radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern + var/spread_range = 7 + var/explosion_size = 3 + +/datum/mine_payload/frag/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner] detonates!") + var/turf/O = get_turf(owner) + if(O) + owner.fragmentate(O, num_fragments, spread_range, fragment_types) + if(explosion_size) + explosion(O, -1, -1, round(explosion_size/2), explosion_size, FALSE) diff --git a/code/game/objects/effects/mines/mine_incendiary.dm b/code/game/objects/effects/mines/mine_incendiary.dm new file mode 100644 index 000000000000..f8b8a49ab2d6 --- /dev/null +++ b/code/game/objects/effects/mines/mine_incendiary.dm @@ -0,0 +1,16 @@ +/obj/item/mine/incendiary + name = "incendiary mine" + desc = "A small explosive mine with a fire symbol on the side." + payload = /datum/mine_payload/incendiary + +/obj/item/mine/incendiary/mapped + armed = TRUE + +/datum/mine_payload/incendiary/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + for(var/turf/floor/target in range(1, owner)) + if(!target.blocks_air) + target.assume_gas(/decl/material/gas/hydrogen, 10) + target.assume_gas(/decl/material/gas/oxygen, 5) + target.hotspot_expose(1000, CELL_VOLUME) + owner.visible_message("\The [owner] spews a cloud of flaming gas!") diff --git a/code/game/objects/effects/mines/mine_kick.dm b/code/game/objects/effects/mines/mine_kick.dm new file mode 100644 index 000000000000..ab38a2a2fb31 --- /dev/null +++ b/code/game/objects/effects/mines/mine_kick.dm @@ -0,0 +1,17 @@ +/obj/item/mine/kick + name = "kick mine" + desc = "Concentrated war crimes. Handle with care." + payload = /datum/mine_payload/kick + +/obj/item/mine/kick/mapped + armed = TRUE + +/datum/mine_payload/kick/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(isexosuit(trigger)) + var/mob/living/exosuit/mech = trigger + for(var/mob/pilot in mech.pilots) + qdel(pilot.client) + if(ismob(trigger)) + var/mob/M = trigger + qdel(M.client) diff --git a/code/game/objects/effects/mines/mine_napalm.dm b/code/game/objects/effects/mines/mine_napalm.dm new file mode 100644 index 000000000000..6bceace3f0e6 --- /dev/null +++ b/code/game/objects/effects/mines/mine_napalm.dm @@ -0,0 +1,15 @@ +/obj/item/mine/napalm + name = "napalm mine" + desc = "A small explosive mine with a fire symbol on the side." + payload = /datum/mine_payload/napalm + +/obj/item/mine/napalm/mapped + armed = TRUE + +/datum/mine_payload/napalm/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(isliving(trigger)) + var/mob/living/M = trigger + M.adjust_fire_intensity(5) + M.fire_act() + owner.visible_message(SPAN_DANGER("\The [owner] bursts into flames!")) diff --git a/code/game/objects/effects/mines/mine_radiation.dm b/code/game/objects/effects/mines/mine_radiation.dm new file mode 100644 index 000000000000..08b00d2a6594 --- /dev/null +++ b/code/game/objects/effects/mines/mine_radiation.dm @@ -0,0 +1,14 @@ +/obj/item/mine/radiation + name = "radiation mine" + desc = "A small explosive mine with a radiation symbol on the side." + payload = /datum/mine_payload/radiation + +/obj/item/mine/radiation/mapped + armed = TRUE + +/datum/mine_payload/radiation/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(isliving(trigger)) + var/mob/living/victim = trigger + victim.apply_random_mutation(50) + owner.visible_message(SPAN_DANGER("\The [owner] flashes violently before disintegrating!")) diff --git a/code/game/objects/effects/mines/mine_sleeping.dm b/code/game/objects/effects/mines/mine_sleeping.dm new file mode 100644 index 000000000000..b6d5ad9a0dee --- /dev/null +++ b/code/game/objects/effects/mines/mine_sleeping.dm @@ -0,0 +1,14 @@ +/obj/item/mine/sleeping + name = "nitrous oxide mine" + desc = "A small explosive mine with three Z's on the side." + payload = /datum/mine_payload/sleeping + +/obj/item/mine/sleeping/mapped + armed = TRUE + +/datum/mine_payload/sleeping/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + for (var/turf/floor/target in range(1, owner)) + if(!target.blocks_air) + target.assume_gas(/decl/material/gas/nitrous_oxide, 30) + owner.visible_message("\The [owner] sprays a cloud of gas!") diff --git a/code/game/objects/effects/mines/mine_stun.dm b/code/game/objects/effects/mines/mine_stun.dm new file mode 100644 index 000000000000..26b5704b304b --- /dev/null +++ b/code/game/objects/effects/mines/mine_stun.dm @@ -0,0 +1,14 @@ +/obj/item/mine/stun + name = "stun mine" + desc = "A small explosive mine with a lightning bolt symbol on the side." + payload = /datum/mine_payload/stun + +/obj/item/mine/stun/mapped + armed = TRUE + +/datum/mine_payload/stun/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(ismob(trigger)) + var/mob/M = trigger + SET_STATUS_MAX(M, STAT_STUN, 30) + owner.visible_message("\The [owner] flashes violently before disintegrating!") diff --git a/code/game/objects/effects/mines/mine_training.dm b/code/game/objects/effects/mines/mine_training.dm new file mode 100644 index 000000000000..13b23f31945c --- /dev/null +++ b/code/game/objects/effects/mines/mine_training.dm @@ -0,0 +1,15 @@ +/obj/item/mine/training + name = "training mine" + desc = "A mine with its payload removed, for EOD training and demonstrations." + payload = /datum/mine_payload/training + +/obj/item/mine/training/mapped + armed = TRUE + +/datum/mine_payload/training + do_sparks = FALSE + destroy_self_on_trigger = FALSE + +/datum/mine_payload/training/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner]'s light flashes rapidly as it 'explodes'.") diff --git a/code/game/objects/effects/temporary.dm b/code/game/objects/effects/temporary.dm index b56d38275f17..c5efa1c7e916 100644 --- a/code/game/objects/effects/temporary.dm +++ b/code/game/objects/effects/temporary.dm @@ -1,6 +1,7 @@ //temporary visual effects /obj/effect/temp_visual icon_state = "nothing" + icon = 'icons/effects/effects.dmi' anchored = TRUE layer = ABOVE_HUMAN_LAYER mouse_opacity = MOUSE_OPACITY_UNCLICKABLE @@ -14,12 +15,10 @@ QDEL_IN(src, duration) /obj/effect/temp_visual/emp_burst - icon = 'icons/effects/effects.dmi' icon_state = "empdisable" /obj/effect/temp_visual/emppulse name = "electromagnetic pulse" - icon = 'icons/effects/effects.dmi' icon_state = "emppulse" duration = 2 SECONDS @@ -49,4 +48,52 @@ target_pixel_x = 16 if(set_dir & WEST) target_pixel_x = -16 - animate(src, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = duration) \ No newline at end of file + animate(src, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = duration) + +/obj/effect/temp_visual/impact_effect + plane = ABOVE_LIGHTING_PLANE + layer = ABOVE_LIGHTING_LAYER // So they're visible even in a shootout in maint. + duration = 5 + icon_state = "impact_bullet" + icon = 'icons/effects/impact_effects.dmi' + +/obj/effect/temp_visual/impact_effect/Initialize(mapload, obj/item/projectile/P, _x, _y) + default_pixel_x = _x + default_pixel_y = _y + pixel_x = default_pixel_x + pixel_y = default_pixel_y + . = ..() + +/obj/effect/temp_visual/impact_effect/red_laser + icon_state = "impact_laser" + duration = 4 + +/obj/effect/temp_visual/impact_effect/blue_laser + icon_state = "impact_laser_blue" + duration = 4 + +/obj/effect/temp_visual/impact_effect/green_laser + icon_state = "impact_laser_green" + duration = 4 + +/obj/effect/temp_visual/impact_effect/purple_laser + icon_state = "impact_laser_purple" + duration = 4 + +// Colors itself based on the projectile. +// Checks light_color and color. +/obj/effect/temp_visual/impact_effect/monochrome_laser + icon_state = "impact_laser_monochrome" + duration = 4 + +/obj/effect/temp_visual/impact_effect/monochrome_laser/Initialize(mapload, obj/item/projectile/P, x, y) + if(istype(P)) + if(P.light_color) + color = P.light_color + else if(P.color) + color = P.color + return ..() + +/obj/effect/temp_visual/impact_effect/ion + icon_state = "shieldsparkles" + duration = 6 diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index f548bcbfada6..d9164b23c5e2 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -726,6 +726,9 @@ if(slot == slot_in_backpack_str) var/obj/item/back = user.get_equipped_item(slot_back_str) return back?.storage?.can_be_inserted(src, user, TRUE) + if(slot == slot_in_wallet_str) + var/obj/item/wallet = user.get_equipped_item(slot_wear_id_str) + return wallet?.storage?.can_be_inserted(src, user, TRUE) var/datum/inventory_slot/inv_slot = user.get_inventory_slot_datum(slot) if(!inv_slot) diff --git a/code/game/objects/items/_item_serde.dm b/code/game/objects/items/_item_serde.dm new file mode 100644 index 000000000000..c4aba6ca9327 --- /dev/null +++ b/code/game/objects/items/_item_serde.dm @@ -0,0 +1,3 @@ +/obj/item/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(paint_verb, /obj/item) diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index 8d9e6d3d06b9..2d2e4cddf964 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -53,7 +53,7 @@ var/turf/T = get_turf(src) if(istype(T) && length(global.using_map.overmap_ids)) - var/obj/effect/overmap/visitable/sector/S = global.overmap_sectors[num2text(T.z)] + var/obj/effect/overmap/visitable/sector/S = global.overmap_sectors[T.z] if(!S) // The blueprints are useless now, but keep them around for fluff. desc = "Some dusty old blueprints. The markings are old, and seem entirely irrelevant for your whereabouts." return FALSE @@ -75,14 +75,14 @@ icon_state = "blueprints2" /obj/item/blueprints/outpost/attack_self(mob/user) - var/obj/effect/overmap/visitable/sector/S = global.overmap_sectors[num2text(get_z(user))] + var/obj/effect/overmap/visitable/sector/S = global.overmap_sectors[get_z(user)] area_prefix = S.name . = ..() /obj/item/blueprints/outpost/set_valid_z_levels() var/turf/T = get_turf(src) if(istype(T) && length(global.using_map.overmap_ids)) - var/obj/effect/overmap/visitable/sector/S = global.overmap_sectors[num2text(T.z)] + var/obj/effect/overmap/visitable/sector/S = global.overmap_sectors[T.z] if(istype(S)) T = locate(1, 1, S.z) var/area/overmap/map = T && get_area(T) @@ -100,8 +100,8 @@ /obj/item/blueprints/shuttle/set_valid_z_levels() var/turf/T = get_turf(src) - if(istype(T) && length(global.using_map.overmap_ids) && global.overmap_sectors[num2text(T.z)]) - var/obj/effect/overmap/visitable/ship/landable/S = global.overmap_sectors[num2text(T.z)] + if(istype(T) && length(global.using_map.overmap_ids) && global.overmap_sectors[T.z]) + var/obj/effect/overmap/visitable/ship/landable/S = global.overmap_sectors[T.z] if(isnull(shuttle_name)) shuttle_name = S.shuttle update_linked_name(S, null, S.name) diff --git a/code/game/objects/items/books/_book_serde.dm b/code/game/objects/items/books/_book_serde.dm new file mode 100644 index 000000000000..25251936e1d9 --- /dev/null +++ b/code/game/objects/items/books/_book_serde.dm @@ -0,0 +1,31 @@ +/obj/item/book/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(last_modified_ckey, /obj/item/book) + SERIALIZE_IF_MODIFIED(dat, /obj/item/book) + SERIALIZE_IF_MODIFIED(title, /obj/item/book) + SERIALIZE_IF_MODIFIED(author, /obj/item/book) + SERIALIZE_IF_MODIFIED(icon_state, /atom) + +/obj/item/book/Deserialize() + . = ..() + SSpersistence.track_value(src, /decl/persistence_handler/book) + +/obj/item/book/GetPossiblySerializableInstances() + . = ..() + if(istype(loc, /obj/structure/bookcase)) + LAZYDISTINCTADD(., loc) + +/obj/item/book/Deserialize(list/instance_map) + ..() + return SERDE_HINT_POSTINIT + +/obj/item/book/DeserializePostInit(list/instance_map) + . = ..() + var/area/area = get_area(src) + if(!area || (area.area_flags & AREA_FLAG_NO_LEGACY_PERSISTENCE)) + forceMove(null) + if(isnull(loc)) + if(length(global.station_bookcases)) + forceMove(pick(global.station_bookcases)) + else + forceMove(get_random_spawn_turf(SPAWN_FLAG_PERSISTENCE_CAN_SPAWN)) diff --git a/code/game/objects/items/candelabra.dm b/code/game/objects/items/candelabra.dm index 1b123c42034f..bd5fcd533aff 100644 --- a/code/game/objects/items/candelabra.dm +++ b/code/game/objects/items/candelabra.dm @@ -49,3 +49,6 @@ if(i > length(candles_storage.candle_offsets)) break compile_overlays() + +/obj/item/candelabra/infinite/WillContain() + return list(/obj/item/flame/candle/infinite = 3) diff --git a/code/game/objects/items/circuitboards/machinery/unary_atmos.dm b/code/game/objects/items/circuitboards/machinery/unary_atmos.dm index bf81f6b51ee4..6ac21c7a7846 100644 --- a/code/game/objects/items/circuitboards/machinery/unary_atmos.dm +++ b/code/game/objects/items/circuitboards/machinery/unary_atmos.dm @@ -11,7 +11,7 @@ /obj/item/stock_parts/circuitboard/unary_atmos/heater name = "circuitboard (gas heating system)" - build_path = /obj/machinery/atmospherics/unary/heater + build_path = /obj/machinery/atmospherics/unary/temperature/heater origin_tech = @'{"powerstorage":2,"engineering":1}' req_components = list( /obj/item/stack/cable_coil = 5, @@ -20,7 +20,7 @@ /obj/item/stock_parts/circuitboard/unary_atmos/cooler name = "circuitboard (gas cooling system)" - build_path = /obj/machinery/atmospherics/unary/freezer + build_path = /obj/machinery/atmospherics/unary/temperature/freezer origin_tech = @'{"magnets":2,"engineering":2}' req_components = list( /obj/item/stack/cable_coil = 2, diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index d592062c4312..fa29f4316653 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -1,6 +1,7 @@ var/global/list/all_gps_units = list() /obj/item/gps - name = "global coordinate system" + name = "global positioning system" + base_name = "global positioning system" desc = "A handheld relay used to triangulate the approximate coordinates of the device in spacetime." icon = 'icons/obj/items/device/locator.dmi' icon_state = ICON_STATE_WORLD @@ -24,21 +25,36 @@ var/global/list/all_gps_units = list() var/can_hide_signal = FALSE // If it can toggle the above var. var/is_special_gps_marker = FALSE // How the GPS marker should be handled. + var/tag_category // Any special category for this tracker to sit in (used by xenofauna tags) + var/list/tag_categories // Any special categories this tracker should show (used in xenofauna GPS) + var/mob/holder var/is_in_processing_list = FALSE var/list/tracking_devices var/list/showing_tracked_names - var/obj/compass_holder/compass + VAR_PRIVATE/obj/compass_holder/_compass var/list/decals + /obj/item/gps/Initialize() global.all_gps_units += src . = ..() - name = "[initial(name)] ([gps_tag])" events_repository.register(/decl/observ/moved, src, src, PROC_REF(update_holder)) - compass = new(src) + create_compass() update_holder() update_icon() + update_name() + +/obj/item/gps/proc/set_gps_tag(_tag) + _tag = sanitize(_tag) + if(istext(_tag) && length(_tag) > 0 && gps_tag != _tag) + gps_tag = _tag + update_name() + +/obj/item/gps/update_name() + . = ..() + if(gps_tag) + SetName("[base_name] ([gps_tag])") /obj/item/gps/get_examine_strings(mob/user, distance, infix, suffix) . = ..() @@ -60,7 +76,8 @@ var/global/list/all_gps_units = list() if(holder && (force_clear || loc != holder)) moved_event.unregister(holder, src) dir_set_event.unregister(holder, src) - holder.client?.screen -= compass + if(_compass) + holder.client?.screen -= _compass holder = null if(!force_clear && ismob(loc)) @@ -72,16 +89,16 @@ var/global/list/all_gps_units = list() if(!is_in_processing_list) START_PROCESSING(SSobj, src) is_in_processing_list = TRUE - if(holder.client) + if(holder.client && _compass) if(check_visible_to_holder()) - holder.client.screen |= compass + holder.client.screen |= _compass else - holder.client.screen -= compass + holder.client.screen -= _compass else STOP_PROCESSING(SSobj, src) is_in_processing_list = FALSE - if(holder?.client) - holder.client.screen -= compass + if(holder?.client && _compass) + holder.client.screen -= _compass /obj/item/gps/equipped_robot() . = ..() @@ -105,13 +122,12 @@ var/global/list/all_gps_units = list() global.all_gps_units -= src events_repository.unregister(/decl/observ/moved, src, src, PROC_REF(update_holder)) update_holder(force_clear = TRUE) - QDEL_NULL(compass) + QDEL_NULL(_compass) return ..() /obj/item/gps/proc/can_track(var/obj/item/gps/other, var/reachable_z_levels) - if(!other.tracking || other.emped || other.hide_signal) + if(!other.tracking || other.emped || other.hide_signal || (other.tag_category && !(other.tag_category in tag_categories))) return FALSE - var/turf/origin = get_turf(src) var/turf/target = get_turf(other) if(!istype(origin) || !istype(target)) @@ -131,9 +147,12 @@ var/global/list/all_gps_units = list() LAZYDISTINCTADD(reachable_z_levels, adding_sites) return (target.z in reachable_z_levels) +/obj/item/gps/proc/create_compass() + _compass ||= new(src) + /obj/item/gps/proc/update_compass(var/update_compass_icon) - compass.hide_waypoints(FALSE) + _compass?.hide_waypoints(FALSE) var/turf/my_turf = get_turf(src) for(var/thing in tracking_devices) @@ -143,14 +162,15 @@ var/global/list/all_gps_units = list() LAZYREMOVE(showing_tracked_names, thing) continue - var/turf/gps_turf = get_turf(gps) - var/gps_tag = LAZYACCESS(showing_tracked_names, thing) ? gps.gps_tag : null - if(istype(gps_turf)) - compass.set_waypoint("\ref[gps]", gps_tag, gps_turf.x, gps_turf.y, gps_turf.z, LAZYACCESS(tracking_devices, "\ref[gps]")) - if(can_track(gps) && my_turf && gps_turf != my_turf) - compass.show_waypoint("\ref[gps]") + if(_compass) + var/turf/gps_turf = get_turf(gps) + var/use_gps_tag = LAZYACCESS(showing_tracked_names, thing) ? gps.gps_tag : null + if(istype(gps_turf)) + _compass.set_waypoint("\ref[gps]", use_gps_tag, gps_turf.x, gps_turf.y, gps_turf.z, LAZYACCESS(tracking_devices, "\ref[gps]")) + if(can_track(gps) && my_turf && gps_turf != my_turf) + _compass.show_waypoint("\ref[gps]") - compass.rebuild_overlay_lists(update_compass_icon) + _compass?.rebuild_overlay_lists(update_compass_icon) /obj/item/gps/proc/toggle_tracking(var/mob/user, var/silent) @@ -321,7 +341,7 @@ var/global/list/all_gps_units = list() if(href_list["stop_track"]) var/gps_ref = href_list["stop_track"] var/obj/item/gps/gps = locate(gps_ref) - compass.clear_waypoint(gps_ref) + _compass?.clear_waypoint(gps_ref) LAZYREMOVE(tracking_devices, gps_ref) LAZYREMOVE(showing_tracked_names, gps_ref) if(istype(gps) && !QDELETED(gps)) @@ -353,8 +373,7 @@ var/global/list/all_gps_units = list() var/a = input("Please enter desired tag.", name, gps_tag) as text a = uppertext(copytext(sanitize(a), 1, 11)) if(CanInteract(user, topic_state)) - gps_tag = a - name = "[initial(name)] ([gps_tag])" + set_gps_tag(a) to_chat(user, SPAN_NOTICE("You set your GPS's tag to '[gps_tag]'.")) . = TOPIC_REFRESH @@ -398,6 +417,7 @@ var/global/list/all_gps_units = list() // Department subtypes. /obj/item/gps/mining + gps_tag = "MIN0" color = "#c08f45" decals = list( "stripe-outside" = "#702e98", @@ -405,6 +425,7 @@ var/global/list/all_gps_units = list() ) /obj/item/gps/science + gps_tag = "SCI0" color = "#dbcfdf" decals = list( "stripe-outside" = "#cc33ff", @@ -412,15 +433,36 @@ var/global/list/all_gps_units = list() ) /obj/item/gps/medical + gps_tag = "MED0" color = "#ebebeb" decals = list( "stripe-outside" = "#6ab8fe", "stripe-inside" = "#339efe" ) -/obj/item/gps/explorer - color = "#4a4a4a" +/obj/item/gps/security + gps_tag = "SEC0" + color = "#5c0000" + decals = list( + "stripe-outside" = "#ff0000", + "stripe-inside" = "#800000" + ) + +/obj/item/gps/security/hos + decals = list( + "stripe-outside" = "#ffae00", + "stripe-inside" = "#9e7900" + ) + +/obj/item/gps/security + color = "#5c0000" + decals = list( + "stripe-outside" = "#ff0000", + "stripe-inside" = "#800000" + ) + +/obj/item/gps/security/hos decals = list( - "stripe-outside" = "#500677", - "stripe-inside" = "#68099e" + "stripe-outside" = "#ffae00", + "stripe-inside" = "#9e7900" ) diff --git a/code/game/objects/items/devices/radio/headsets_shared.dm b/code/game/objects/items/devices/radio/headsets_shared.dm index ead756343e37..475a5e515909 100644 --- a/code/game/objects/items/devices/radio/headsets_shared.dm +++ b/code/game/objects/items/devices/radio/headsets_shared.dm @@ -172,19 +172,19 @@ access_security ) -/obj/item/encryptionkey/ert - name = "\improper ERT radio encryption key" +/obj/item/encryptionkey/specops + name = "\improper special operations radio encryption key" can_decrypt = list(access_cent_specops) -/obj/item/encryptionkey/ert/Initialize(ml, material_key) +/obj/item/encryptionkey/specops/Initialize(ml, material_key) . = ..() can_decrypt |= get_all_station_access() -/obj/item/radio/headset/ert - name = "emergency response team radio headset" +/obj/item/radio/headset/specops + name = "special operations radio headset" desc = "The headset of the boss's boss." icon = 'icons/obj/items/device/radio/headsets/headset_admin.dmi' - encryption_keys = list(/obj/item/encryptionkey/ert) + encryption_keys = list(/obj/item/encryptionkey/specops) /obj/item/encryptionkey/mercenary origin_tech = @'{"esoteric":2}' diff --git a/code/game/objects/items/devices/radio/radio_borg.dm b/code/game/objects/items/devices/radio/radio_borg.dm index eab3b5bd6e08..6ce348ceaaa3 100644 --- a/code/game/objects/items/devices/radio/radio_borg.dm +++ b/code/game/objects/items/devices/radio/radio_borg.dm @@ -17,9 +17,6 @@ if(!robot.handle_radio_transmission()) return FALSE -/obj/item/radio/borg/ert - encryption_keys = list(/obj/item/encryptionkey/ert) - /obj/item/radio/borg/syndicate encryption_keys = list(/obj/item/encryptionkey/hacked) diff --git a/code/game/objects/items/flame/flame_candle.dm b/code/game/objects/items/flame/flame_candle.dm index 906824d52bcd..2c5d3b105709 100644 --- a/code/game/objects/items/flame/flame_candle.dm +++ b/code/game/objects/items/flame/flame_candle.dm @@ -17,6 +17,21 @@ /obj/item/flame/candle/spent _fuel = 0 +/obj/item/flame/candle/infinite/get_fuel() + return 10 + +/obj/item/flame/candle/infinite/has_fuel(amount) + return TRUE + +/obj/item/flame/candle/infinite/expend_fuel(amount) + return TRUE + +/obj/item/flame/candle/infinite/red + paint_color = COLOR_RED + +/obj/item/flame/candle/infinite/white + paint_color = COLOR_WHITE + /obj/item/flame/candle/red paint_color = COLOR_RED diff --git a/code/modules/augment/helping_hands.dm b/code/game/objects/items/helping_hands.dm similarity index 100% rename from code/modules/augment/helping_hands.dm rename to code/game/objects/items/helping_hands.dm diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index cbe87ec2d299..f2a7ee2d6d44 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -8,14 +8,14 @@ /obj/item/borg/overdrive name = "overdrive" icon = 'icons/obj/signs/warnings.dmi' - icon_state = "shock" + icon_state = "shock-large" /********************************************************************** HUD/SIGHT things ***********************************************************************/ /obj/item/borg/sight icon = 'icons/obj/signs/warnings.dmi' - icon_state = "secureareaold" + icon_state = "securearea-large" var/sight_mode = null var/glasses_hud_type diff --git a/code/game/objects/items/stacks/stack_serde.dm b/code/game/objects/items/stacks/stack_serde.dm new file mode 100644 index 000000000000..53702ee76722 --- /dev/null +++ b/code/game/objects/items/stacks/stack_serde.dm @@ -0,0 +1,3 @@ +/obj/item/stack/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(amount, /obj/item/stack) diff --git a/code/game/objects/items/trash_serde.dm b/code/game/objects/items/trash_serde.dm new file mode 100644 index 000000000000..ad791d2e8d39 --- /dev/null +++ b/code/game/objects/items/trash_serde.dm @@ -0,0 +1,24 @@ +/obj/item/trash/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(age, /obj/item/trash) + +/obj/item/trash/ShouldSerialize(_age) + return ..() && (isnull(_age) || age < _age) + +/obj/item/trash/Deserialize(list/instance_map) + ..() + return SERDE_HINT_POSTINIT + +/obj/item/trash/DeserializePostInit(list/instance_map) + . = ..() + for(var/obj/item/trash/thing in loc) + if(thing != src && thing.type == type) + qdel(src) + return + var/too_much_trash = 0 + for(var/obj/item/trash/trash in loc) + if(trash == src) + too_much_trash++ + if(too_much_trash >= 5) + qdel(src) + return diff --git a/code/game/objects/items/weapons/material/ashtray.dm b/code/game/objects/items/weapons/material/ashtray.dm index 305f721f1730..34a48cec61b4 100644 --- a/code/game/objects/items/weapons/material/ashtray.dm +++ b/code/game/objects/items/weapons/material/ashtray.dm @@ -58,3 +58,6 @@ /obj/item/ashtray/glass material = /decl/material/solid/glass + +/obj/item/ashtray/bronze + material = /decl/material/solid/metal/bronze diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm index fc19d7e17dd6..20fec64b1b05 100644 --- a/code/game/objects/items/weapons/material/knives.dm +++ b/code/game/objects/items/weapons/material/knives.dm @@ -129,4 +129,4 @@ name = "lightweight utility knife" desc = "A lightweight utility knife made out of a titanium alloy." material = /decl/material/solid/metal/titanium - draw_handle = FALSE \ No newline at end of file + draw_handle = FALSE diff --git a/code/game/objects/items/weapons/material/stick.dm b/code/game/objects/items/weapons/material/stick.dm index 92faaca3265f..8e18a94c2a5e 100644 --- a/code/game/objects/items/weapons/material/stick.dm +++ b/code/game/objects/items/weapons/material/stick.dm @@ -27,7 +27,7 @@ if(!sharp && (istype(used_item, /obj/item/stack/material/bolt) || istype(used_item, /obj/item/stack/material/bundle))) var/choice = input(user, "Do you want to make a torch, or a splint?", "Stick Crafting") as null|anything in list("Torch", "Splint") - if(!choice || QDELETED(user) || user.get_active_held_item() != used_item || QDELETED(used_item) || !QDELETED(src) || (loc != user && !Adjacent(user)) || sharp) + if(!choice || QDELETED(user) || user.get_active_held_item() != used_item || QDELETED(used_item) || QDELETED(src) || (loc != user && !Adjacent(user)) || sharp) return TRUE var/obj/item/stack/material/cloth = used_item @@ -54,7 +54,11 @@ var/was_held = (loc == user) cloth.use(cloth_cost) if(!was_held || user.try_unequip(src)) - var/obj/item/thing = new product_type(get_turf(src), material?.type, used_item.material?.type) + var/obj/item/thing + if(ispath(product_type, /obj/item/stack)) + thing = new product_type(get_turf(src), 1, material?.type, used_item.material?.type) + else + thing = new product_type(get_turf(src), material?.type, used_item.material?.type) if(was_held) user.put_in_hands(thing) to_chat(user, SPAN_NOTICE("You fashion \the [src] into \a [thing].")) diff --git a/code/game/objects/items/weapons/material/swiss.dm b/code/game/objects/items/weapons/material/swiss.dm index 33db97ea49cd..b91369cf2356 100644 --- a/code/game/objects/items/weapons/material/swiss.dm +++ b/code/game/objects/items/weapons/material/swiss.dm @@ -1,14 +1,3 @@ -#define SWISSKNF_CLOSED "Close" -#define SWISSKNF_LBLADE "Large Blade" -#define SWISSKNF_SBLADE "Small Blade" -#define SWISSKNF_CLIFTER "Cap Lifter-Screwdriver" -#define SWISSKNF_COPENER "Can Opener-Screwdriver" -#define SWISSKNF_CSCREW "Corkscrew" -#define SWISSKNF_GBLADE "Glass Cutter" -#define SWISSKNF_WCUTTER "Wirecutters" -#define SWISSKNF_WBLADE "Wood Saw" -#define SWISSKNF_CROWBAR "Pry Bar" - /obj/item/knife/folding/swiss name = "combi-knife" desc = "A small, colourable, multi-purpose folding knife." @@ -17,6 +6,17 @@ material = /decl/material/solid/metal/steel material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME + var/const/SWISSKNF_CLOSED = "Close" + var/const/SWISSKNF_LBLADE = "Large Blade" + var/const/SWISSKNF_SBLADE = "Small Blade" + var/const/SWISSKNF_CLIFTER = "Cap Lifter-Screwdriver" + var/const/SWISSKNF_COPENER = "Can Opener-Screwdriver" + var/const/SWISSKNF_CSCREW = "Corkscrew" + var/const/SWISSKNF_GBLADE = "Glass Cutter" + var/const/SWISSKNF_WCUTTER = "Wirecutters" + var/const/SWISSKNF_WBLADE = "Wood Saw" + var/const/SWISSKNF_CROWBAR = "Pry Bar" + var/active_tool = SWISSKNF_CLOSED var/list/tools = list(SWISSKNF_LBLADE, SWISSKNF_CLIFTER, SWISSKNF_COPENER) var/static/list/sharp_tools = list(SWISSKNF_LBLADE, SWISSKNF_SBLADE, SWISSKNF_GBLADE, SWISSKNF_WBLADE) @@ -151,25 +151,8 @@ handle_color = COLOR_AMBER tools = list(SWISSKNF_LBLADE, SWISSKNF_SBLADE, SWISSKNF_CLIFTER, SWISSKNF_COPENER, SWISSKNF_WBLADE, SWISSKNF_WCUTTER) -/obj/item/knife/folding/swiss/explorer - name = "explorer's combi-knife" - desc = "A small, purple, multi-purpose folding knife. This one adds a wood saw and prybar." - handle_color = COLOR_PURPLE - tools = list(SWISSKNF_LBLADE, SWISSKNF_SBLADE, SWISSKNF_CLIFTER, SWISSKNF_COPENER, SWISSKNF_WBLADE, SWISSKNF_CROWBAR) - /obj/item/knife/folding/swiss/loot name = "black combi-knife" desc = "A small, silver, multi-purpose folding knife. This one adds a small blade and corkscrew." handle_color = COLOR_GRAY40 tools = list(SWISSKNF_LBLADE, SWISSKNF_SBLADE, SWISSKNF_CLIFTER, SWISSKNF_COPENER, SWISSKNF_CSCREW) - -#undef SWISSKNF_CLOSED -#undef SWISSKNF_LBLADE -#undef SWISSKNF_SBLADE -#undef SWISSKNF_CLIFTER -#undef SWISSKNF_COPENER -#undef SWISSKNF_CSCREW -#undef SWISSKNF_GBLADE -#undef SWISSKNF_WCUTTER -#undef SWISSKNF_WBLADE -#undef SWISSKNF_CROWBAR diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 2b3e166a2d24..4ac41d06e88e 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -355,54 +355,6 @@ return 1 return ..() -//ERT backpacks. -/obj/item/backpack/ert - name = "emergency response team backpack" - desc = "A spacious backpack with lots of pockets, used by members of the Emergency Response Team." - icon = 'icons/obj/items/storage/backpack/backpack_ert.dmi' - var/marking_state - var/marking_colour - -/obj/item/backpack/ert/on_update_icon() - . = ..() - if(marking_state) - var/image/I = image(icon, marking_state) - I.color = marking_colour - I.appearance_flags |= RESET_COLOR - add_overlay(I) - -/obj/item/backpack/ert/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing = TRUE) - if(overlay && slot == slot_back_str && marking_state) - var/image/I = image(overlay.icon, "[overlay.icon_state]-[marking_state]") - I.color = marking_colour - I.appearance_flags |= RESET_COLOR - overlay.add_overlay(I) - . = ..() - -/obj/item/backpack/ert/commander - name = "emergency response team commander backpack" - desc = "A spacious backpack with lots of pockets, worn by the commander of an Emergency Response Team." - marking_colour = COLOR_BLUE_GRAY - marking_state = "com" - -/obj/item/backpack/ert/security - name = "emergency response team security backpack" - desc = "A spacious backpack with lots of pockets, worn by security members of an Emergency Response Team." - marking_colour = COLOR_NT_RED - marking_state = "sec" - -/obj/item/backpack/ert/engineer - name = "emergency response team engineer backpack" - desc = "A spacious backpack with lots of pockets, worn by engineering members of an Emergency Response Team." - marking_colour = COLOR_GOLD - marking_state = "eng" - -/obj/item/backpack/ert/medical - name = "emergency response team medical backpack" - desc = "A spacious backpack with lots of pockets, worn by medical members of an Emergency Response Team." - marking_colour = COLOR_OFF_WHITE - marking_state = "med" - /* * Messenger Bags */ diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index fa38daee6000..3dc1ec39db8b 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -166,6 +166,18 @@ /obj/item/box/ammo/blanks/WillContain() return list(/obj/item/ammo_casing/shotgun/blank = 8) +/obj/item/box/ammo/blanks/large + icon_state = "largebox" + w_class = ITEM_SIZE_LARGE + storage = /datum/storage/box/large/metal + +/obj/item/box/ammo/blanks/large/Initialize(ml, material_key) + . = ..() + storage.make_exact_fit() + +/obj/item/box/ammo/blanks/large/WillContain() + return list(/obj/item/ammo_casing/shotgun/blank = 16) + /obj/item/box/ammo/practiceshells name = "box of practice shells" /obj/item/box/ammo/practiceshells/WillContain() diff --git a/code/game/objects/items/weapons/storage/med_pouch.dm b/code/game/objects/items/weapons/storage/med_pouch.dm index 0bb25ee3ac04..1d7cc0f7c27b 100644 --- a/code/game/objects/items/weapons/storage/med_pouch.dm +++ b/code/game/objects/items/weapons/storage/med_pouch.dm @@ -195,16 +195,23 @@ Single Use Emergency Pouches chem_volume = 15 abstract_type = /obj/item/chems/pill/pouch_pill var/_reagent_name + var/_reagent_volume /obj/item/chems/pill/pouch_pill/Initialize(ml, material_key) . = ..() - if(!REAGENT_TOTAL_VOLUME(reagents)) + if(!istype(reagents) || !REAGENT_TOTAL_VOLUME(reagents)) log_warning("[log_info_line(src)] was deleted for containing no reagents during init!") return INITIALIZE_HINT_QDEL - if(reagents?.get_primary_reagent_name() && !_reagent_name) - _reagent_name = "emergency [reagents.get_primary_reagent_name()] pill ([REAGENT_TOTAL_VOLUME(reagents)]u)" - if(_reagent_name) - SetName(_reagent_name) + if(isnull(_reagent_name)) + _reagent_name = reagents.get_primary_reagent_name() + _reagent_volume = REAGENT_TOTAL_VOLUME(reagents) + if(_reagent_name && _reagent_volume) + SetName("emergency [_reagent_name] pill ([_reagent_volume]u)") + +/obj/item/chems/pill/pouch_pill/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(_reagent_name, /obj/item/chems/pill/pouch_pill) + SERIALIZE_IF_MODIFIED(_reagent_volume, /obj/item/chems/pill/pouch_pill) /obj/item/chems/pill/pouch_pill/stabilizer/populate_reagents() add_to_reagents(/decl/material/liquid/stabilizer, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/code/game/objects/items/weapons/storage/specialized.dm b/code/game/objects/items/weapons/storage/specialized.dm index 014814223111..8a90eb636360 100644 --- a/code/game/objects/items/weapons/storage/specialized.dm +++ b/code/game/objects/items/weapons/storage/specialized.dm @@ -9,7 +9,7 @@ // Mining Satchel // ----------------------------- -/obj/item/ore +/obj/item/ore_satchel name = "mining satchel" desc = "This sturdy bag can be used to store and transport ores." icon = 'icons/obj/items/mining_satchel.dmi' @@ -37,7 +37,7 @@ // Plant bag // ----------------------------- -/obj/item/plants +/obj/item/plant_satchel name = "botanical satchel" desc = "This bag can be used to store all kinds of plant products and botanical specimen." icon = 'icons/obj/hydroponics/hydroponics_machines.dmi' diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index 13c9162b87f9..2995bcc82700 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -1,7 +1,6 @@ /* Types of tanks! * Contains: * Oxygen - * Anesthetic * Air * Hydrogen * Emergency Oxygen @@ -55,6 +54,9 @@ /obj/item/tank/hydrogen/empty starting_pressure = list() +/obj/item/tank/hydrogen/collector + starting_pressure = list(/decl/material/gas/hydrogen = 70) + /* * Emergency Oxygen */ @@ -105,4 +107,4 @@ icon = 'icons/obj/items/tanks/tank_red.dmi' distribute_pressure = ONE_ATMOSPHERE*O2STANDARD starting_pressure = list(/decl/material/gas/nitrogen = 10 ATM) - gas_volume = 180 \ No newline at end of file + gas_volume = 180 diff --git a/code/game/objects/random/subtypes/clothing.dm b/code/game/objects/random/subtypes/clothing.dm index 8524aff474c2..a54cac010f93 100644 --- a/code/game/objects/random/subtypes/clothing.dm +++ b/code/game/objects/random/subtypes/clothing.dm @@ -238,3 +238,21 @@ /obj/item/clothing/suit/space/void/medical/alt ) return spawnable_choices + +/obj/random/poncho + name = "random poncho" + icon = /obj/item/clothing/suit/poncho/green::icon + icon_state = /obj/item/clothing/suit/poncho/green::icon_state + +/obj/random/poncho/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/clothing/suit/poncho/green, + /obj/item/clothing/suit/poncho/red, + /obj/item/clothing/suit/poncho/purple, + /obj/item/clothing/suit/poncho/blue, + /obj/item/clothing/suit/poncho/security, + /obj/item/clothing/suit/poncho/medical, + /obj/item/clothing/suit/poncho/engineering, + /obj/item/clothing/suit/poncho/cargo + ) + return spawn_choices diff --git a/code/game/objects/random/subtypes/misc.dm b/code/game/objects/random/subtypes/misc.dm index 27d600db80a6..990d11c5a1aa 100644 --- a/code/game/objects/random/subtypes/misc.dm +++ b/code/game/objects/random/subtypes/misc.dm @@ -6,6 +6,10 @@ color = COLOR_PURPLE spawn_nothing_percentage = 50 +/obj/random/contraband/nofail + name = "guaranteed random illegal item" + spawn_nothing_percentage = 0 + /obj/random/contraband/spawn_choices() var/static/list/spawnable_choices = list( /obj/item/grooming/comb = 4, @@ -54,6 +58,16 @@ ) return spawnable_choices +/obj/random/mug + name = "random coffee cup" + desc = "A random coffee cup/mug." + icon = 'icons/obj/drink_glasses/coffecup.dmi' + icon_state = "coffeecup" + +/obj/random/mug/spawn_choices() + var/static/list/spawnable_choices = typesof(/obj/item/chems/drinks/glass2/coffeecup) - /obj/item/chems/drinks/glass2/coffeecup/custom + return spawnable_choices + /obj/random/drinkbottle name = "random drink" desc = "This is a random drink." @@ -304,7 +318,7 @@ /obj/item/box/large = 2, /obj/item/box/glowsticks = 3, /obj/item/wallet = 1, - /obj/item/ore = 2, + /obj/item/ore_satchel = 2, /obj/item/belt/utility/full = 2, /obj/item/belt/medical/emt = 2, /obj/item/belt/medical = 2, @@ -387,12 +401,28 @@ desc = "This is a randomly selected vending machine." icon = 'icons/obj/machines/vending/coffee.dmi' icon_state = "world-hellfire" + abstract_type = /obj/random/vendor -/obj/random/vendor/spawn_choices() +/obj/random/vendor/food + name = "random food vending machine" + +/obj/random/vendor/food/spawn_choices() var/static/list/spawnable_choices = list( /obj/machinery/vending/weeb, /obj/machinery/vending/sol, - /obj/machinery/vending/snix + /obj/machinery/vending/snix, + /obj/machinery/vending/snack + ) + return spawnable_choices + + +/obj/random/vendor/drink + name = "random drink vending machine" + +/obj/random/vendor/drink/spawn_choices() + var/static/list/spawnable_choices = list( + /obj/machinery/vending/coffee, + /obj/machinery/vending/cola ) return spawnable_choices @@ -524,3 +554,64 @@ ) return spawnable_choices + +/obj/random/ore_pile + name = "random ore pile" + desc = "A pile of random ores. High chance of a larger pile of common ores, lower chances of small piles of rarer ores." + +/obj/random/ore_pile/spawn_choices() + var/static/list/spawnable_choices = list( + /obj/item/stack/material/ore/handful/sand/fifteen = 15, + /obj/item/stack/material/ore/bauxite/ten = 10, + /obj/item/stack/material/ore/coal/ten = 10, + /obj/item/stack/material/ore/tetrahedrite/ten = 10, + /obj/item/stack/material/ore/iron/ten = 10, + /obj/item/stack/material/ore/galena/ten = 10, + /obj/item/stack/material/lump/large/marble/five = 5, + /obj/item/stack/material/ore/gold/five = 5, + /obj/item/stack/material/ore/diamond/three = 3, + /obj/item/stack/material/ore/osmium/three = 3, + /obj/item/stack/material/ore/hydrogen/two = 2, + /obj/item/stack/material/ore/rutile/five = 5, + /obj/item/stack/material/ore/silver/five = 3, + /obj/item/stack/material/ore/uranium/three = 2 + ) + return spawnable_choices + +/obj/random/meat + name = "random meat" + icon = /obj/item/food/butchery/meat/beef::icon + icon_state = /obj/item/food/butchery/meat/beef::icon_state + color = /obj/item/food/butchery/meat/beef::color + +/obj/random/meat/spawn_choices() + var/static/list/spawnable_choices = list( + /obj/item/food/butchery/meat/beef, + /obj/item/food/butchery/meat/goat, + /obj/item/food/butchery/meat/chicken, + /obj/item/food/butchery/meat/corgi, + /obj/item/food/butchery/meat/bear, + /obj/item/food/butchery/meat/fish/shark, + /obj/item/food/butchery/meat/fish/carp, + /obj/item/food/butchery/meat/fish/octopus, + /obj/item/food/butchery/meat/fish/mollusc + ) + return spawnable_choices + +/obj/random/mouseremains + name = "random mouseremains" + desc = "For use with mouse spawners." + icon = /obj/item/assembly/mousetrap::icon + icon_state = /obj/item/assembly/mousetrap::icon_state + +/obj/random/mouseremains/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/assembly/mousetrap, + /obj/item/assembly/mousetrap/armed, + /obj/effect/decal/cleanable/spiderling_remains, + /obj/effect/decal/cleanable/ash, + /obj/item/trash/cigbutt, + /obj/item/trash/cigbutt/cigarbutt, + /obj/item/remains/mouse + ) + return spawn_choices diff --git a/code/game/objects/random/subtypes/mobs.dm b/code/game/objects/random/subtypes/mobs.dm index 03e86f8f5f4c..d26977a5bf19 100644 --- a/code/game/objects/random/subtypes/mobs.dm +++ b/code/game/objects/random/subtypes/mobs.dm @@ -49,3 +49,26 @@ /mob/living/simple_animal/hostile/scarybat/cave = 4 ) return spawnable_choices + +/obj/random/hostile/hivebot + name = "Random Hivebot" + icon = /mob/living/simple_animal/hostile/hivebot::icon + icon_state = /mob/living/simple_animal/hostile/hivebot::icon_state + +/obj/random/hostile/hivebot/spawn_choices() + var/static/list/spawnable_choices = typesof(/mob/living/simple_animal/hostile/hivebot) + return spawnable_choices + +/obj/random/hostile/hivebot/melee + name = "Random Melee Hivebot" + +/obj/random/hostile/hivebot/melee/spawn_choices() + var/static/list/spawnable_choices = typesof(/mob/living/simple_animal/hostile/hivebot/melee) + return spawnable_choices + +/obj/random/hostile/hivebot/ranged + name = "Random Ranged Hivebot" + +/obj/random/hostile/hivebot/ranged/spawn_choices() + var/static/list/spawnable_choices = typesof(/mob/living/simple_animal/hostile/hivebot/ranged) + return spawnable_choices diff --git a/code/game/objects/random/subtypes/salvage.dm b/code/game/objects/random/subtypes/salvage.dm new file mode 100644 index 000000000000..298557addcac --- /dev/null +++ b/code/game/objects/random/subtypes/salvage.dm @@ -0,0 +1,128 @@ +/obj/random/scrapped_gun + name = "random scrapped gun" + icon = /obj/item/gun/projectile/automatic/assault_rifle::icon + icon_state = /obj/item/gun/projectile/automatic/assault_rifle::icon_state + +/obj/random/scrapped_gun/spawn_choices() + var/static/list/spawn_choices = list( + /obj/random/scrapped_pistol = 10, + /obj/random/scrapped_smg = 5, + /obj/random/scrapped_laser = 5, + /obj/random/scrapped_shotgun = 3, + /obj/random/scrapped_ionrifle = 3, + /obj/random/scrapped_assault = 1, + /obj/random/scrapped_flechette = 1, + /obj/random/scrapped_grenadelauncher = 1, + /obj/random/scrapped_dartgun = 1 + ) + return spawn_choices + +/obj/random/scrapped_assault + name = "random scrapped assault rifle" + icon = /obj/item/gun/projectile/automatic/assault_rifle::icon + icon_state = /obj/item/gun/projectile/automatic/assault_rifle::icon_state + +/obj/random/scrapped_assault/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/ballistic/assault = 10, + /obj/item/gun/projectile/automatic/assault_rifle = 1 + ) + return spawn_choices + +/obj/random/scrapped_pistol + name = "random scrapped pistol" + icon = /obj/item/gun/projectile/pistol::icon + icon_state = /obj/item/gun/projectile/pistol::icon_state + +/obj/random/scrapped_pistol/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/ballistic/pistol = 10, + /obj/item/gun/projectile/pistol = 1 + ) + return spawn_choices + +/obj/random/scrapped_ionrifle + name = "random scrapped ion rifle" + icon = /obj/item/gun/energy/ionrifle::icon + icon_state = /obj/item/gun/energy/ionrifle::icon_state + +/obj/random/scrapped_ionrifle/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/energy/ionrifle = 10, + /obj/item/gun/energy/ionrifle = 1 + ) + return spawn_choices + +/obj/random/scrapped_grenadelauncher + name = "random scrapped grenade launcher" + icon = /obj/item/gun/launcher/grenade::icon + icon_state = /obj/item/gun/launcher/grenade::icon_state + +/obj/random/scrapped_grenadelauncher/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/launcher/grenade = 10, + /obj/item/gun/launcher/grenade = 1 + ) + return spawn_choices + +/obj/random/scrapped_laser + name = "random scrapped laser rifle" + icon = /obj/item/gun/energy/laser::icon + icon_state = /obj/item/gun/energy/laser::icon_state + +/obj/random/scrapped_laser/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/energy/laserrifle = 10, + /obj/item/gun/energy/laser = 1 + ) + return spawn_choices + +/obj/random/scrapped_smg + name = "random scrapped submachine gun" + icon = /obj/item/gun/projectile/automatic/smg::icon + icon_state = /obj/item/gun/projectile/automatic/smg::icon_state + +/obj/random/scrapped_smg/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/ballistic/smg = 10, + /obj/item/gun/projectile/automatic/smg = 1 + ) + return spawn_choices + +/obj/random/scrapped_shotgun + name = "random scrapped shotgun" + icon = /obj/item/gun/projectile/shotgun/pump::icon + icon_state = /obj/item/gun/projectile/shotgun/pump::icon_state + +/obj/random/scrapped_shotgun/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/ballistic/shotgun_pump = 10, + /obj/item/salvage/ballistic/shotgun_doublebarrel = 10, + /obj/item/gun/projectile/shotgun/pump = 1, + /obj/item/gun/projectile/shotgun/doublebarrel = 1 + ) + return spawn_choices + +/obj/random/scrapped_dartgun + name = "random scrapped dartgun" + icon = /obj/item/gun/projectile/dartgun::icon + icon_state = /obj/item/gun/projectile/dartgun::icon_state + +/obj/random/scrapped_dartgun/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/launcher/dartgun = 10, + /obj/item/gun/projectile/dartgun = 1 + ) + return spawn_choices + +/obj/random/scrapped_flechette + name = "random scrapped flechette rifle" + icon = /obj/item/gun/magnetic/railgun/flechette::icon + icon_state = /obj/item/gun/magnetic/railgun/flechette::icon_state + +/obj/random/scrapped_flechette/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/magnetic/flechette = 10, + /obj/item/gun/magnetic/railgun/flechette = 1 + ) + return spawn_choices diff --git a/code/game/objects/random/subtypes/tech.dm b/code/game/objects/random/subtypes/tech.dm index a1b1ab86e65d..2bd7302535ca 100644 --- a/code/game/objects/random/subtypes/tech.dm +++ b/code/game/objects/random/subtypes/tech.dm @@ -74,6 +74,36 @@ ) return spawnable_choices +/obj/random/tech_supply/nofail + name = "guaranteed random tech supply" + spawn_nothing_percentage = 0 + +/obj/random/tech_supply/component + name = "random tech component" + desc = "This is a random machine component." + +/obj/random/tech_supply/component/nofail + name = "guaranteed random tech component" + spawn_nothing_percentage = 0 + +/obj/random/tech_supply/component/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/stock_parts/console_screen = 2, + /obj/item/stock_parts/capacitor = 3, + /obj/item/stock_parts/capacitor/adv = 2, + /obj/item/stock_parts/capacitor/super = 1, + /obj/item/stock_parts/manipulator = 3, + /obj/item/stock_parts/manipulator/nano = 2, + /obj/item/stock_parts/manipulator/pico = 1, + /obj/item/stock_parts/matter_bin = 3, + /obj/item/stock_parts/matter_bin/adv = 2, + /obj/item/stock_parts/matter_bin/super = 1, + /obj/item/stock_parts/scanning_module = 3, + /obj/item/stock_parts/scanning_module/adv = 2, + /obj/item/stock_parts/scanning_module/phasic = 1 + ) + return spawn_choices + /obj/random/tank name = "random tank" desc = "This is a tank." @@ -120,3 +150,50 @@ /obj/item/oxycandle ) return spawnable_choices + +/obj/random/hardsuit + name = "random hardsuit" + desc = "This is a random hardsuit." + icon = 'icons/clothing/rigs/rig.dmi' + icon_state = ICON_STATE_WORLD + +/obj/random/hardsuit/spawn_choices() + var/static/list/spawnable_choices = list( + /obj/item/rig/light/hacker/unlocked = 4, + /obj/item/rig/industrial/unlocked = 5, + /obj/item/rig/eva/unlocked = 5, + /obj/item/rig/light/stealth/unlocked = 4, + /obj/item/rig/hazard/unlocked = 3, + /obj/item/rig/merc/empty/unlocked = 1 + ) + return spawnable_choices + +/obj/random/powercell + name = "random powercell" + desc = "This is a random powercell." + icon = /obj/item/cell::icon + icon_state = /obj/item/cell::icon_state + +/obj/random/powercell/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/cell = 40, + /obj/item/cell/gun = 25, + /obj/item/cell/high = 25, + /obj/item/cell/super = 9, + /obj/item/cell/hyper = 1 + ) + return spawn_choices + +/obj/random/smes_coil + name = "random smes coil" + desc = "This is a random smes coil." + icon = /obj/item/stock_parts/smes_coil::icon + icon_state = /obj/item/stock_parts/smes_coil::icon_state + +/obj/random/smes_coil/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/stock_parts/smes_coil = 4, + /obj/item/stock_parts/smes_coil/super_capacity = 1, + /obj/item/stock_parts/smes_coil/super_io = 1 + ) + return spawn_choices diff --git a/code/game/objects/random/subtypes/toys.dm b/code/game/objects/random/subtypes/toys.dm index 02b5aa65728b..255e50a2bb14 100644 --- a/code/game/objects/random/subtypes/toys.dm +++ b/code/game/objects/random/subtypes/toys.dm @@ -229,3 +229,25 @@ /obj/item/toy/figure/ert ) return spawnable_choices + +/obj/random/mech_toy + name = "random mech toy" + desc = "This is a random mech toy." + icon = /obj/item/toy/prize/powerloader::icon + icon_state = /obj/item/toy/prize/powerloader::icon_state + +/obj/random/mech_toy/spawn_choices() + var/static/list/spawnable_choices = list( + /obj/item/toy/prize/powerloader, + /obj/item/toy/prize/fireripley, + /obj/item/toy/prize/deathripley, + /obj/item/toy/prize/gygax, + /obj/item/toy/prize/durand, + /obj/item/toy/prize/honk, + /obj/item/toy/prize/marauder, + /obj/item/toy/prize/seraph, + /obj/item/toy/prize/mauler, + /obj/item/toy/prize/odysseus, + /obj/item/toy/prize/phazon + ) + return spawnable_choices diff --git a/code/game/objects/random/subtypes/weapons.dm b/code/game/objects/random/subtypes/weapons.dm index 9414c6c2d2be..14a526bda1df 100644 --- a/code/game/objects/random/subtypes/weapons.dm +++ b/code/game/objects/random/subtypes/weapons.dm @@ -93,3 +93,19 @@ /obj/item/ammo_magazine/smg/rubber = 6 ) return spawnable_choices + +/obj/random/landmine + name = "random landmine" + icon = /obj/item/mine::icon + icon_state = /obj/item/mine::icon_state + +/obj/random/landmine/spawn_choices() + var/static/list/spawnable_choices = list( + /obj/item/mine/emp/mapped, + /obj/item/mine/frag/mapped, + /obj/item/mine/incendiary/mapped, + /obj/item/mine/napalm/mapped, + /obj/item/mine/radiation/mapped, + /obj/item/mine/stun/mapped + ) + return spawnable_choices diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index be2c2102ae50..27239ec9127b 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -8,7 +8,6 @@ /// Multiplier for degree of comfort offered to mobs buckled to this furniture. var/user_comfort = 0 // TODO: extremely uncomfortable chairs - var/structure_flags var/last_damage_message var/hitsound = 'sound/weapons/Genhit.ogg' @@ -16,7 +15,6 @@ var/parts_amount var/footstep_type var/mob_offset - var/paint_verb /obj/structure/get_color() diff --git a/code/game/objects/structures/_structure_serde.dm b/code/game/objects/structures/_structure_serde.dm new file mode 100644 index 000000000000..1f69c1294225 --- /dev/null +++ b/code/game/objects/structures/_structure_serde.dm @@ -0,0 +1,3 @@ +/obj/structure/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(paint_verb, /obj/structure) diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index 3f5b94ea0716..8598d9db8d55 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -60,7 +60,6 @@ /obj/structure/catwalk/can_climb_from_below(var/mob/climber) return TRUE - /obj/structure/catwalk/proc/redraw_nearby_catwalks() for(var/direction in global.alldirs) var/obj/structure/catwalk/L = locate() in get_step(src, direction) diff --git a/code/game/objects/structures/chairs/chairs.dm b/code/game/objects/structures/chairs/chairs.dm index 5bc36823d97e..e22920c145a6 100644 --- a/code/game/objects/structures/chairs/chairs.dm +++ b/code/game/objects/structures/chairs/chairs.dm @@ -158,6 +158,8 @@ initial_padding_color = "#62e36c" /obj/structure/chair/comfy/yellow initial_padding_color = "#ffbf00" +/obj/structure/chair/comfy/orange + initial_padding_color = COLOR_ORANGE /obj/structure/chair/comfy/captain name = "captain chair" diff --git a/code/game/objects/structures/cliffs.dm b/code/game/objects/structures/cliffs.dm new file mode 100644 index 000000000000..5745913052ab --- /dev/null +++ b/code/game/objects/structures/cliffs.dm @@ -0,0 +1,300 @@ +/* +Cliffs give a visual illusion of depth by separating two places while presenting a 'top' and 'bottom' side. + +Ported from Polaris. + +Mobs moving into a cliff from the bottom side will simply bump into it and be denied moving into the tile, +where as mobs moving into a cliff from the top side will 'fall' off the cliff, forcing them to the bottom, causing significant damage and stunning them. + +Mobs can climb this while wearing climbing equipment by clickdragging themselves onto a cliff, as if it were a table. + +Flying mobs can pass over all cliffs with no risk of falling. + +Projectiles and thrown objects can pass, however if moving upwards, there is a chance for it to be stopped by the cliff. +This makes fighting something that is on top of a cliff more challenging. + +As a note, dir points upwards, e.g. pointing WEST means the left side is 'up', and the right side is 'down'. + +When mapping these in, be sure to give at least a one tile clearance, as NORTH facing cliffs expand to +two tiles on initialization, and which way a cliff is facing may change during maploading. +*/ + +/obj/structure/cliff + name = "cliff" + desc = "A steep rock ledge. You might be able to climb it if you feel bold enough." + icon = 'icons/obj/structures/cliffs.dmi' + anchored = TRUE + density = TRUE + opacity = FALSE + atom_flags = ATOM_FLAG_CLIMBABLE + appearance_flags = KEEP_TOGETHER + climb_speed_mult = 2 + + var/icon_variant = null // Used to make cliffs less repetitive by having a selection of sprites to display. + var/corner = FALSE // Used for icon things. + var/ramp = FALSE // Ditto. + var/bottom = FALSE // Used for 'bottom' typed cliffs, to avoid infinite cliffs, and for icons. + + var/is_double_cliff = FALSE // Set to true when making the two-tile cliffs, used for projectile checks. + var/uphill_penalty = 30 // Odds of a projectile not making it up the cliff. + +/obj/structure/cliff/Initialize() + . = ..() + register_dangerous_to_step() + +/obj/structure/cliff/Destroy() + unregister_dangerous_to_step() + if(is_double_cliff && !bottom) + var/turf/other = get_step(src, SOUTH) + if(istype(other)) + for(var/obj/structure/cliff/bottom/bottom in other) + qdel(bottom) + . = ..() + +/obj/structure/cliff/get_examine_hints(mob/user, distance, infix, suffix) + . = ..() + var/static/desc_string = "Walking off the edge of a cliff while on top will cause you to fall off, causing severe injury.
\ + You can climb this cliff if wearing special climbing equipment, by click-dragging yourself onto the cliff.
\ + Projectiles traveling up a cliff may hit the cliff instead, making it more difficult to fight something \ + on top." + LAZYADD(., desc_string) + +/obj/structure/cliff/Move() + var/turf/old_turf = get_turf(src) + . = ..() + if(.) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf.unregister_dangerous_object(src) + new_turf.register_dangerous_object(src) + +// These arrange their sprites at runtime, as opposed to being statically placed in the map file. +/obj/structure/cliff/automatic + icon_state = "cliffbuilder" + dir = NORTH + +/obj/structure/cliff/automatic/corner + icon_state = "cliffbuilder-corner" + dir = NORTHEAST + corner = TRUE + +// Tiny part that doesn't block, used for making 'ramps'. +/obj/structure/cliff/automatic/ramp + icon_state = "cliffbuilder-ramp" + dir = NORTHEAST + density = FALSE + ramp = TRUE + +// Made automatically as needed by automatic cliffs. +/obj/structure/cliff/bottom + bottom = TRUE + is_spawnable_type = FALSE + +/obj/structure/cliff/automatic/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +// Paranoid about the maploader, direction is very important to cliffs, since they may get bigger if initialized while facing NORTH. +/obj/structure/cliff/automatic/LateInitialize() + if(dir in global.cardinal) + icon_variant = pick("a", "b", "c") + + if(dir & NORTH && !bottom) // North-facing cliffs require more cliffs to be made. + make_bottom() + + update_icon() + +/obj/structure/cliff/proc/make_bottom() + // First, make sure there's room to put the bottom side. + var/turf/turf = locate(x, y - 1, z) + if(!istype(turf)) + return FALSE + + // Now make the bottom cliff have mostly the same variables. + var/obj/structure/cliff/bottom/bottom_cliff = new(turf) + is_double_cliff = TRUE + climb_speed_mult /= 2 // Since there are two cliffs to climb when going north, both take half the time. + + bottom_cliff.dir = dir + bottom_cliff.is_double_cliff = TRUE + bottom_cliff.climb_speed_mult = climb_speed_mult + bottom_cliff.icon_variant = icon_variant + bottom_cliff.corner = corner + bottom_cliff.ramp = ramp + bottom_cliff.layer = layer - 0.1 + bottom_cliff.density = density + bottom_cliff.update_icon() + +/obj/structure/cliff/set_dir(new_dir) + ..() + update_icon() + +/obj/structure/cliff/on_update_icon() + icon_state = "cliff-[dir][icon_variant][bottom ? "-bottom" : ""][corner ? "-corner" : ""][ramp ? "-ramp" : ""]" + + // Now for making the top-side look like a different turf. + var/turf/turf = get_step(src, dir) + if(!istype(turf)) + return + + underlays.Cut() + var/subtraction_icon_state = "[icon_state]-subtract" + if(turf && (check_state_in_icon(subtraction_icon_state, icon))) + var/image/subtract = image(icon, subtraction_icon_state) + subtract.blend_mode = BLEND_SUBTRACT + underlays += subtract + +// Movement-related code. +/obj/structure/cliff/CanPass(atom/movable/mover, turf/target) + if(isliving(mover)) + var/mob/living/faller = mover + if(faller.can_overcome_gravity()) // Flying mobs can always pass. + return TRUE + return ..() + + else if(!istype(mover, /obj/item/projectile) && !mover.throwing) // 'sliding' objects can fall / bump into cliffs. + return ..() + + // Projectiles and objects flying 'upward' have a chance to hit the cliff instead, wasting the shot. + else if(istype(mover, /obj)) + var/obj/O = mover + if(check_shield_arc(src, dir, O)) // This is actually for mobs but it will work for our purposes as well. + if(prob(uphill_penalty / (1 + is_double_cliff) )) // Firing upwards facing NORTH means it will likely have to pass through two cliffs, so the chance is halved. + return FALSE + return TRUE + +/obj/structure/cliff/Bumped(atom/movable/mover) + if(!istype(mover, /obj/item/projectile) && !mover.throwing && should_fall(mover)) + fall_off_cliff(mover) + return + ..() + +/obj/structure/cliff/proc/should_fall(atom/movable/mover) + if(isliving(mover)) + var/mob/living/faller = mover + if(faller.can_overcome_gravity()) + return FALSE + var/turf/turf = get_turf(mover) + if(turf && get_dir(turf, loc) & global.reverse_dir[dir]) // dir points 'up' the cliff, e.g. cliff pointing NORTH will cause someone to fall if moving SOUTH into it. + return TRUE + return FALSE + +/obj/structure/cliff/proc/fall_off_cliff(atom/movable/mover) + . = FALSE + + var/mob/living/faller + if(isliving(mover)) + faller = mover + var/turf/turf = get_step(src, global.reverse_dir[dir]) + var/displaced = FALSE + + if(dir in list(EAST, WEST)) // Apply an offset if flying sideways, to help maintain the illusion of depth. + for(var/i = 1 to 2) + var/turf/new_T = locate(turf.x, turf.y - i, turf.z) + if(!new_T || locate(/obj/structure/cliff) in new_T) + break + turf = new_T + displaced = TRUE + + if(!istype(turf)) + return + + var/safe_fall = FALSE + if(istype(faller)) + safe_fall = faller.can_overcome_gravity() + else if(istype(mover, /obj/vehicle/bike)) + var/obj/vehicle/bike/Bi = mover + if(Bi.on) + safe_fall = TRUE + + // Buckled people can't react to save themselves, if they're not on a vehicle. + if(!istype(mover, /obj/vehicle) && !isexosuit(mover) && !faller && mover.buckled_mob) + faller = mover.buckled_mob + + if(safe_fall) + visible_message(SPAN_NOTICE("\The [mover] glides down from \the [src].")) + else + visible_message(SPAN_DANGER("\The [mover] falls off \the [src]!")) + + mover.forceMove(turf) + + var/harm = !is_double_cliff ? 1 : 0.5 + if(!safe_fall) + // Do the actual hurting. Double cliffs do halved damage due to them most likely hitting twice. + if(faller) + SET_STATUS_MAX(faller, STAT_WEAK, (5 * harm)) + + if(istype(mover, /obj/vehicle)) + var/obj/vehicle/vehicle = mover + vehicle.take_damage(40 * harm) + vehicle.visible_message(SPAN_WARNING("\The [vehicle] absorbs some of the impact, damaging it.")) + harm = round(harm * 0.5) + if(vehicle.buckled_mob) + var/damage = clamp(vehicle.buckled_mob.get_max_health() * 0.4, 20, 100) + vehicle.buckled_mob.take_damage(damage * harm, BRUTE, inflicter = src) + shake_camera(vehicle.buckled_mob, 1, 1) + else if(isexosuit(mover)) + var/mob/living/exosuit/Mech = mover + harm = round(harm * 0.5) + var/list/passengers = list() + for(var/mob/living/passenger in Mech.pilots) + passengers |= passenger + passenger.take_damage(clamp(faller.get_max_health() * 0.4, 10, 50) * harm, BRUTE, inflicter = src) + shake_camera(passenger, 1, 1) + to_chat(passenger, SPAN_DANGER("\The [Mech] shakes, bouncing you violently!")) + Mech.take_damage(clamp(Mech.get_max_health() * 0.4 * harm, 50, 300)) + if(QDELETED(Mech) && length(passengers)) // Damage caused the mech to explode, or otherwise vanish. + for(var/mob/living/victim in passengers) + to_chat(victim, SPAN_DANGER("The exosuit shears apart around you, throwing you from the debris!")) + victim.throw_at_random(FALSE,2,1, 32) + + playsound(mover, 'sound/effects/break_stone.ogg', 70, 1) + + var/fall_time = 3 + if(displaced) // Make the fall look more natural when falling sideways. + mover.pixel_z = 32 * 2 + animate(mover, pixel_z = 0, time = fall_time) + + sleep(fall_time) // A brief delay inbetween the two sounds helps sell the 'ouch' effect. + + if(QDELETED(src) || QDELETED(mover) || QDELETED(turf)) + return + + if(safe_fall) + visible_message(SPAN_NOTICE("\The [mover] lands on \the [turf].")) + playsound(mover, "rustle", 25, 1) + return + + playsound(mover, "punch", 70, 1) + + visible_message(SPAN_DANGER("\The [mover] hits \the [turf]!")) + + if(faller) + // The bigger they are, the harder they fall. + // They will take at least 20 damage at the minimum, and tries to scale up to 40% of their max health. + // This scaling is capped at 100 total damage, which occurs if the thing that fell has more than 250 health. + faller.take_damage(clamp(faller.get_max_health() * 0.4, 20, 100) * harm, BRUTE, ran_zone(), inflicter = src) + shake_camera(faller, 1, 1) + + // Now fall off more cliffs below this one if they exist. + var/obj/structure/cliff/bottom_cliff = locate() in turf + if(bottom_cliff && !QDELETED(mover)) // Exosuits are deleted when destroyed. This is to prevent phantom exosuits. + visible_message(SPAN_DANGER("\The [mover] rolls down towards \the [bottom_cliff]!")) + addtimer(CALLBACK(bottom_cliff, TYPE_PROC_REF(/obj/structure/cliff, fall_off_cliff), mover), 5) + +/obj/structure/cliff/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) + // Cliff climbing requires climbing gear. + if(ishuman(user)) + var/mob/living/human/H = user + var/obj/item/clothing/shoes/shoes = H.get_equipped_item(slot_shoes_str) + if(shoes?.rock_climbing) + return ..() // Do the other checks too. + if(!silent) + to_chat(user, SPAN_WARNING("\The [src] is too steep to climb unassisted.")) + return FALSE + +// This tells AI mobs to not be dumb and step off cliffs willingly. +/obj/structure/cliff/is_safe_to_step(mob/living/stepper) + if(should_fall(stepper)) + return FALSE + return ..() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/hydroponics.dm b/code/game/objects/structures/crates_lockers/closets/secure/hydroponics.dm index f8a63b950114..78be7fdb88bc 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/hydroponics.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/hydroponics.dm @@ -6,7 +6,7 @@ /obj/structure/closet/secure_closet/hydroponics/WillContain() return list( new /datum/atom_creator/weighted(list(/obj/item/clothing/suit/apron, /obj/item/clothing/suit/apron/overalls)), - /obj/item/plants, + /obj/item/plant_satchel, /obj/item/clothing/jumpsuit/hydroponics, /obj/item/scanner/plant, /obj/item/radio/headset/headset_service, diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 6b85c90efb6d..3d4ff0479f34 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -249,7 +249,7 @@ return list( /obj/item/chems/spray/plantbgone = 2, /obj/item/tool/hoe/mini = 2, - /obj/item/plants = 2, + /obj/item/plant_satchel = 2, /obj/item/tool/axe/hatchet = 2, /obj/item/wirecutters/clippers = 2, /obj/item/scanner/plant = 2 diff --git a/code/game/objects/structures/curtain_decls.dm b/code/game/objects/structures/curtain_decls.dm index 909a5fcc418f..fa5524530c01 100644 --- a/code/game/objects/structures/curtain_decls.dm +++ b/code/game/objects/structures/curtain_decls.dm @@ -58,6 +58,9 @@ /decl/curtain_kind/plastic/shower/security color = COLOR_DARK_RED +/decl/curtain_kind/plastic/shower/medical + color = COLOR_CYAN + /decl/curtain_kind/plastic/canteen name = "privacy curtain" color = COLOR_BLUE_GRAY diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index 0ef9f5dfbce3..e15b1fc302c5 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -167,6 +167,8 @@ curtain_kind_path = /decl/curtain_kind/plastic/shower/engineering /obj/item/curtain/shower/security curtain_kind_path = /decl/curtain_kind/plastic/shower/security +/obj/item/curtain/shower/medical + curtain_kind_path = /decl/curtain_kind/plastic/shower/medical /obj/item/curtain/canteen curtain_kind_path = /decl/curtain_kind/plastic/canteen @@ -225,3 +227,6 @@ /obj/structure/curtain/open/shower/security curtain_kind_path = /decl/curtain_kind/plastic/shower/security color = /decl/curtain_kind/plastic/shower/security::color +/obj/structure/curtain/open/shower/medical + curtain_kind_path = /decl/curtain_kind/plastic/shower/medical + color = /decl/curtain_kind/plastic/shower/medical::color diff --git a/code/game/objects/structures/drying_rack.dm b/code/game/objects/structures/drying_rack.dm index 3feffa51de95..6f116ab4451f 100644 --- a/code/game/objects/structures/drying_rack.dm +++ b/code/game/objects/structures/drying_rack.dm @@ -8,6 +8,16 @@ material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC var/obj/item/drying +/obj/structure/drying_rack/Initialize(ml, _mat, _reinf_mat) + . = ..() + // This is mostly for serde. + for(var/obj/item/thing in get_contained_external_atoms()) + if(!drying && thing.is_dryable()) + drying = thing + update_icon() + else + thing.dropInto(loc) + /obj/structure/drying_rack/ebony material = /decl/material/solid/organic/wood/ebony color = /decl/material/solid/organic/wood/ebony::color diff --git a/code/game/objects/structures/fences.dm b/code/game/objects/structures/fences.dm index 05eb791fb898..1ed302b304a5 100644 --- a/code/game/objects/structures/fences.dm +++ b/code/game/objects/structures/fences.dm @@ -22,6 +22,7 @@ icon_state = "straight" material = /decl/material/solid/metal/steel + material_alteration = MAT_FLAG_ALTERATION_ALL tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT var/cuttable = TRUE diff --git a/code/game/objects/structures/fishtanks.dm b/code/game/objects/structures/fishtanks.dm index c9ab4bc5f72e..2e828ed6e803 100644 --- a/code/game/objects/structures/fishtanks.dm +++ b/code/game/objects/structures/fishtanks.dm @@ -138,25 +138,25 @@ var/global/list/global/aquarium_states_and_layers = list( for(var/atom/movable/AM in get_contained_external_atoms()) add_overlay(AM) -/obj/structure/glass_tank/can_climb(var/mob/living/user, post_climb_check=0) +/obj/structure/glass_tank/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) if (!user.can_touch(src) || !(atom_flags & ATOM_FLAG_CLIMBABLE) || (!post_climb_check && (user in climbers))) - return 0 - + return FALSE if (!Adjacent(user)) - to_chat(user, SPAN_DANGER("You can't climb there, the way is blocked.")) - return 0 - + if(!silent) + to_chat(user, SPAN_WARNING("You can't climb there, the way is blocked.")) + return FALSE var/obj/occupied = turf_is_crowded() if(occupied) - to_chat(user, SPAN_DANGER("There's \a [occupied] in the way.")) - return 0 - return 1 + if(!silent) + to_chat(user, SPAN_WARNING("There's \a [occupied] in the way.")) + return FALSE + return TRUE /obj/structure/glass_tank/do_climb(var/mob/living/user) if(!istype(user) || !can_climb(user)) return user.visible_message(SPAN_WARNING("\The [user] starts climbing into \the [src]!")) - if(!do_after(user,50)) + if(!do_after(user, 5 SECONDS)) return if (!can_climb(user)) return diff --git a/code/game/objects/structures/flaps.dm b/code/game/objects/structures/flaps.dm index b3b305a9f946..06ba909c7b45 100644 --- a/code/game/objects/structures/flaps.dm +++ b/code/game/objects/structures/flaps.dm @@ -18,6 +18,7 @@ /mob/living/silicon/robot/drone ) var/airtight = FALSE + var/can_pass_lying = TRUE /obj/structure/flaps/CanPass(atom/A, turf/T) if(istype(A) && A.checkpass(PASS_FLAG_GLASS)) @@ -32,7 +33,7 @@ var/mob/living/M = A if(istype(M)) - if(M.current_posture.prone && !M.buckled) + if(M.current_posture.prone && can_pass_lying) return ..() for(var/mob_type in mobs_can_pass) if(istype(A, mob_type)) @@ -80,4 +81,12 @@ update_nearby_tiles() /obj/structure/flaps/airtight // airtight defaults to on - airtight = TRUE \ No newline at end of file + airtight = TRUE + +/obj/structure/flaps/animal + name = "animal access flaps" // doggy door + airtight = TRUE + can_pass_lying = FALSE + mobs_can_pass = list( + /mob/living/simple_animal + ) \ No newline at end of file diff --git a/code/game/objects/structures/flora/plant_serde.dm b/code/game/objects/structures/flora/plant_serde.dm new file mode 100644 index 000000000000..26cfbedac5e9 --- /dev/null +++ b/code/game/objects/structures/flora/plant_serde.dm @@ -0,0 +1,7 @@ +/obj/structure/flora/plant/ShouldSerialize(_age) + return plant?.roundstart && ..(_age) + +/obj/structure/flora/plant/Serialize() + . = ..() + if(plant && plant.name != initial(plant)) + .[nameof(/obj/structure/flora/plant::plant)] = plant.name diff --git a/code/game/objects/structures/mineral_bath.dm b/code/game/objects/structures/mineral_bath.dm index 1ed4d581c726..1ff9947a566c 100644 --- a/code/game/objects/structures/mineral_bath.dm +++ b/code/game/objects/structures/mineral_bath.dm @@ -137,7 +137,7 @@ for(var/obj/item/organ/external/limb in occupant.get_external_organs()) if(BP_IS_PROSTHETIC(limb)) for(var/obj/implanted_object in limb.implants) - if(!istype(implanted_object,/obj/item/implant) && !istype(implanted_object,/obj/item/organ/internal/augment) && prob(25)) // We don't want to remove REAL implants. Just shrapnel etc. + if(!should_dissolve_implant(implanted_object)) // We don't want to remove REAL implants. Just shrapnel etc. LAZYREMOVE(limb.implants, implanted_object) to_chat(occupant, SPAN_NOTICE("The mineral-rich bath dissolves the [implanted_object.name].")) qdel(implanted_object) @@ -149,3 +149,8 @@ limb.status |= ORGAN_BRITTLE to_chat(occupant, SPAN_WARNING("It feels a bit brittle, though...")) break + +/obj/structure/mineral_bath/proc/should_dissolve_implant(obj/implanted_object) + if(istype(implanted_object, /obj/item/implant)) + return FALSE + return prob(25) diff --git a/code/game/objects/structures/racks.dm b/code/game/objects/structures/racks.dm index cda6216899a8..4830025c3535 100644 --- a/code/game/objects/structures/racks.dm +++ b/code/game/objects/structures/racks.dm @@ -15,6 +15,9 @@ anchored = TRUE structure_flags = STRUCTURE_FLAG_SURFACE +/obj/structure/rack/steel + material = /decl/material/solid/metal/steel + /obj/structure/rack/Initialize() ..() return INITIALIZE_HINT_LATELOAD diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index b04f59f9a150..776c5162398b 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -27,6 +27,9 @@ color = COLOR_ORANGE paint_color = COLOR_ORANGE +/obj/structure/railing/mapped/grey + paint_color = COLOR_SILVER + /obj/structure/railing/mapped/no_density density = FALSE @@ -314,13 +317,13 @@ WOOD_RAILING_SUBTYPE(yew) if(!QDELETED(src)) qdel(src) -/obj/structure/railing/can_climb(var/mob/living/user, post_climb_check=0) - . = ..() - if(. && get_turf(user) == get_turf(src)) +/obj/structure/railing/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) + if((. = ..()) && get_turf(user) == get_turf(src)) var/turf/T = get_step(src, dir) if(T.turf_is_crowded(user)) - to_chat(user, "You can't climb there, the way is blocked.") - return 0 + if(!silent) + to_chat(user, SPAN_WARNING("You can't climb there, the way is blocked.")) + return FALSE /obj/structure/railing/do_climb(var/mob/living/user) . = ..() diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs/_signs.dm similarity index 90% rename from code/game/objects/structures/signs.dm rename to code/game/objects/structures/signs/_signs.dm index 0b1245e25d7c..2241befe6254 100644 --- a/code/game/objects/structures/signs.dm +++ b/code/game/objects/structures/signs/_signs.dm @@ -87,7 +87,7 @@ ///A wall mountable sign structure /obj/structure/sign name = "sign" - icon = 'icons/obj/signs/warnings.dmi' + icon = 'icons/obj/signs/signs.dmi' anchored = TRUE opacity = FALSE density = FALSE @@ -134,3 +134,18 @@ /obj/structure/sign/double/handle_default_screwdriver_attackby(mob/user, obj/item/screwdriver) return FALSE + +/obj/structure/sign/clock + name = "clock" + desc = "It's a functionally useless print-out of a clock face." + icon_state = "clock" + +/obj/structure/sign/calendar + name = "calendar" + desc = "It's a functionally useless print-out of a calendar." + icon_state = "calendar" + +/obj/structure/sign/periodic_table + name = "periodic table" + desc = "It's an old, outdated copy of the periodic table of elements." + icon_state = "periodic" diff --git a/code/game/objects/structures/signs/bar_signs.dm b/code/game/objects/structures/signs/bar_signs.dm index 51396fea7bbf..1d53cf2fa3a8 100644 --- a/code/game/objects/structures/signs/bar_signs.dm +++ b/code/game/objects/structures/signs/bar_signs.dm @@ -1,4 +1,3 @@ - /obj/structure/sign/double/maltesefalcon name = "The Maltese Falcon" desc = "The Maltese Falcon, Space Bar and Grill." @@ -13,3 +12,4 @@ /obj/structure/sign/double/maltesefalcon/right icon_state = "maltesefalcon-right" + diff --git a/code/game/objects/structures/signs/decks.dm b/code/game/objects/structures/signs/decks.dm new file mode 100644 index 000000000000..3fc6dadc59fd --- /dev/null +++ b/code/game/objects/structures/signs/decks.dm @@ -0,0 +1,95 @@ +///////////////////////////////////////////////////// +// Deck Signs +///////////////////////////////////////////////////// + +///A sign for indicating what level is the current level vertically +/obj/structure/sign/deck + abstract_type = /obj/structure/sign/deck + name = "current level sign" + desc = "A sign indicating on what level the observer is currently on." + icon = 'icons/obj/signs/decks.dmi' + +///////////////////////////////////////////////////// +// Deck Signs Definition +///////////////////////////////////////////////////// + +/obj/structure/sign/deck/bridge + name = "\improper Bridge Deck" + icon_state = "deck-b" + +/obj/structure/sign/deck/first + name = "\improper First Deck" + icon_state = "deck-1" + +/obj/structure/sign/deck/second + name = "\improper Second Deck" + icon_state = "deck-2" + +/obj/structure/sign/deck/third + name = "\improper Third Deck" + icon_state = "deck-3" + +/obj/structure/sign/deck/fourth + name = "\improper Fourth Deck" + icon_state = "deck-4" + +/obj/structure/sign/deck/fifth + name = "\improper Fifth Deck" + icon_state = "deck-5" + +/obj/structure/sign/deck/bridge/large + icon_state = "deck-b-large" + +/obj/structure/sign/deck/first/large + icon_state = "deck-1-large" + +/obj/structure/sign/deck/second/large + icon_state = "deck-2-large" + +/obj/structure/sign/deck/third/large + icon_state = "deck-3-large" + +/obj/structure/sign/deck/fourth/large + icon_state = "deck-4-large" + +/obj/structure/sign/deck/level_basement + name = "\improper Basemenet Level" + icon_state = "level-b" + +/obj/structure/sign/deck/level_one + name = "\improper Level One" + icon_state = "level-1" + +/obj/structure/sign/deck/level_two + name = "\improper Level Two" + icon_state = "level-2" + +/obj/structure/sign/deck/level_three + name = "\improper Level Three" + icon_state = "level-3" + +/obj/structure/sign/deck/level_four + name = "\improper Level Four" + icon_state = "level-4" + +/obj/structure/sign/deck/level_ground + name = "\improper Ground Level" + icon_state = "level-g" + +/obj/structure/sign/deck/level_basement/large + icon_state = "level-b-large" + +/obj/structure/sign/deck/level_one/large + icon_state = "level-1-large" + +/obj/structure/sign/deck/level_two/large + icon_state = "level-2-large" + +/obj/structure/sign/deck/level_three/large + icon_state = "level-3-large" + +/obj/structure/sign/deck/level_four/large + icon_state = "level-4-large" + +/obj/structure/sign/deck/level_ground/large + icon_state = "level-g-large" diff --git a/code/game/objects/structures/signs/department_signs.dm b/code/game/objects/structures/signs/departments.dm similarity index 77% rename from code/game/objects/structures/signs/department_signs.dm rename to code/game/objects/structures/signs/departments.dm index 92fa661be71e..0018ae5d6a88 100644 --- a/code/game/objects/structures/signs/department_signs.dm +++ b/code/game/objects/structures/signs/departments.dm @@ -4,7 +4,7 @@ /obj/structure/sign/department abstract_type = /obj/structure/sign/department - icon = 'icons/obj/signs/slim_location_signs.dmi' + icon = 'icons/obj/signs/departments.dmi' /////////////////////////////////////////////////////////////////////////////////// // Department Sign Definitions @@ -18,36 +18,37 @@ /obj/structure/sign/department/science_2 name = "\improper RESEARCH" desc = "A sign labelling an area where research is performed." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "science2" +/obj/structure/sign/department/science_3 + icon_state = "science1" + /obj/structure/sign/department/xenobio_1 name = "\improper XENOBIOLOGY" desc = "A sign labelling an area as a place where xenobiological entities are researched." icon_state = "xenobio" +/obj/structure/sign/department/xenobio_1/large + icon_state = "xenobio-large" + /obj/structure/sign/department/xenobio_2 name = "\improper XENOBIOLOGY" desc = "A sign labelling an area as a place where xenobiological entities are researched." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "xenobio2" /obj/structure/sign/department/xenobio_3 name = "\improper XENOBIOLOGY" desc = "A sign labelling an area as a place where xenobiological entities are researched." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "xenobio3" /obj/structure/sign/department/xenobio_4 name = "\improper XENOBIOLOGY" desc = "A sign labelling an area as a place where xenobiological entities are researched." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "xenobio4" /obj/structure/sign/department/xenoarch name = "\improper XENOARCHAEOLOGY" desc = "A sign labelling an area as a place where xenoarchaeological finds are researched." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "xenobio4" /obj/structure/sign/department/chemistry @@ -55,16 +56,20 @@ desc = "A sign labelling an area containing chemical equipment." icon_state = "chemistry" +/obj/structure/sign/department/chemistry/alt_1 + icon_state = "chemistry1" + +/obj/structure/sign/department/chemistry/alt_2 + icon_state = "chemistry2" + /obj/structure/sign/department/xenoflora name = "\improper XENOFLORA" desc = "A sign labelling an area as a place where xenobiological plants are researched." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "hydro4" /obj/structure/sign/department/botany name = "\improper BOTANY" - desc = "A warning sign which reads 'BOTANY!'." - icon = 'icons/obj/signs/location_signs.dmi' + desc = "A warning sign which reads 'BOTANY'." icon_state = "hydro3" /obj/structure/sign/department/hydro @@ -75,19 +80,30 @@ /obj/structure/sign/department/hydrostorage name = "\improper HYDROPONICS STORAGE" desc = "A sign labelling an area as a place where plant growing supplies are kept." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "hydro3" +/obj/structure/sign/department/hydro/alt_1 + icon_state = "hydro1" + +/obj/structure/sign/department/hydro/alt_2 + icon_state = "hydro2" + /obj/structure/sign/department/janitor name = "\improper JANITORIAL CLOSET" desc = "A sign indicating a room used to store cleaning supplies." icon_state = "janitor" +/obj/structure/sign/department/janitor/alt + icon_state = "custodian" + /obj/structure/sign/department/engineering name = "\improper ENGINEERING" desc = "A sign labelling an area as the Engineering department." icon_state = "engineering" +/obj/structure/sign/department/engineering/engine + icon_state = "engine" + /obj/structure/sign/department/telecomms name = "\improper TELECOMMUNICATIONS" desc = "A sign labelling an area as the Telecommunications room." @@ -98,11 +114,17 @@ desc = "A sign labelling the area as a cargo bay." icon_state = "cargo" +/obj/structure/sign/department/cargo/large + icon_state = "cargo-large" + /obj/structure/sign/department/mail_delivery name = "\improper MAIL DELIVERY" desc = "A sign labelling a mail delivery point." icon_state = "mail" +/obj/structure/sign/department/mail_delivery/large + icon_state = "mail-large" + /obj/structure/sign/department/bridge name = "\improper BRIDGE" desc = "A sign indicating the Bridge. Not the kind you cross rivers with, the other kind." @@ -121,6 +143,9 @@ /obj/structure/sign/department/security/alt icon_state = "sec_cuff" +/obj/structure/sign/department/security/large + icon_state = "security" + /obj/structure/sign/department/eva name = "\improper EVA" desc = "A sign indicating this is where Extra Vehicular Activity equipment is stored." @@ -141,41 +166,44 @@ desc = "A sign that lets you know that this is where you want to be when the station is full of holes and on fire." icon_state = "evac" -/obj/structure/sign/department/watercloset - name = "\improper BATHROOMS" +/obj/structure/sign/department/evac/large + icon_state = "evac-large" + +/obj/structure/sign/department/restroom + name = "restroom" desc = "Need to take a piss? You've come to the right place." icon_state = "watercloset" +/obj/structure/sign/department/restroom/alt + icon_state = "restroom" + /obj/structure/sign/department/examroom - name = "\improper Exam Room" + name = "exam room" icon_state = "examroom" -/obj/structure/sign/department/redcross +/obj/structure/sign/department/examroom/large + icon_state = "examroom-large" + +/obj/structure/sign/department/cross name = "medbay" desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." - icon = 'icons/obj/signs/medical.dmi' icon_state = "redcross" -/obj/structure/sign/department/greencross - name = "medbay" - desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." - icon = 'icons/obj/signs/medical.dmi' +/obj/structure/sign/department/cross/green icon_state = "greencross" -/obj/structure/sign/department/bluecross_1 - name = "infirmary" - desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." - icon = 'icons/obj/signs/medical.dmi' +/obj/structure/sign/department/cross/blue icon_state = "bluecross" -/obj/structure/sign/department/bluecross_2 - name = "infirmary" - desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." - icon = 'icons/obj/signs/medical.dmi' +/obj/structure/sign/department/cross/blue2 icon_state = "bluecross2" -/obj/structure/sign/department/star_of_life +/obj/structure/sign/department/cross/star_of_life name = "emergency" desc = "The blue six-pointed star with a rod of Asclepius is the intergalactic symbol of emergency medical services." - icon = 'icons/obj/signs/medical.dmi' - icon_state = "staroflife" \ No newline at end of file + icon_state = "staroflife" + +/obj/structure/sign/department/chapel + name = "\improper CHAPEL" + desc = "A sign labelling this area as the Chapel." + icon_state = "holy" diff --git a/code/game/objects/structures/signs/direction_signs.dm b/code/game/objects/structures/signs/direction_signs.dm deleted file mode 100644 index 1b331afc9941..000000000000 --- a/code/game/objects/structures/signs/direction_signs.dm +++ /dev/null @@ -1,120 +0,0 @@ -///////////////////////////////////////////////////// -//Direction Signs -///////////////////////////////////////////////////// - -///Signs for showing the way to passerby. The dir of the sign is the direction it points towards. The icon of the sign itself is always south facing. -/obj/structure/sign/directions - name = "direction sign" - desc = "A direction sign, claiming to know the way." - icon = 'icons/obj/signs/directions.dmi' - icon_state = "direction" - //Direction signs are always meant to face south! The arrow on the sign matches the direction it points to. - directional_offset = @'{"NORTH":{"y":32}, "SOUTH":{"y":32}, "WEST":{"y":32}, "EAST":{"y":32}}' - -/obj/structure/sign/directions/update_description() - desc = "A direction sign, pointing out \the [name] is [global.dir_name(dir)]." - -///////////////////////////////////////////////////// -//Direction Signs Definition -///////////////////////////////////////////////////// - -/obj/structure/sign/directions/science - name = "\improper Research Division" - icon_state = "direction_sci" - -/obj/structure/sign/directions/engineering - name = "\improper Engineering Bay" - icon_state = "direction_eng" - -/obj/structure/sign/directions/security - name = "\improper Security Wing" - icon_state = "direction_sec" - -/obj/structure/sign/directions/medical - name = "\improper Medical Bay" - icon_state = "direction_med" - -/obj/structure/sign/directions/evac - name = "\improper Evacuation Wing" - icon_state = "direction_evac" - -/obj/structure/sign/directions/bridge - name = "\improper Bridge" - icon_state = "direction_bridge" - -/obj/structure/sign/directions/supply - name = "\improper Supply Office" - icon_state = "direction_supply" - -/obj/structure/sign/directions/infirmary - name = "\improper Infirmary" - icon_state = "direction_infirm" - -/obj/structure/sign/directions/pods - name = "\improper ESCAPE PODS" - icon_state = "direction_pods" - -/obj/structure/sign/directions/cryo - name = "\improper Cryogenic Storage" - icon_state = "direction_cryo" - - -///////////////////////////////////////////////////// -// Hangar Signs -///////////////////////////////////////////////////// -/obj/structure/sign/hangar - abstract_type = /obj/structure/sign/hangar - name = "hangar sign" - desc = "A sign indicating which hangar the observer is near." - icon = 'icons/obj/signs/hangars.dmi' - -/obj/structure/sign/hangar/one - name = "\improper Hangar One" - icon_state = "hangar-1" - -/obj/structure/sign/hangar/two - name = "\improper Hangar Two" - icon_state = "hangar-2" - -/obj/structure/sign/hangar/three - name = "\improper Hangar Three" - icon_state = "hangar-3" - -///////////////////////////////////////////////////// -// Deck Signs -///////////////////////////////////////////////////// - -///A sign for indicating what level is the current level vertically -/obj/structure/sign/deck - abstract_type = /obj/structure/sign/deck - name = "current level sign" - desc = "A sign indicating on what level the observer is currently on." - icon = 'icons/obj/signs/slim_decks.dmi' - -///////////////////////////////////////////////////// -// Deck Signs Definition -///////////////////////////////////////////////////// - -/obj/structure/sign/deck/bridge - name = "\improper Bridge Deck" - icon_state = "deck-b" - -/obj/structure/sign/deck/first - name = "\improper First Deck" - icon_state = "deck-1" - -/obj/structure/sign/deck/second - name = "\improper Second Deck" - icon_state = "deck-2" - -/obj/structure/sign/deck/third - name = "\improper Third Deck" - icon_state = "deck-3" - -/obj/structure/sign/deck/fourth - name = "\improper Fourth Deck" - icon_state = "deck-4" - -/obj/structure/sign/deck/fifth - name = "\improper Fifth Deck" - icon_state = "deck-5" \ No newline at end of file diff --git a/code/game/objects/structures/signs/directions.dm b/code/game/objects/structures/signs/directions.dm new file mode 100644 index 000000000000..c89ae69448fb --- /dev/null +++ b/code/game/objects/structures/signs/directions.dm @@ -0,0 +1,282 @@ +///////////////////////////////////////////////////// +//Direction Signs +///////////////////////////////////////////////////// + +///Signs for showing the way to passerby. The dir of the sign is the direction it points towards. The icon of the sign itself is always south facing. +/obj/structure/sign/directions + name = "direction sign" + desc = "A direction sign, claiming to know the way." + icon = 'icons/obj/signs/directions.dmi' + icon_state = "direction" + //Direction signs are always meant to face south! The arrow on the sign matches the direction it points to. + directional_offset = @'{"NORTH":{"y":32}, "SOUTH":{"y":32}, "WEST":{"y":32}, "EAST":{"y":32}}' + +/obj/structure/sign/directions/update_description() + desc = "A direction sign, pointing out \the [name] is [global.dir_name(dir)]." + +///////////////////////////////////////////////////// +//Direction Signs Definition +///////////////////////////////////////////////////// + +/obj/structure/sign/directions/science + name = "\improper Research Division" + icon_state = "direction_sci" + +/obj/structure/sign/directions/science/xeno + name = "\improper Xenobiology" + icon_state = "direction_xeno" + +/obj/structure/sign/directions/science/xenoarch + name = "\improper Xenoarchaeology" + icon_state = "direction_xenoarch" + +/obj/structure/sign/directions/science/xenoflora + name = "\improper Xenoflora" + icon_state = "direction_xflora" + +/obj/structure/sign/directions/science/xenobiology + icon_state = "direction_xbio" + +/obj/structure/sign/directions/science/exploration + name = "\improper Exploration" + icon_state = "direction_explo" + +/obj/structure/sign/directions/science/toxins + name = "\improper Toxins" + icon_state = "direction_toxins" + +/obj/structure/sign/directions/science/robotics + name = "\improper Robotics" + icon_state = "direction_robotics" + +/obj/structure/sign/directions/science/rnd + name = "\improper Research and Development" + icon_state = "direction_rnd" + +/obj/structure/sign/directions/engineering + name = "\improper Engineering Bay" + icon_state = "direction_eng" + +/obj/structure/sign/directions/engineering/solars + name = "\improper Solar Array" + icon_state = "direction_solar" + +/obj/structure/sign/directions/engineering/engeqp + name = "\improper Engineering Equipment" + icon_state = "direction_engeqp" + +/obj/structure/sign/directions/engineering/reactor + name = "\improper Reactor Core" + icon_state = "direction_core" + +/obj/structure/sign/directions/engineering/atmospherics + name = "\improper Atmospherics" + icon_state = "direction_atmos" + +/obj/structure/sign/directions/cargo + name = "\improper Cargo" + icon_state = "direction_crg" + +/obj/structure/sign/directions/cargo/supply + name = "\improper Supply Office" + icon_state = "direction_supply" + +/obj/structure/sign/directions/cargo/mining + name = "\improper Mining" + icon_state = "direction_mining" + +/obj/structure/sign/directions/cargo/refinery + name = "\improper Refinery" + icon_state = "direction_refinery" + +/obj/structure/sign/directions/security + name = "\improper Security Wing" + icon_state = "direction_sec" + +/obj/structure/sign/directions/security/interrogation + name = "\improper Interrogation" + icon_state = "direction_interrogation" + +/obj/structure/sign/directions/security/internal_affairs + name = "\improper Internal Affairs" + icon_state = "direction_intaff" + +/obj/structure/sign/directions/security/forensics + name = "\improper Forensics" + icon_state = "direction_forensics" + +/obj/structure/sign/directions/security/forensics/alt + name = "\improper Forensics Laboratory" + icon_state = "direction_lab" + +/obj/structure/sign/directions/security/brig + name = "\improper Security Brig" + icon_state = "direction_brig" + +/obj/structure/sign/directions/security/armory + name = "\improper Security Armory" + icon_state = "direction_armory" + +/obj/structure/sign/directions/security/seceqp + name = "\improper Security Equipment" + icon_state = "direction_seceqp" + +/obj/structure/sign/directions/medical + name = "\improper Medical Bay" + icon_state = "direction_med" + +/obj/structure/sign/directions/medical/morgue + name = "\improper Morgue" + icon_state = "direction_morgue" + +/obj/structure/sign/directions/medical/equipment + name = "\improper Medical Equipment" + icon_state = "direction_medeqp" + +/obj/structure/sign/directions/medical/virology + name = "\improper Virology" + icon_state = "direction_viro" + +/obj/structure/sign/directions/medical/surgery + name = "\improper Surgery" + icon_state = "direction_surgery" + +/obj/structure/sign/directions/medical/operating_1 + name = "\improper Operating Theatre 1" + icon_state = "direction_op1" + +/obj/structure/sign/directions/medical/operating_2 + name = "\improper Operating Theatre 2" + icon_state = "direction_op2" + +/obj/structure/sign/directions/medical/cloning + name = "\improper Cloning" + icon_state = "direction_cloning" + +/obj/structure/sign/directions/medical/resleeve + name = "\improper Resleeving" + icon_state = "direction_resleeve" + +/obj/structure/sign/directions/medical/chemlab + name = "\improper Chemistry Laboratory" + icon_state = "direction_chemlab" + +/obj/structure/sign/directions/evac + name = "\improper Evacuation Wing" + icon_state = "direction_evac" + +/obj/structure/sign/directions/bridge + name = "\improper Bridge" + icon_state = "direction_bridge" + +/obj/structure/sign/directions/infirmary + name = "\improper Infirmary" + icon_state = "direction_infirm" + +/obj/structure/sign/directions/pods + name = "\improper Escape Pods" + icon_state = "direction_pods" + +/obj/structure/sign/directions/cryo + name = "\improper Cryogenic Storage" + icon_state = "direction_cryo" + +/obj/structure/sign/directions/elevator + name = "\improper Elevator" + icon_state = "direction_elv" + +/obj/structure/sign/directions/command + name = "\improper Command" + icon_state = "direction_command" + +/obj/structure/sign/directions/dorms + name = "\improper Dormitories" + icon_state = "direction_dorms" + +/obj/structure/sign/directions/teleporter + name = "\improper Teleporter" + icon_state = "direction_teleport" + +/obj/structure/sign/directions/roomnum + name = "\improper Private Room" + icon_state = "direction_roomnum" // TODO: move this to signs.dmi or something. + +/obj/structure/sign/directions/recreation + name = "\improper Recreation" + icon_state = "direction_recreation" + +/obj/structure/sign/directions/pool + name = "\improper Pool" + icon_state = "direction_pool" + +/obj/structure/sign/directions/janitor + name = "\improper Custodial Office" + icon_state = "direction_janitor" + +/obj/structure/sign/directions/eva + name = "\improper EVA" + icon_state = "direction_eva" + +/obj/structure/sign/directions/bar + name = "\improper Bar" + icon_state = "direction_bar" + +/obj/structure/sign/directions/ai_core + name = "\improper AI Core" + icon_state = "direction_ai_core" + +/obj/structure/sign/directions/gravity + name = "\improper Gravity Management" + icon_state = "direction_grav" + +/obj/structure/sign/directions/telecomms + name = "\improper Telecommunications" + icon_state = "direction_tcomms" + +/obj/structure/sign/directions/kitchen + name = "\improper Kitchen" + icon_state = "direction_kitchen" + +/obj/structure/sign/directions/tram + name = "\improper Transit" + icon_state = "direction_tram" + +/obj/structure/sign/directions/chapel + name = "\improper Chapel" + icon_state = "direction_chapel" + +/obj/structure/sign/directions/library + name = "\improper Library" + icon_state = "direction_library" + +/obj/structure/sign/directions/dock + name = "\improper Dock" + icon_state = "direction_dock" + +/obj/structure/sign/directions/gym + name = "\improper Gymnasium" + icon_state = "direction_gym" + +/obj/structure/sign/directions/exit + name = "\improper Emergency Exit" + icon_state = "exit_sign" + +/obj/structure/sign/directions/stairs + name = "\improper Stairwell" + icon_state = "stairwell" + +/obj/structure/sign/directions/stairs/up + icon_state = "stairs_up" + +/obj/structure/sign/directions/stairs/down + icon_state = "stairs_down" + +/obj/structure/sign/directions/ladder + name = "\improper Ladder" + icon_state = "ladderwell" + +/obj/structure/sign/directions/ladder/up + icon_state = "ladder_up" + +/obj/structure/sign/directions/ladder/down + icon_state = "ladder_down" diff --git a/code/game/objects/structures/signs/flags.dm b/code/game/objects/structures/signs/flags.dm new file mode 100644 index 000000000000..8fce965bc1aa --- /dev/null +++ b/code/game/objects/structures/signs/flags.dm @@ -0,0 +1,27 @@ +/obj/structure/sign/double/flag + name = "flag" + desc = "A plain flag." + icon = 'icons/obj/signs/flags.dmi' + icon_state = "flag" +/obj/structure/sign/double/flag/left + icon_state = "flag_l" +/obj/structure/sign/double/flag/right + icon_state = "flag_r" + +/obj/structure/sign/double/flag/pirate + name = "pirate flag" + desc = "Yarr harr, fiddle de dee!" + icon_state = "pirate" +/obj/structure/sign/double/flag/pirate/left + icon_state = "pirate_l" +/obj/structure/sign/double/flag/pirate/right + icon_state = "pirate_r" + +/obj/structure/sign/double/flag/catpirate + name = "cat pirate flag" + desc = "Nyarr harr, fiddle de dee!" + icon_state = "catpirate" +/obj/structure/sign/double/flag/catpirate/left + icon_state = "catpirate_l" +/obj/structure/sign/double/flag/catpirate/right + icon_state = "catpirate_r" diff --git a/code/game/objects/structures/signs/hangar.dm b/code/game/objects/structures/signs/hangar.dm new file mode 100644 index 000000000000..0ad300afe060 --- /dev/null +++ b/code/game/objects/structures/signs/hangar.dm @@ -0,0 +1,20 @@ +///////////////////////////////////////////////////// +// Hangar Signs +///////////////////////////////////////////////////// +/obj/structure/sign/hangar + abstract_type = /obj/structure/sign/hangar + name = "hangar sign" + desc = "A sign indicating which hangar the observer is near." + icon = 'icons/obj/signs/hangars.dmi' + +/obj/structure/sign/hangar/one + name = "\improper Hangar One" + icon_state = "hangar-1" + +/obj/structure/sign/hangar/two + name = "\improper Hangar Two" + icon_state = "hangar-2" + +/obj/structure/sign/hangar/three + name = "\improper Hangar Three" + icon_state = "hangar-3" diff --git a/code/game/objects/structures/signs/levels.dm b/code/game/objects/structures/signs/levels.dm new file mode 100644 index 000000000000..214b43bc6508 --- /dev/null +++ b/code/game/objects/structures/signs/levels.dm @@ -0,0 +1,232 @@ +/obj/structure/sign/levels + icon = 'icons/obj/signs/levels.dmi' + icon_state = "level" + //Level signs are always meant to face south! The arrow on the sign matches the direction it points to. + directional_offset = @'{"NORTH":{"y":32}, "SOUTH":{"y":32}, "WEST":{"y":32}, "EAST":{"y":32}}' + +/obj/structure/sign/levels/update_description() + desc = "A sign indicating your position within \the [name]." + +/obj/structure/sign/levels/engineering + name = "\improper Engineering" + icon_state = "level_eng" + +/obj/structure/sign/levels/engineering/core + name = "\improper Reactor Core" + icon_state = "level_core" + +/obj/structure/sign/levels/engineering/solar + name = "\improper Solar Array" + icon_state = "level_solar" + +/obj/structure/sign/levels/engineering/atmos + name = "\improper Atmospherics" + icon_state = "level_atmos" + +/obj/structure/sign/levels/engineering/gravity + name = "\improper Gravity Control" + icon_state = "level_grav" + +/obj/structure/sign/levels/engineering/equipment + name = "\improper Engineering Equipment" + icon_state = "level_engeqp" + +/obj/structure/sign/levels/medical + name = "\improper Medical" + icon_state = "level_med" + +/obj/structure/sign/levels/medical/virology + name = "\improper Virology" + icon_state = "level_viro" + +/obj/structure/sign/levels/medical/morgue + name = "\improper Morgue" + icon_state = "level_morgue" + +/obj/structure/sign/levels/medical/surgery + name = "\improper Surgery" + icon_state = "level_surgery" + +/obj/structure/sign/levels/medical/cloning + name = "\improper Cloning" + icon_state = "level_cloning" + +/obj/structure/sign/levels/medical/resleeve + name = "\improper Resleeving" + icon_state = "level_resleeve" + +/obj/structure/sign/levels/medical/chemlab + name = "\improper Chemistry" + icon_state = "level_chemlab" + +/obj/structure/sign/levels/medical/equipment + name = "\improper Medical Equipment" + icon_state = "level_medeqp" + +/obj/structure/sign/levels/medical/operating_1 + name = "\improper Operating Theatre 1" + icon_state = "level_op1" + +/obj/structure/sign/levels/medical/operating_2 + name = "\improper Operating Theatre 2" + icon_state = "level_op2" + +/obj/structure/sign/levels/security + name = "\improper Security" + icon_state = "level_sec" + +/obj/structure/sign/levels/security/seceqp + name = "\improper Security Equipment" + icon_state = "level_seceqp" + +/obj/structure/sign/levels/security/interrogation + name = "\improper Interrogation" + icon_state = "level_interrogation" + +/obj/structure/sign/levels/security/forensics + name = "\improper Forensics" + icon_state = "level_forensics" + +/obj/structure/sign/levels/security/brig + name = "\improper Security Brig" + icon_state = "level_brig" + +/obj/structure/sign/levels/security/armory + name = "\improper Security Armory" + icon_state = "level_armory" + +/obj/structure/sign/levels/security/internalaffairs + name = "\improper Internal Affairs" + icon_state = "level_intaff" + +/obj/structure/sign/levels/cryo + name = "\improper Cryogenics" + icon_state = "level_cry" + +/obj/structure/sign/levels/evac + name = "\improper Evac Wing" + icon_state = "level_evac" + +/obj/structure/sign/levels/eva + name = "\improper EVA" + icon_state = "level_eva" + +/obj/structure/sign/levels/command + name = "\improper Command" + icon_state = "level_command" + +/obj/structure/sign/levels/science + name = "\improper Research Wing" + icon_state = "level_sci" + +/obj/structure/sign/levels/science/xenoflora + name = "\improper Xenoflora" + icon_state = "level_xflora" + +/obj/structure/sign/levels/science/xenobiology + name = "\improper Xenobiology" + icon_state = "level_xbio" + +/obj/structure/sign/levels/science/exploration + name = "\improper Exploration" + icon_state = "level_explo" + +/obj/structure/sign/levels/science/robotics + name = "\improper Robotics" + icon_state = "level_robotics" + +/obj/structure/sign/levels/science/toxins + name = "\improper Toxins" + icon_state = "level_toxins" + +/obj/structure/sign/levels/science/xenoarch + name = "\improper Xenoarchaeology" + icon_state = "level_xenoarch" + +/obj/structure/sign/levels/science/rnd + name = "\improper Research and Development" + icon_state = "level_rnd" + +/obj/structure/sign/levels/dorms + name = "\improper Dormitories" + icon_state = "level_dorms" + +/obj/structure/sign/levels/cargo + name = "\improper Cargo" + icon_state = "level_crg" + +/obj/structure/sign/levels/cargo/mining + name = "\improper Mining" + icon_state = "level_mining" + +/obj/structure/sign/levels/cargo/refinery + name = "\improper Refinery" + icon_state = "level_refinery" + +/obj/structure/sign/levels/recreation + name = "\improper Recreation" + icon_state = "level_recreation" + +/obj/structure/sign/levels/laboratory + name = "\improper Laboratory" + icon_state = "level_lab" + +/obj/structure/sign/levels/xeno + name = "\improper Xenobiology" + icon_state = "level_xeno" + +/obj/structure/sign/levels/ai_core + name = "\improper AI Core" + icon_state = "level_ai_core" + +/obj/structure/sign/levels/bridge + name = "\improper Bridge" + icon_state = "level_bridge" + +/obj/structure/sign/levels/teleporter + name = "\improper Teleporter" + icon_state = "level_teleport" + +/obj/structure/sign/levels/telecomms + name = "\improper Telecommunications" + icon_state = "level_tcomms" + +/obj/structure/sign/levels/elevator + name = "\improper Elevator" + icon_state = "level_elv" + +/obj/structure/sign/levels/bar + name = "\improper Bar" + icon_state = "level_bar" + +/obj/structure/sign/levels/kitchen + name = "\improper Kitchen" + icon_state = "level_kitchen" + +/obj/structure/sign/levels/tram + name = "\improper Tram" + icon_state = "level_tram" + +/obj/structure/sign/levels/janitor + name = "\improper Janitor" + icon_state = "level_janitor" + +/obj/structure/sign/levels/chapel + name = "\improper Chapel" + icon_state = "level_chapel" + +/obj/structure/sign/levels/library + name = "\improper Library" + icon_state = "level_library" + +/obj/structure/sign/levels/dock + name = "\improper Docks" + icon_state = "level_dock" + +/obj/structure/sign/levels/gym + name = "\improper Gymnasium" + icon_state = "level_gym" + +/obj/structure/sign/levels/pool + name = "\improper Pool" + icon_state = "level_pool" diff --git a/code/game/objects/structures/signs/plaques.dm b/code/game/objects/structures/signs/plaques.dm index 19d72c4e1efc..4a4257437d12 100644 --- a/code/game/objects/structures/signs/plaques.dm +++ b/code/game/objects/structures/signs/plaques.dm @@ -19,6 +19,10 @@ /obj/structure/sign/plaque/dark icon_state = "darkplaque" +/obj/structure/sign/plaque/floor + desc = "A floor-mounted commemorative plaque." + icon_state = "floorplaque" + /obj/structure/sign/plaque/golden name = "The Most Robust Men Award for Robustness" desc = "To be Robust is not an action or a way of life, but a mental state. Only those with the force of Will strong enough to act during a crisis, saving friend from foe, are truly Robust. Stay Robust my friends." diff --git a/code/game/objects/structures/signs/warning_signs.dm b/code/game/objects/structures/signs/warning_signs.dm index 7165a62c8a04..2bcb946dec88 100644 --- a/code/game/objects/structures/signs/warning_signs.dm +++ b/code/game/objects/structures/signs/warning_signs.dm @@ -6,7 +6,7 @@ /obj/structure/sign/warning name = "\improper WARNING" desc = "You've been warned!" - icon = 'icons/obj/signs/slim_warnings.dmi' + icon = 'icons/obj/signs/warnings.dmi' icon_state = "securearea" directional_offset = @'{"NORTH":{"y":-32}, "SOUTH":{"y":32}, "WEST":{"x":34}, "EAST":{"x":-34}}' @@ -28,25 +28,28 @@ name = "\improper EXTERNAL AIRLOCK" icon_state = "doors" -/obj/structure/sign/warning/evac - name = "\improper KEEP CLEAR: EVAC DOCKING AREA" - icon = 'icons/obj/signs/warnings.dmi' - icon_state = "evac" +/obj/structure/sign/warning/airlock/large + icon_state = "doors-large" + +/obj/structure/sign/warning/pods + name = "\improper WARNING: ESCAPE POD DOCKING AREA" + icon_state = "pods" /obj/structure/sign/warning/deathsposal name = "\improper DISPOSAL LEADS TO SPACE" - icon = 'icons/obj/signs/warnings.dmi' icon_state = "deathsposal" /obj/structure/sign/warning/shock name = "\improper HIGH VOLTAGE" - icon = 'icons/obj/signs/warnings.dmi' icon_state = "shock" /obj/structure/sign/warning/compressed_gas name = "\improper COMPRESSED GAS" icon_state = "hikpa" +/obj/structure/sign/warning/compressed_gas/large + icon_state = "hikpa-large" + /obj/structure/sign/warning/docking_area name = "\improper KEEP CLEAR: DOCKING AREA" @@ -57,19 +60,24 @@ name = "\improper MOVING PARTS" icon_state = "movingparts" +/obj/structure/sign/warning/moving_parts/large + icon_state = "movingparts-large" + /obj/structure/sign/warning/nosmoking_1 name = "\improper NO SMOKING" icon_state = "nosmoking" +/obj/structure/sign/warning/nosmoking_1/large + icon_state = "nosmoking-large" + /obj/structure/sign/warning/nosmoking_2 name = "\improper NO SMOKING" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "nosmoking2" /obj/structure/sign/warning/nosmoking_burned name = "\improper NO SMOKING" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "nosmoking2_b" + /obj/structure/sign/warning/nosmoking_burned/update_description() . = ..() desc += " It looks charred." @@ -77,22 +85,37 @@ /obj/structure/sign/warning/smoking name = "\improper SMOKING" icon_state = "smoking" + /obj/structure/sign/warning/smoking/update_description() . = ..() desc += " Hell yeah." +/obj/structure/sign/warning/smoking/large + icon_state = "smoking-large" + /obj/structure/sign/warning/secure_area name = "\improper SECURE AREA" icon_state = "securearea2" +/obj/structure/sign/warning/secure_area/large + icon_state = "securearea2-large" + +/obj/structure/sign/warning/large + icon_state = "securearea-large" + /obj/structure/sign/warning/armory name = "\improper ARMORY" icon_state = "armory" +/obj/structure/sign/warning/armory/large + icon_state = "armory-large" + /obj/structure/sign/warning/server_room name = "\improper SERVER ROOM" icon_state = "server" +/obj/structure/sign/warning/server_room/large + icon_state = "server-large" /////////////////////////////////////////////////////////////////////////////////// // Hazard Sign Definitions @@ -100,13 +123,15 @@ /obj/structure/sign/warning/biohazard name = "\improper BIOHAZARD" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "bio" /obj/structure/sign/warning/radioactive name = "\improper RADIOACTIVE AREA" icon_state = "radiation" +/obj/structure/sign/warning/radioactive/large + icon_state = "radiation-large" + /obj/structure/sign/warning/radioactive/alt name = "\improper IONIZING RADIATION" icon_state = "radiation_2" @@ -115,10 +140,16 @@ name = "\improper DANGER: FIRE" icon_state = "fire" +/obj/structure/sign/warning/fire/large + icon_state = "fire-large" + /obj/structure/sign/warning/high_voltage name = "\improper HIGH VOLTAGE" icon_state = "shock" +/obj/structure/sign/warning/high_voltage/large + icon_state = "shock-large" + /obj/structure/sign/warning/hot_exhaust name = "\improper HOT EXHAUST" icon_state = "fire" @@ -132,17 +163,14 @@ /obj/structure/sign/warning/bomb_range name = "\improper BOMB RANGE" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "blast" /obj/structure/sign/warning/fall name = "\improper FALL HAZARD" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "falling" /obj/structure/sign/warning/lethal_turrets name = "\improper LETHAL TURRETS" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "turrets" /obj/structure/sign/warning/lethal_turrets/update_description() @@ -156,6 +184,9 @@ name = "\improper HARD VACUUM AHEAD" icon_state = "space" +/obj/structure/sign/warning/vacuum/large + icon_state = "space-large" + /obj/structure/sign/warning/vent_port name = "\improper EJECTION/VENTING PORT" @@ -165,14 +196,54 @@ /obj/structure/sign/warning/mass_spectrometry name = "\improper MASS SPECTROMETRY" -//legacy stuff - The exodus map still uses it -/obj/structure/sign/warning/pods - name = "\improper ESCAPE PODS" - icon = 'icons/obj/signs/directions.dmi' - icon_state = "podsnorth" -/obj/structure/sign/warning/pods/south - icon_state = "podssouth" -/obj/structure/sign/warning/pods/east - icon_state = "podseast" -/obj/structure/sign/warning/pods/west - icon_state = "podswest" \ No newline at end of file +/obj/structure/sign/warning/acid + name = "\improper WARNING: CORROSIVE MATERIALS" + icon_state = "acid" + +/obj/structure/sign/warning/cold + name = "\improper WARNING: LOW TEMPERATURES" + icon_state = "cold" + +/obj/structure/sign/warning/lava + name = "\improper WARNING: MOLTEN ROCK" + icon_state = "lava" + +/obj/structure/sign/warning/malfunction + name = "\improper IN CASE OF MALFUNCTION" + icon_state = "rogueai" + +/obj/structure/sign/warning/explosives + name = "\improper WARNING: HIGH EXPLOSIVES" + icon_state = "explosives" + +/obj/structure/sign/warning/chemicals + name = "\improper WARNING: RISK OF CHEMICAL EXPOSURE" + icon_state = "chemdiamond" + +/obj/structure/sign/warning/atmos_co2 + name = "\improper WARNING: CO2" + icon_state = "atmos_co2" + +/obj/structure/sign/warning/atmos_n2o + name = "\improper WARNING: N2O" + icon_state = "atmos_n2o" + +/obj/structure/sign/warning/atmos_phoron + name = "\improper WARNING: EXOTIC MATTER" + icon_state = "atmos_phoron" + +/obj/structure/sign/warning/atmos_o2 + name = "\improper WARNING: O2" + icon_state = "atmos_o2" + +/obj/structure/sign/warning/atmos_air + name = "\improper WARNING: PRESSURIZED AIR" + icon_state = "atmos_air" + +/obj/structure/sign/warning/atmos_n2 + name = "\improper WARNING: N2" + icon_state = "atmos_n2" + +/obj/structure/sign/warning/atmos_waste + name = "\improper WARNING: WASTE UNDER PRESSURE" + icon_state = "atmos_waste" diff --git a/code/game/objects/structures/stasis_cage.dm b/code/game/objects/structures/stasis_cage.dm deleted file mode 100644 index 5474cb223cf5..000000000000 --- a/code/game/objects/structures/stasis_cage.dm +++ /dev/null @@ -1,93 +0,0 @@ -/obj/structure/stasis_cage - name = "stasis cage" - desc = "A high-tech animal cage, designed to keep contained fauna docile and safe." - icon = 'icons/obj/stasis_cage.dmi' - icon_state = "stasis_cage" - density = TRUE - layer = ABOVE_OBJ_LAYER - - var/mob/living/simple_animal/contained - -/obj/structure/stasis_cage/Initialize() - . = ..() - - var/mob/living/simple_animal/A = locate() in loc - if(A) - contain(A) - -/obj/structure/stasis_cage/attackby(obj/item/used_item, mob/user) - if(contained && istype(used_item, /obj/item/scanner/xenobio)) - return contained.attackby(used_item, user) - . = ..() - -/obj/structure/stasis_cage/attack_hand(var/mob/user) - if(!user.check_dexterity(DEXTERITY_SIMPLE_MACHINES, TRUE)) - return ..() - try_release(user) - return TRUE - -/obj/structure/stasis_cage/attack_robot(var/mob/user) - if(CanPhysicallyInteract(user)) - try_release(user) - return TRUE - -/obj/structure/stasis_cage/proc/try_release(mob/user) - if(!contained) - to_chat(user, SPAN_NOTICE("There's no animals inside \the [src]")) - return - user.visible_message("[user] begins undoing the locks and latches on \the [src].") - if(do_after(user, 20, src)) - user.visible_message("[user] releases \the [contained] from \the [src]!") - release() - -/obj/structure/stasis_cage/on_update_icon() - ..() - if(contained) - icon_state = "[initial(icon_state)]_on" - else - icon_state = initial(icon_state) - -/obj/structure/stasis_cage/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - if(contained) - . += "\The [contained] is kept inside." - -/obj/structure/stasis_cage/proc/contain(var/mob/living/simple_animal/animal) - if(contained || !istype(animal)) - return - - contained = animal - animal.forceMove(src) - animal.in_stasis = 1 - update_icon() - -/obj/structure/stasis_cage/proc/release() - if(!contained) - return - - contained.dropInto(src) - contained.in_stasis = 0 - contained = null - update_icon() - -/obj/structure/stasis_cage/Destroy() - release() - return ..() - -/mob/living/simple_animal/handle_mouse_drop(atom/over, mob/user, params) - if(istype(over, /obj/structure/stasis_cage)) - var/obj/structure/stasis_cage/cage = over - if(!stat && !istype(buckled, /obj/effect/energy_net)) - to_chat(user, SPAN_WARNING("It's going to be difficult to convince \the [src] to move into \the [cage] without capturing it in a net.")) - return TRUE - user.visible_message( \ - SPAN_NOTICE("\The [user] begins stuffing \the [src] into \the [cage]."), \ - SPAN_NOTICE("You begin stuffing \the [src] into \the [cage].")) - Bumped(user) - if(do_after(user, 20, cage)) - cage.visible_message( \ - SPAN_NOTICE("\The [user] has stuffed \the [src] into \the [cage]."), \ - SPAN_NOTICE("You have stuffed \the [src] into \the [cage].")) - cage.contain(src) - return TRUE - . = ..() diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index 8fbd7f7f983e..bca175038d28 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -164,3 +164,6 @@ else to_chat(usr, "The windoor will now slide to the left.") update_icon() + +/obj/structure/windoor_assembly/secure + secure = TRUE diff --git a/code/game/sound.dm b/code/game/sound.dm index 3f0d24378d25..6aa5066cea21 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -141,29 +141,29 @@ var/global/const/FALLOFF_SOUNDS = 0.5 /proc/get_sfx(soundin) if(istext(soundin)) switch(soundin) - if ("shatter") soundin = pick(global.shatter_sound) - if ("explosion") soundin = pick(global.explosion_sound) - if ("sparks") soundin = pick(global.spark_sound) - if ("rustle") soundin = pick(global.rustle_sound) - if ("punch") soundin = pick(global.punch_sound) + if ("shatter") soundin = pick(global.shatter_sound) + if ("explosion") soundin = pick(global.explosion_sound) + if ("sparks") soundin = pick(global.spark_sound) + if ("rustle") soundin = pick(global.rustle_sound) + if ("punch") soundin = pick(global.punch_sound) if ("light_strike") soundin = pick(global.light_strike_sound) - if ("clownstep") soundin = pick(global.clown_sound) - if ("swing_hit") soundin = pick(global.swing_hit_sound) - if ("hiss") soundin = pick(global.hiss_sound) - if ("pageturn") soundin = pick(global.page_sound) - if ("fracture") soundin = pick(global.fracture_sound) - if ("light_bic") soundin = pick(global.lighter_sound) - if ("keyboard") soundin = pick(global.keyboard_sound) - if ("keystroke") soundin = pick(global.keystroke_sound) - if ("switch") soundin = pick(global.switch_sound) - if ("button") soundin = pick(global.button_sound) - if ("chop") soundin = pick(global.chop_sound) - if ("glasscrack") soundin = pick(global.glasscrack_sound) - if ("tray_hit") soundin = pick(global.tray_hit_sound) - if ("sweeping") soundin = pick(global.sweeping_sound) + if ("clownstep") soundin = pick(global.clown_sound) + if ("swing_hit") soundin = pick(global.swing_hit_sound) + if ("hiss") soundin = pick(global.hiss_sound) + if ("pageturn") soundin = pick(global.page_sound) + if ("fracture") soundin = pick(global.fracture_sound) + if ("light_bic") soundin = pick(global.lighter_sound) + if ("keyboard") soundin = pick(global.keyboard_sound) + if ("keystroke") soundin = pick(global.keystroke_sound) + if ("switch") soundin = pick(global.switch_sound) + if ("button") soundin = pick(global.button_sound) + if ("chop") soundin = pick(global.chop_sound) + if ("glasscrack") soundin = pick(global.glasscrack_sound) + if ("tray_hit") soundin = pick(global.tray_hit_sound) + if ("sweeping") soundin = pick(global.sweeping_sound) + if("ricochet") soundin = pick(global.ricochet_sound) return soundin - ///Volume to play DTMF key sounds at. They're pretty loud, so 15 is fine. #define VOL_DTMF_KEY 15 diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index a93566d69e38..f30adf01de71 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -27,6 +27,7 @@ var/global/list/flooring_cache = list() var/damage_temperature var/icon_edge_layer = FLOOR_EDGE_NONE var/has_environment_proc + var/can_conceal_hazards = FALSE /// Unbuildable if not set. Must be /obj/item/stack. var/build_type @@ -68,6 +69,8 @@ var/global/list/flooring_cache = list() var/wall_smooth /// How we smooth with space and openspace tiles var/space_smooth + /// If we smooth with everything, we can skip a bunch of other smoothing checks. This is a bool and not an enum. + var/omni_smooth /// same z flags used for turfs, i.e ZMIMIC_DEFAULT etc var/z_flags @@ -76,6 +79,7 @@ var/global/list/flooring_cache = list() var/constructed = FALSE + var/has_corners = TRUE var/has_internal_edges = FALSE var/has_external_edges = FALSE var/edge_state @@ -128,6 +132,8 @@ var/global/list/flooring_cache = list() space_smooth = default_smooth if(isnull(floor_smooth)) floor_smooth = default_smooth + if(isnull(omni_smooth) && floor_smooth == wall_smooth && wall_smooth == space_smooth) + omni_smooth = (floor_smooth == SMOOTH_ALL) // bool, not enum /decl/flooring/validate() . = ..() @@ -198,37 +204,32 @@ var/global/list/flooring_cache = list() if (icon_edge_layer != FLOOR_EDGE_NONE && (has_internal_edges || has_external_edges)) var/edge_layer = target.layer + icon_edge_layer - var/list/edge_overlays = list() var/has_border = 0 for(var/step_dir in global.cardinal) var/turf/T = get_step_resolving_mimic(target, step_dir) - if(!istype(T) || symmetric_test_link(target, T)) + if(!istype(T) || test_link(T)) continue has_border |= step_dir - if(icon_edge_layer != FLOOR_EDGE_NONE) - if(has_internal_edges) - edge_overlays += get_flooring_overlay("[icon]_[icon_base]-edge-[step_dir]", edge_state, step_dir, edge_layer = edge_layer) - if(has_external_edges && target.can_draw_edge_over(T)) - edge_overlays += get_flooring_overlay("[icon]_[icon_base]-outer-edge-[step_dir]", outer_edge_state, step_dir, TRUE, edge_layer = edge_layer) - - var/has_smooth = ~(has_border & (NORTH | SOUTH | EAST | WEST)) - for(var/step_dir in global.cornerdirs) - var/turf/T = get_step_resolving_mimic(target, step_dir) - if(!istype(T) || symmetric_test_link(target, T)) - continue if(has_internal_edges) - if((has_smooth & step_dir) == step_dir) - edge_overlays += get_flooring_overlay("[icon]_[icon_base]-corner-[step_dir]", corner_state, step_dir, edge_layer = edge_layer) - else if((has_border & step_dir) == step_dir) - edge_overlays += get_flooring_overlay("[icon]_[icon_base]-edge-[step_dir]", edge_state, step_dir, edge_layer = edge_layer) + target.add_overlay(get_flooring_overlay("[icon]_[icon_base]-edge-[step_dir]", edge_state, step_dir, edge_layer = edge_layer)) if(has_external_edges && target.can_draw_edge_over(T)) - if((has_smooth & step_dir) == step_dir) - edge_overlays += get_flooring_overlay("[icon]_[icon_base]-outer-corner-[step_dir]", outer_corner_state, step_dir, TRUE, edge_layer = edge_layer) - else if((has_border & step_dir) == step_dir) - edge_overlays += get_flooring_overlay("[icon]_[icon_base]-outer-edge-[step_dir]", outer_edge_state, step_dir, TRUE, edge_layer = edge_layer) + target.add_overlay(get_flooring_overlay("[icon]_[icon_base]-outer-edge-[step_dir]", outer_edge_state, step_dir, TRUE, edge_layer = edge_layer)) - if(length(edge_overlays)) - target.add_overlay(edge_overlays) + if(has_corners) + for(var/step_dir in global.cornerdirs) + var/turf/T = get_step_resolving_mimic(target, step_dir) + if(!istype(T) || test_link(T)) + continue + if(has_internal_edges) + if((has_border & step_dir) == 0) // smooth + target.add_overlay(get_flooring_overlay("[icon]_[icon_base]-corner-[step_dir]", corner_state, step_dir, edge_layer = edge_layer)) + else if((has_border & step_dir) == step_dir) + target.add_overlay(get_flooring_overlay("[icon]_[icon_base]-edge-[step_dir]", edge_state, step_dir, edge_layer = edge_layer)) + if(has_external_edges) + if((has_border & step_dir) == 0 && target.can_draw_edge_over(T)) // smooth + target.add_overlay(get_flooring_overlay("[icon]_[icon_base]-outer-corner-[step_dir]", outer_corner_state, step_dir, TRUE, edge_layer = edge_layer)) + else if((has_border & step_dir) == step_dir && target.can_draw_edge_over(T)) + target.add_overlay(get_flooring_overlay("[icon]_[icon_base]-outer-edge-[step_dir]", outer_edge_state, step_dir, TRUE, edge_layer = edge_layer)) if(target.is_floor_broken()) target.add_overlay(get_damage_overlay(target._floor_broken)) @@ -393,3 +394,6 @@ var/global/list/flooring_cache = list() /// contaminant is, optionally, the material of the coating that wants to be added. /decl/flooring/proc/can_show_coating_footprints(turf/target, decl/material/contaminant) return TRUE + +/decl/flooring/proc/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle::base_speed diff --git a/code/game/turfs/flooring/_flooring_decals.dm b/code/game/turfs/flooring/_flooring_decals.dm index 50ca2d36ab71..b1847ad05ab8 100644 --- a/code/game/turfs/flooring/_flooring_decals.dm +++ b/code/game/turfs/flooring/_flooring_decals.dm @@ -1221,7 +1221,7 @@ var/global/list/floor_decals = list() /obj/effect/floor_decal/solarpanel icon_state = "solarpanel" -/obj/effect/floor_decal/snow +/obj/effect/floor_decal/snow_floor icon = 'icons/turf/overlays.dmi' icon_state = "snowfloor" @@ -1634,4 +1634,34 @@ var/global/list/floor_decals = list() /obj/effect/floor_decal/corner_oldtile/green/full name = "corner oldtile full" - icon_state = "corner_oldtile_full" \ No newline at end of file + icon_state = "corner_oldtile_full" + +// Decorative overlays. +/obj/effect/floor_decal/vines + name = "vines" + desc = "A tangle of plant growth." + icon_state = "vines" + +/obj/effect/floor_decal/vines/top + icon_state = "vines_top" + +/obj/effect/floor_decal/vines/mid + icon_state = "vines_mid" + +/obj/effect/floor_decal/vines/bottom + icon_state = "vines_bottom" + +/obj/effect/floor_decal/snow + name = "snow" + desc = "A fine dusting of snow." + icon_state = "snowy" + +/obj/effect/floor_decal/rust + name = "rust" + desc = "A clumpy area of rust." + icon_state = "rusted" + +/obj/effect/floor_decal/floornums + name = "floor marker" + desc = "A number corresponding to the position of this floor." + icon_state = "floornums" diff --git a/code/game/turfs/flooring/flooring_concrete.dm b/code/game/turfs/flooring/flooring_concrete.dm index e5d6edd5f174..89ed297a1f7a 100644 --- a/code/game/turfs/flooring/flooring_concrete.dm +++ b/code/game/turfs/flooring/flooring_concrete.dm @@ -7,6 +7,7 @@ force_material = /decl/material/solid/stone/concrete constructed = TRUE uid = "floor_concrete" + can_conceal_hazards = TRUE /decl/flooring/concrete/reinforced name = "reinforced concrete" diff --git a/code/game/turfs/flooring/flooring_grass.dm b/code/game/turfs/flooring/flooring_grass.dm index dc53aaf46808..f51600fa91dc 100644 --- a/code/game/turfs/flooring/flooring_grass.dm +++ b/code/game/turfs/flooring/flooring_grass.dm @@ -14,6 +14,8 @@ force_material = /decl/material/solid/organic/plantmatter/grass growth_value = 1.2 // Shouldn't really matter since you can't plant on grass, it turns to dirt first. uid = "floor_grass" + can_conceal_hazards = TRUE + var/harvestable = FALSE /decl/flooring/grass/fire_act(turf/floor/target, datum/gas_mixture/air, exposed_temperature, exposed_volume) @@ -48,6 +50,9 @@ return TRUE return ..() +/decl/flooring/grass/get_vehicle_transit_delay(obj/vehicle/vehicle) + return 1 + /decl/flooring/grass/fake desc = "Do they smoke grass out in space, Bowie? Or do they smoke AstroTurf?" icon = 'icons/turf/flooring/fakegrass.dmi' @@ -56,3 +61,6 @@ build_type = /obj/item/stack/tile/grass force_material = /decl/material/solid/organic/plastic uid = "floor_grass_fake" + +/decl/flooring/grass/fake/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle::base_speed diff --git a/code/game/turfs/flooring/flooring_mud.dm b/code/game/turfs/flooring/flooring_mud.dm index bdce5b2859e8..e7f26e889bec 100644 --- a/code/game/turfs/flooring/flooring_mud.dm +++ b/code/game/turfs/flooring/flooring_mud.dm @@ -5,6 +5,7 @@ icon_base = "mud" color = null // autoset from material icon_edge_layer = FLOOR_EDGE_MUD + has_corners = FALSE footstep_type = /decl/footsteps/mud turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/soil @@ -33,12 +34,16 @@ return FALSE return ..() +/decl/flooring/mud/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 1.4 : 1.5 + /decl/flooring/dry_mud name = "dry mud" desc = "This was once mud, but forgot to keep hydrated." icon = 'icons/turf/flooring/seafloor.dmi' icon_base = "seafloor" icon_edge_layer = FLOOR_EDGE_MUD + has_corners = FALSE footstep_type = /decl/footsteps/mud turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID color = "#ae9e66" @@ -46,6 +51,9 @@ force_material = /decl/material/solid/soil uid = "floor_dry_mud" +/decl/flooring/dry_mud/get_vehicle_transit_delay(obj/vehicle/vehicle) + return 1 + /decl/flooring/dry_mud/fluid_act(turf/floor/target, datum/reagents/fluids) if(target.get_topmost_flooring() == src) target.set_flooring(/decl/flooring/mud) @@ -61,12 +69,14 @@ icon = 'icons/turf/flooring/dirt.dmi' icon_base = "dirt" icon_edge_layer = FLOOR_EDGE_DIRT + has_corners = FALSE color = null // autoset from material footstep_type = /decl/footsteps/asteroid turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/soil growth_value = 1 uid = "floor_dirt" + can_conceal_hazards = TRUE /decl/flooring/dirt/fluid_act(turf/floor/target, datum/reagents/fluids) if(target.get_topmost_flooring() == src) @@ -76,3 +86,6 @@ target.set_base_flooring(/decl/flooring/mud) . = TRUE return . || ..() + +/decl/flooring/dirt/get_vehicle_transit_delay(obj/vehicle/vehicle) + return 1 diff --git a/code/game/turfs/flooring/flooring_natural.dm b/code/game/turfs/flooring/flooring_natural.dm index 128bdc34cbc4..932289909a91 100644 --- a/code/game/turfs/flooring/flooring_natural.dm +++ b/code/game/turfs/flooring/flooring_natural.dm @@ -4,6 +4,7 @@ icon = 'icons/turf/flooring/seafloor.dmi' icon_base = "seafloor" icon_edge_layer = FLOOR_EDGE_SEAFLOOR + has_corners = FALSE turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/sand gender = NEUTER @@ -43,6 +44,7 @@ footstep_type = /decl/footsteps/asteroid turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH icon_edge_layer = FLOOR_EDGE_BARREN + has_corners = FALSE force_material = /decl/material/solid/sand growth_value = 0.1 uid = "floor_barren" @@ -53,6 +55,7 @@ icon = 'icons/turf/flooring/clay.dmi' icon_base = "clay" icon_edge_layer = FLOOR_EDGE_CLAY + has_corners = FALSE footstep_type = /decl/footsteps/mud turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/clay @@ -70,6 +73,9 @@ force_material = /decl/material/solid/ice uid = "floor_ice" +/decl/flooring/ice/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 0.8 : ..() + /decl/flooring/ice/update_turf_icon(turf/floor/target) . = ..() if(istype(target)) diff --git a/code/game/turfs/flooring/flooring_path.dm b/code/game/turfs/flooring/flooring_path.dm index 6a934d5c5eb3..8632af3b386c 100644 --- a/code/game/turfs/flooring/flooring_path.dm +++ b/code/game/turfs/flooring/flooring_path.dm @@ -16,7 +16,7 @@ var/paver_noun = "stones" /decl/flooring/path/update_turf_strings(turf/floor/target) - var/decl/material/floor_material = target?.get_material() + var/decl/material/floor_material = RESOLVE_TO_DECL(target?.get_material()) ASSERT(floor_material?.adjective_name) ASSERT(paver_noun) target.SetName("[floor_material.adjective_name] [name]") @@ -27,9 +27,10 @@ desc = "A rustic cobblestone path." icon_base = "cobble" icon_edge_layer = FLOOR_EDGE_PATH + has_corners = FALSE flooring_flags = TURF_REMOVE_CROWBAR - has_base_range = 1 - uid = "floor_path_cobble" + has_base_range = 1 + uid = "floor_path_cobble" /decl/flooring/path/running_bond name = "stone path" diff --git a/code/game/turfs/flooring/flooring_rock.dm b/code/game/turfs/flooring/flooring_rock.dm index 13836a0a7da7..94227641346c 100644 --- a/code/game/turfs/flooring/flooring_rock.dm +++ b/code/game/turfs/flooring/flooring_rock.dm @@ -6,11 +6,15 @@ has_base_range = null color = null icon_edge_layer = FLOOR_EDGE_VOLCANIC + has_corners = FALSE gender = NEUTER uid = "floor_reinf_shuttle_rock" /decl/flooring/rock/update_turf_strings(turf/floor/target) - var/decl/material/turf_material = target?.get_material() + var/decl/material/turf_material = RESOLVE_TO_DECL(target?.get_material()) ASSERT(turf_material?.adjective_name) target.SetName("[turf_material.adjective_name] [name]") target.desc = "An expanse of bare [turf_material.solid_name]." + +/decl/flooring/rock/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 1.5 : ..() diff --git a/code/game/turfs/flooring/flooring_sand.dm b/code/game/turfs/flooring/flooring_sand.dm index 3a7c0843f653..5b4f13501eaa 100644 --- a/code/game/turfs/flooring/flooring_sand.dm +++ b/code/game/turfs/flooring/flooring_sand.dm @@ -29,6 +29,7 @@ icon_base = "chlorine" has_base_range = 11 icon_edge_layer = FLOOR_EDGE_CHLORINE_SAND + has_corners = FALSE color = "#d2e0b7" dirt_color = "#d2e0b7" footstep_type = /decl/footsteps/sand diff --git a/code/game/turfs/flooring/flooring_snow.dm b/code/game/turfs/flooring/flooring_snow.dm index 881da81bdcce..72362928c06e 100644 --- a/code/game/turfs/flooring/flooring_snow.dm +++ b/code/game/turfs/flooring/flooring_snow.dm @@ -4,6 +4,7 @@ icon = 'icons/turf/flooring/snow.dmi' icon_base = "snow" icon_edge_layer = FLOOR_EDGE_SNOW + has_corners = FALSE flooring_flags = TURF_REMOVE_SHOVEL footstep_type = /decl/footsteps/snow has_base_range = 13 @@ -12,6 +13,7 @@ print_type = /obj/effect/footprints drop_material_on_remove = TRUE uid = "floor_snow" + can_conceal_hazards = TRUE /decl/flooring/snow/get_movement_delay(var/travel_dir, var/mob/mover) . = ..() @@ -46,6 +48,9 @@ return FALSE return ..() +/decl/flooring/snow/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 0.8 : 1.7 + /decl/flooring/permafrost name = "permafrost" desc = "A stretch of frozen soil that hasn't seen a thaw for many seasons." @@ -54,8 +59,16 @@ force_material = /decl/material/solid/ice uid = "floor_permafrost" +/decl/flooring/permafrost/get_vehicle_transit_delay(obj/vehicle/vehicle) + if(holographic) + return vehicle::base_speed + if(vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE) + return 0.8 + return ..() + /decl/flooring/snow/fake name = "holosnow" desc = "Not quite the same as snow on an entertainment terminal, but close." holographic = TRUE uid = "floor_snow_fake" + diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index 3982c3dab82d..ece70741f5c2 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -11,7 +11,7 @@ zone_membership_candidate = TRUE open_turf_type = /turf/open/airless - // Reagent to use to fill the turf. + /// Reagent to use to refill trenches to capacity automatically. var/fill_reagent_type var/can_engrave = TRUE @@ -69,7 +69,17 @@ var/my_height = get_physical_height() if(fill_reagent_type && my_height < 0 && (!reagents || !QDELING(reagents)) && REAGENT_TOTAL_VOLUME(reagents) < abs(my_height)) var/reagents_to_add = abs(my_height) - REAGENT_TOTAL_VOLUME(reagents) - add_to_reagents(fill_reagent_type, reagents_to_add, phase = MAT_PHASE_LIQUID) + var/contaminant_to_add = 0 + if(contaminant_reagent_type) + contaminant_to_add = CHEMS_QUANTIZE(reagents_to_add * contaminant_proportion) + add_to_reagents(fill_reagent_type, reagents_to_add - contaminant_to_add, phase = MAT_PHASE_LIQUID, defer_update = !!contaminant_to_add) + if(contaminant_to_add) + add_to_reagents(contaminant_reagent_type, contaminant_to_add, phase = MAT_PHASE_LIQUID) + +/turf/floor/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + if(check_fluid_depth(FLUID_SHALLOW)) + . += SPAN_NOTICE("It has a pool of [get_fluid_name()].") /turf/floor/can_climb_from_below(var/mob/climber) return TRUE @@ -189,3 +199,9 @@ /turf/floor/can_show_coating_footprints(decl/material/contaminant = null) return ..() && get_topmost_flooring()?.can_show_coating_footprints(src, contaminant) + +/turf/floor/proc/get_vehicle_transit_delay(obj/vehicle/vehicle) + var/decl/flooring/terrain = get_topmost_flooring() + if(!istype(vehicle) || QDELETED(vehicle) || !istype(terrain) || vehicle.vehicle_transit_type == vehicle::VEHICLE_GENERIC) + return vehicle::base_speed + return terrain.get_vehicle_transit_delay(vehicle) diff --git a/code/game/turfs/floors/floor_height.dm b/code/game/turfs/floors/floor_height.dm index 8968d6020a5a..43057b4dc0cc 100644 --- a/code/game/turfs/floors/floor_height.dm +++ b/code/game/turfs/floors/floor_height.dm @@ -19,4 +19,5 @@ for(var/atom/movable/thing in contents) thing.on_turf_height_change(new_height) + state_was_modified() return TRUE diff --git a/code/game/turfs/floors/floor_icon.dm b/code/game/turfs/floors/floor_icon.dm index 2b799313b9b1..0f684b9a5a47 100644 --- a/code/game/turfs/floors/floor_icon.dm +++ b/code/game/turfs/floors/floor_icon.dm @@ -123,11 +123,13 @@ /turf/floor/proc/update_floor_strings() var/decl/flooring/flooring = get_topmost_flooring() if(istype(flooring)) - SetName(flooring.name) - desc = flooring.desc + flooring.update_turf_strings(src) else SetName(initial(name)) desc = initial(desc) + // do this once name and desc have been updated + if(check_fluid_depth(FLUID_SHALLOW)) + SetName(get_fluid_name()) // just entirely overwrite name, but keep desc /turf/floor/proc/update_floor_icon() var/decl/flooring/use_flooring = get_topmost_flooring() @@ -156,6 +158,7 @@ _floor_broken = new_broken if(!skip_update) queue_icon_update() + state_was_modified() return TRUE return FALSE @@ -170,10 +173,13 @@ _floor_burned = new_burned if(!skip_update) queue_icon_update() + state_was_modified() return TRUE return FALSE -/decl/flooring/proc/test_link(var/turf/origin, var/turf/opponent) +/decl/flooring/proc/test_link(var/turf/opponent) + if(omni_smooth) // override EVERYTHING + return TRUE // Just a normal floor if (istype(opponent, /turf/floor)) if (floor_smooth == SMOOTH_ALL) @@ -196,7 +202,7 @@ if (wall_smooth == SMOOTH_ALL && locate(/obj/structure/wall_frame) in opponent) return TRUE // Wall turf - else if(opponent.is_wall()) + else if(opponent.is_wall()) // don't combine these so that we don't check if a wall is space just because we don't smooth with walls if(wall_smooth == SMOOTH_ALL) return TRUE //If is_open is true, then it's space or openspace @@ -204,6 +210,3 @@ if(space_smooth == SMOOTH_ALL) return TRUE return FALSE - -/decl/flooring/proc/symmetric_test_link(var/turf/A, var/turf/B) - return test_link(A, B) && test_link(B,A) diff --git a/code/game/turfs/floors/floor_layers.dm b/code/game/turfs/floors/floor_layers.dm index 8fb98f91fa6a..24e1629b664c 100644 --- a/code/game/turfs/floors/floor_layers.dm +++ b/code/game/turfs/floors/floor_layers.dm @@ -55,21 +55,25 @@ remove_flooring(_flooring, TRUE, place_product) if(!skip_update) update_from_flooring() + state_was_modified() return TRUE /turf/floor/proc/remove_flooring(var/decl/flooring/flooring, skip_update, place_product) // Remove floor layers one by one. _topmost_flooring = null + if(islist(flooring)) for(var/floor in UNLINT(flooring)) if(remove_flooring(floor, TRUE, place_product)) . = TRUE - if(. && !skip_update) - set_floor_broken(skip_update = TRUE) - set_floor_burned(skip_update = TRUE) - update_from_flooring() - return + if(.) + state_was_modified() + if(!skip_update) + set_floor_broken(skip_update = TRUE) + set_floor_burned(skip_update = TRUE) + update_from_flooring() + return // Validate our input. flooring = RESOLVE_TO_DECL(flooring) @@ -85,6 +89,8 @@ else if(_flooring == flooring) _flooring = null + state_was_modified() + // If the turf was not the topmost turf, then we don't really need to care about it. if(!was_topmost) return @@ -140,7 +146,7 @@ _flooring = RESOLVE_TO_DECL(newflooring) else return FALSE - + state_was_modified() if(!skip_update) update_from_flooring() return TRUE @@ -162,6 +168,7 @@ for(var/floor in UNLINT(newflooring)) if(add_flooring(floor, skip_update = FALSE)) . = TRUE + state_was_modified() if(!skip_update) set_floor_broken(skip_update = TRUE) set_floor_burned(skip_update = TRUE) @@ -189,6 +196,8 @@ _flooring = list(_flooring) _flooring |= newflooring + state_was_modified() + // Update for the new top layer. if(!skip_update) set_floor_broken(skip_update = TRUE) diff --git a/code/game/turfs/floors/floor_materials.dm b/code/game/turfs/floors/floor_materials.dm index 213deaca53b1..5a7ff3459fd2 100644 --- a/code/game/turfs/floors/floor_materials.dm +++ b/code/game/turfs/floors/floor_materials.dm @@ -11,8 +11,10 @@ material = get_default_material() . = TRUE - if(. && !skip_update) - queue_icon_update() + if(.) + state_was_modified() + if(!skip_update) + queue_icon_update() /turf/floor/get_material() var/decl/flooring/flooring = get_topmost_flooring() diff --git a/code/game/turfs/floors/floor_serde.dm b/code/game/turfs/floors/floor_serde.dm new file mode 100644 index 000000000000..9e88c47f2a83 --- /dev/null +++ b/code/game/turfs/floors/floor_serde.dm @@ -0,0 +1,30 @@ +/turf/floor/Serialize() + . = ..() + + SERIALIZE_IF_MODIFIED(_floor_broken, /turf/floor) + SERIALIZE_IF_MODIFIED(_floor_burned, /turf/floor) + SERIALIZE_IF_MODIFIED(height, /turf/floor) + SERIALIZE_DECL_IF_MODIFIED(_base_flooring, /turf/floor) + + var/initial_flooring = initial(_flooring) + if(isnull(_flooring) && !isnull(initial_flooring)) + .[nameof(/turf/floor::_flooring)] = json_encode(list()) + else if((ispath(_flooring) || istype(_flooring, /decl)) && (!ispath(initial_flooring) || !DECLS_ARE_EQUIVALENT(_flooring, initial_flooring))) + var/decl/flooring/flooring = RESOLVE_TO_DECL(_flooring) + if(istype(flooring)) + .[nameof(/turf/floor::_flooring)] = json_encode(list(flooring.uid)) + else if(islist(_flooring)) + var/list/flooring_uids + for(var/floor in _flooring) + var/decl/flooring/floor_decl = RESOLVE_TO_DECL(floor) + if(istype(floor_decl)) + LAZYADD(flooring_uids, floor_decl.uid) + if(!istext(initial_flooring) || !(flooring_uids ~= cached_json_decode(initial_flooring))) + .[nameof(/turf/floor::_flooring)] = json_encode(flooring_uids) + +/turf/floor/Deserialize(list/instance_map) + . = ..() + fill_reagent_type = null // Assume any fluids on this turf were serialized and will be deserialized on /turf/Deserialize() + DESERIALIZE_DECL_TO_TYPE(_base_flooring) + // _flooring is expected as a JSON list in base floor + // Initialize(), so no additional deserializing needed here. diff --git a/code/game/turfs/floors/subtypes/floor_circuit.dm b/code/game/turfs/floors/subtypes/floor_circuit.dm index 7684009c32a4..77bb38e13a45 100644 --- a/code/game/turfs/floors/subtypes/floor_circuit.dm +++ b/code/game/turfs/floors/subtypes/floor_circuit.dm @@ -13,6 +13,10 @@ name = "mainframe base" // TODO: force name overriding flooring? temperature = 263 +/turf/floor/bluegrid/cryo + initial_gas = list(/decl/material/gas/nitrogen = MOLES_CELLSTANDARD) + temperature = -200 CELSIUS + /turf/floor/greengrid name = "mainframe floor" icon = 'icons/turf/flooring/circuit.dmi' diff --git a/code/game/turfs/floors/subtypes/floor_concrete.dm b/code/game/turfs/floors/subtypes/floor_concrete.dm index 60303ef1ee79..f52e66144503 100644 --- a/code/game/turfs/floors/subtypes/floor_concrete.dm +++ b/code/game/turfs/floors/subtypes/floor_concrete.dm @@ -13,6 +13,10 @@ flooded = /decl/material/liquid/water color = COLOR_LIQUID_WATER +/turf/floor/concrete/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt + /turf/floor/concrete/reinforced name = "reinforced concrete" icon_state = "hexacrete" diff --git a/code/game/turfs/floors/subtypes/floor_natural.dm b/code/game/turfs/floors/subtypes/floor_natural.dm index ae7350535805..9a73b28f4d1b 100644 --- a/code/game/turfs/floors/subtypes/floor_natural.dm +++ b/code/game/turfs/floors/subtypes/floor_natural.dm @@ -85,13 +85,25 @@ height = -(FLUID_SHALLOW) fill_reagent_type = /decl/material/liquid/water +/turf/floor/mud/water/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt + /turf/floor/mud/water/deep color = COLOR_BLUE height = -(FLUID_DEEP) +/turf/floor/mud/water/deep/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water + /turf/floor/mud/flooded flooded = /decl/material/liquid/water +/turf/floor/mud/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water + /turf/floor/dry name = "dry mud" icon = 'icons/turf/flooring/seafloor.dmi' @@ -105,22 +117,23 @@ color = "#ae9e66" _flooring = /decl/flooring/sand -/turf/floor/rock/basalt/sand - name = "sand" - icon = 'icons/turf/flooring/sand.dmi' - icon_state = "sand0" - color = "#ae9e66" - _flooring = /decl/flooring/sand - /turf/floor/rock/sand/water color = COLOR_SKY_BLUE height = -(FLUID_SHALLOW) fill_reagent_type = /decl/material/liquid/water +/turf/floor/rock/sand/water/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water + /turf/floor/rock/sand/water/deep color = COLOR_BLUE height = -(FLUID_DEEP) +/turf/floor/rock/sand/water/deep/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water + /turf/floor/seafloor name = "sea floor" icon = 'icons/turf/flooring/seafloor.dmi' @@ -131,6 +144,10 @@ flooded = /decl/material/liquid/water color = COLOR_LIQUID_WATER +/turf/floor/seafloor/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water + /turf/floor/shrouded name = "packed sand" icon = 'icons/turf/flooring/shrouded.dmi' diff --git a/code/game/turfs/floors/subtypes/floor_path.dm b/code/game/turfs/floors/subtypes/floor_path.dm index b67e06bf1b70..bded9334f69b 100644 --- a/code/game/turfs/floors/subtypes/floor_path.dm +++ b/code/game/turfs/floors/subtypes/floor_path.dm @@ -11,8 +11,10 @@ _base_flooring = /decl/flooring/dirt /turf/floor/path/Initialize(mapload, no_update_icon) + // Take advantage of the set_turf_materials call in ..() + // to avoid doing pointless work + material ||= get_strata_material_type() || /decl/material/solid/stone/sandstone . = ..() - set_turf_materials(material || get_strata_material_type() || /decl/material/solid/stone/sandstone, skip_update = no_update_icon) if(mapload && is_outside() && prob(20)) var/image/moss = image('icons/effects/decals/plant_remains.dmi', "leafy_bits", DECAL_LAYER) moss.pixel_x = rand(-6, 6) diff --git a/code/game/turfs/floors/subtypes/floor_rock.dm b/code/game/turfs/floors/subtypes/floor_rock.dm index a0e1194fda70..e34bc367a3cc 100644 --- a/code/game/turfs/floors/subtypes/floor_rock.dm +++ b/code/game/turfs/floors/subtypes/floor_rock.dm @@ -5,13 +5,10 @@ _base_flooring = /decl/flooring/rock /turf/floor/rock/Initialize(mapload, no_update_icon) + // Take advantage of the set_turf_materials call in ..() + material ||= get_strata_material_type() || /decl/material/solid/stone/sandstone . = ..() - set_turf_materials(material || get_strata_material_type() || /decl/material/solid/stone/sandstone, skip_update = no_update_icon) /turf/floor/rock/volcanic name = "volcanic floor" material = /decl/material/solid/stone/basalt - -/turf/floor/rock/basalt - color = /decl/material/solid/stone/basalt::color - material = /decl/material/solid/stone/basalt diff --git a/code/game/turfs/floors/subtypes/floor_shuttle.dm b/code/game/turfs/floors/subtypes/floor_shuttle.dm index 24c79df35f49..17404b65e480 100644 --- a/code/game/turfs/floors/subtypes/floor_shuttle.dm +++ b/code/game/turfs/floors/subtypes/floor_shuttle.dm @@ -4,6 +4,9 @@ desc = "A synthetic floor plate commonly seen in shuttles and other vehicles." _flooring = /decl/flooring/reinforced/shuttle +/turf/floor/shuttle/airless + initial_gas = null + /turf/floor/shuttle/blue icon_state = "floor" _flooring = /decl/flooring/reinforced/shuttle/blue diff --git a/code/game/turfs/floors/subtypes/floor_tiled.dm b/code/game/turfs/floors/subtypes/floor_tiled.dm index 33dc224c1966..9777e8146710 100644 --- a/code/game/turfs/floors/subtypes/floor_tiled.dm +++ b/code/game/turfs/floors/subtypes/floor_tiled.dm @@ -4,11 +4,17 @@ icon = 'icons/turf/flooring/tiles.dmi' icon_state = "tiled" _flooring = /decl/flooring/tiling + color = /decl/flooring/tiling::color /turf/floor/tiled/dark name = "dark floor" icon_state = "dark" _flooring = /decl/flooring/tiling/dark + color = /decl/flooring/tiling/dark::color + +/turf/floor/tiled/dark/cryo + initial_gas = list(/decl/material/gas/nitrogen = MOLES_CELLSTANDARD) + temperature = -200 CELSIUS /turf/floor/tiled/dark/monotile name = "floor" @@ -36,6 +42,7 @@ name = "floor" icon_state = "steel_monofloor" _flooring = /decl/flooring/tiling/mono + color = /decl/flooring/tiling/mono::color /turf/floor/tiled/white/airless name = "airless floor" @@ -46,6 +53,7 @@ name = "tiles" icon_state = "freezer" _flooring = /decl/flooring/tiling/freezer + color = /decl/flooring/tiling/freezer::color /turf/floor/tiled/freezer/kitchen name = "kitchen freezer floor" // TODO: force override of flooring name @@ -68,6 +76,10 @@ icon_state = "techfloor_gray" _flooring = /decl/flooring/tiling/tech +/turf/floor/tiled/techfloor/cryo + initial_gas = list(/decl/material/gas/nitrogen = MOLES_CELLSTANDARD) + temperature = -200 CELSIUS + /turf/floor/tiled/monotile name = "floor" icon_state = "steel_monotile" @@ -102,12 +114,17 @@ name = "stone slab floor" icon_state = "stone" _flooring = /decl/flooring/tiling/stone + color = /decl/flooring/tiling/stone::color /turf/floor/tiled/techfloor/grid name = "floor" icon_state = "techfloor_grid" _flooring = /decl/flooring/tiling/tech/grid +/turf/floor/tiled/techfloor/grid/cryo + initial_gas = list(/decl/material/gas/nitrogen = MOLES_CELLSTANDARD) + temperature = -200 CELSIUS + /turf/floor/tiled/airless name = "airless floor" initial_gas = null diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm index abbcfecc695a..186c84cb2642 100644 --- a/code/game/turfs/open/_open.dm +++ b/code/game/turfs/open/_open.dm @@ -27,6 +27,11 @@ name = "open water" flooded = /decl/material/liquid/water +/turf/open/flooded/salt + name = "open saltwater" // alt. ver: open ocean? + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt + /turf/open/Entered(var/atom/movable/mover, var/atom/oldloc) ..() mover.fall(oldloc) diff --git a/code/game/turfs/open/open_sky.dm b/code/game/turfs/open/open_sky.dm index c3b4c1a4ddf7..74dd2d6c05e9 100644 --- a/code/game/turfs/open/open_sky.dm +++ b/code/game/turfs/open/open_sky.dm @@ -31,3 +31,39 @@ /turf/open/sky/moving/east dir = EAST + +// For planetary skyboxes/shuttle transit areas. +/turf/unsimulated/sky + name = "sky" + desc = "Hope you don't have a fear of heights..." + icon = 'icons/turf/flooring/sky_static.dmi' + icon_state = "0" + z_flags = 0 + dynamic_lighting = FALSE // TODO: put the arrivals shuttle on a level with ambient lighting so the sky can reflect daytime. + +/turf/unsimulated/sky/north + dir = NORTH + +/turf/unsimulated/sky/south + dir = SOUTH + +/turf/unsimulated/sky/west + dir = WEST + +/turf/unsimulated/sky/east + dir = EAST + +/turf/unsimulated/sky/moving + icon = 'icons/turf/flooring/sky_slow.dmi' + +/turf/unsimulated/sky/moving/north + dir = NORTH + +/turf/unsimulated/sky/moving/south + dir = SOUTH + +/turf/unsimulated/sky/moving/west + dir = WEST + +/turf/unsimulated/sky/moving/east + dir = EAST diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 0c4d7107c015..ff76d205ad23 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -23,6 +23,7 @@ SHOULD_CALL_PARENT(FALSE) atom_flags |= ATOM_FLAG_INITIALIZED + _earliest_type ||= type AMBIENCE_QUEUE_TURF(src) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 33828543cd5d..df7d8c2b68ad 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -7,6 +7,9 @@ temperature_sensitive = TRUE atom_flags = ATOM_FLAG_OPEN_CONTAINER + // Linear lazylist of weakrefs to dangerous things on this turf. + var/list/dangerous_objects + /// Will participate in ZAS, join zones, etc. var/zone_membership_candidate = FALSE /// Will participate in external atmosphere simulation if the turf is outside and no zone is set. @@ -38,6 +41,14 @@ var/footstep_type var/open_turf_type = /turf/open // Which open turf type to use by default above this turf in a multiz context. Overridden by area. + // If you ever need to refill or flood a turf with more than two reagents, this should be rewritten entirely. + // The reason it's written like this is to avoid creating a new list for every turf with contaminants + // and that should still hold up even if you have turfs with three or more liquids in the mixture. + /// Reagent to contaminate refilled or flooded reagents. + var/contaminant_reagent_type + /// What fraction of the refilled/flooded liquid should be the contaminant? If zero, no contaminant is added. + var/contaminant_proportion + var/tmp/changing_turf var/tmp/prev_type // Previous type of the turf, prior to turf translation. @@ -90,6 +101,7 @@ /turf/Initialize(mapload, ...) . = null && ..() // This weird construct is to shut up the 'parent proc not called' warning without disabling the lint for child types. We explicitly return an init hint so this won't change behavior. + _earliest_type ||= type color = null // atom/Initialize has been copied here for performance (or at least the bits of it that turfs use has been) @@ -105,6 +117,10 @@ else luminosity = 1 + // Reagents got deserialized, set them up. Do not return as we want to finish turf init. + // we don't care about volume because turfs always create a maximum volume holder on reagent add. + FINALIZE_REAGENTS_SERDE(reagents) + AMBIENCE_QUEUE_TURF(src) if (opacity) @@ -128,6 +144,7 @@ if(flooded) set_flooded(flooded, TRUE, skip_vis_contents_update = TRUE, mapload = mapload) + update_vis_contents() if(simulated) @@ -616,6 +633,7 @@ if(is_outside == new_outside) return FALSE + state_was_modified() is_outside = new_outside update_external_atmos_participation() AMBIENCE_QUEUE_TURF(src) @@ -929,3 +947,24 @@ /turf/take_vaporized_reagent(reagent, amount) return assume_gas(reagent, round(amount / REAGENT_UNITS_PER_GAS_MOLE)) + +// Tells the turf that it currently contains something that automated movement should consider if planning to enter the tile. +// This uses lazy list macros to reduce memory footprint, since for 99% of turfs the list would've been empty anyways. +/turf/proc/register_dangerous_object(atom/thing) + if(!istype(thing)) + return FALSE + LAZYDISTINCTADD(dangerous_objects, weakref(thing)) + +// Similar to above, for when the dangerous object stops being dangerous/gets deleted/moved/etc. +/turf/proc/unregister_dangerous_object(atom/thing) + if(!istype(thing)) + return FALSE + LAZYREMOVE(dangerous_objects, weakref(thing)) + +/turf/proc/is_safe_to_enter(mob/living/stepper) + if(LAZYLEN(dangerous_objects)) + for(var/weakref/ref in dangerous_objects) + var/atom/thing = ref.resolve() + if(istype(thing) && !QDELETED(thing) && !thing.is_safe_to_step(stepper)) + return FALSE + return TRUE diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 5c849feec9a2..0dfa901cf988 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -55,6 +55,7 @@ // Track a number of old values for the purposes of raising // state change events after changing the turf to the new type. + var/old_earliest_type = _earliest_type var/old_fire = fire var/old_above = above var/old_opacity = opacity @@ -80,6 +81,7 @@ var/old_ambient_light_old_r = ambient_light_old_r var/old_ambient_light_old_g = ambient_light_old_g var/old_ambient_light_old_b = ambient_light_old_b + var/old_dangerous_objects = dangerous_objects var/old_zone_membership_candidate = zone_membership_candidate @@ -104,6 +106,7 @@ // Set our observation bookkeeping lists back. changed_turf.event_listeners = old_event_listeners changed_turf._listening_to = old_listening_to + changed_turf.dangerous_objects = old_dangerous_objects changed_turf.affecting_heat_sources = old_affecting_heat_sources @@ -199,6 +202,9 @@ for(var/atom/movable/thing in changed_turf.get_contained_external_atoms()) thing.fall() + changed_turf._earliest_type = old_earliest_type + changed_turf.state_was_modified() + /turf/proc/transport_properties_from(turf/other, transport_air) if(transport_air && can_inherit_air && (other.zone || other.air)) if(!air) diff --git a/code/game/turfs/turf_fluids.dm b/code/game/turfs/turf_fluids.dm index fe248f689773..aafbb2bb1931 100644 --- a/code/game/turfs/turf_fluids.dm +++ b/code/game/turfs/turf_fluids.dm @@ -75,8 +75,8 @@ . = (get_fluid_depth() >= min) /turf/proc/get_fluid_name() - var/decl/material/mat = reagents?.get_primary_reagent_decl() - return mat.get_reagent_name(reagents, MAT_PHASE_LIQUID) || "liquid" + var/decl/material/mat = reagents?.get_primary_reagent_decl() || RESOLVE_TO_DECL(flooded) + return mat?.get_reagent_name(reagents, MAT_PHASE_LIQUID) || "liquid" /turf/get_fluid_depth() if(is_flooded(absolute=1)) @@ -179,6 +179,8 @@ if(!(. = ..())) return + state_was_modified() + if(REAGENT_TOTAL_LIQUID_VOLUME(reagents) < FLUID_SLURRY) dump_solid_reagents() @@ -204,7 +206,7 @@ for(var/checkdir in global.cardinal) var/turf/neighbor = get_step_resolving_mimic(src, checkdir) - if(REAGENT_TOTAL_VOLUME(neighbor?.reagents) > FLUID_QDEL_POINT) + if(istype(neighbor) && (islist(neighbor.reagents) || (istype(neighbor.reagents) && REAGENT_TOTAL_VOLUME(neighbor.reagents) > FLUID_QDEL_POINT))) ADD_ACTIVE_FLUID(neighbor) /turf/proc/dump_solid_reagents(datum/reagents/solids) diff --git a/code/game/turfs/turf_serde.dm b/code/game/turfs/turf_serde.dm new file mode 100644 index 000000000000..d8f7515f2b84 --- /dev/null +++ b/code/game/turfs/turf_serde.dm @@ -0,0 +1,60 @@ +/turf + var/_earliest_type + var/_state_was_modified + var/_contents_were_modified + +/turf/ShouldSerialize(_age) + if(type == _earliest_type && !_state_was_modified && !_contents_were_modified) + return FALSE + var/area/area = get_area(src) + if(!(area?.area_flags & AREA_FLAG_ALLOW_LEVEL_PERSISTENCE)) + return FALSE + return ..(_age) + +/turf/Serialize() + . = ..() + SERIALIZE_VALUE(loc, /atom/movable, list(x, y, z)) + SERIALIZE_IF_MODIFIED(is_outside, /turf) + +/turf/Deserialize(list/instance_map) + . = ..() + state_was_modified() + +/turf/proc/state_was_modified() + if(!simulated || _state_was_modified) + return + _state_was_modified = TRUE + update_level_persistence_tracking() + +/atom/proc/contents_were_modified() + var/turf/turf = get_turf(src) + turf?.contents_were_modified() + +/turf/contents_were_modified() + if(!simulated || _contents_were_modified) + return + _contents_were_modified = TRUE + update_level_persistence_tracking() + +/turf/proc/update_level_persistence_tracking() + var/area/area = get_area(src) + if(!(area?.area_flags & AREA_FLAG_ALLOW_LEVEL_PERSISTENCE)) + return + var/datum/level_data/level = SSmapping.levels_by_z[z] + if(!istype(level) || !level.is_persistent()) + return + var/list/coord = json_encode(list(x, y)) + LAZYSET(level.changed_turfs, coord, TRUE) + +/turf/proc/UnpackSerializableInstances() + // Get all recursively nested instances on this turf. + var/list/instances_to_unpack = list(src) + while(length(instances_to_unpack)) + var/datum/instance = instances_to_unpack[1] + instances_to_unpack.Cut(1, 2) + if(instance in .) + continue + LAZYADD(., instance) + var/list/packed_instances = instance.GetPossiblySerializableInstances() + if(length(packed_instances)) + instances_to_unpack |= packed_instances diff --git a/code/game/turfs/unsimulated/mask.dm b/code/game/turfs/unsimulated/mask.dm index 1b82356696eb..a380cfa054dc 100644 --- a/code/game/turfs/unsimulated/mask.dm +++ b/code/game/turfs/unsimulated/mask.dm @@ -9,6 +9,11 @@ icon_state = "rockvault" color = COLOR_SILVER +// Why do these exist? Are they just for typechecks when generating random maps? Does the flooding code even run for unsim turfs? /turf/unsimulated/mask/flooded flooded = /decl/material/liquid/water color = COLOR_LIQUID_WATER + +/turf/unsimulated/mask/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt \ No newline at end of file diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm index 1d26dbb74a65..3706a99fcfa1 100644 --- a/code/game/turfs/walls/_wall.dm +++ b/code/game/turfs/walls/_wall.dm @@ -129,8 +129,10 @@ var/global/list/wall_fullblend_objects = list( var/proj_damage = Proj.get_structure_damage() - if(Proj.ricochet_sounds && prob(15)) - playsound(src, pick(Proj.ricochet_sounds), 100, 1) + if(prob(15)) + var/list/ricochet_sounds = Proj.get_ricochet_sounds() + if(length(ricochet_sounds)) + playsound(src, pick(ricochet_sounds), 100, 1) if(reinf_material) if(Proj.atom_damage_type == BURN) diff --git a/code/game/turfs/walls/wall_brick.dm b/code/game/turfs/walls/wall_brick.dm index 28f4df68b140..1c529c70ab28 100644 --- a/code/game/turfs/walls/wall_brick.dm +++ b/code/game/turfs/walls/wall_brick.dm @@ -46,4 +46,4 @@ MATERIAL_BRICK_WALL(basalt) MATERIAL_BRICK_WALL(granite) MATERIAL_BRICK_WALL(marble) MATERIAL_BRICK_WALL(pottery) -#undef MATERIAL_BRICK_WALL \ No newline at end of file +#undef MATERIAL_BRICK_WALL diff --git a/code/game/turfs/walls/wall_material.dm b/code/game/turfs/walls/wall_material.dm index a71c306e0412..f82b5d0be9a0 100644 --- a/code/game/turfs/walls/wall_material.dm +++ b/code/game/turfs/walls/wall_material.dm @@ -25,5 +25,7 @@ girder_material = new_girder_material . = TRUE - if(. && !skip_update) - update_material() + if(.) + state_was_modified() + if(!skip_update) + queue_icon_update() diff --git a/code/game/turfs/walls/wall_natural.dm b/code/game/turfs/walls/wall_natural.dm index 4710e89e5626..b8ba4f36922b 100644 --- a/code/game/turfs/walls/wall_natural.dm +++ b/code/game/turfs/walls/wall_natural.dm @@ -20,6 +20,10 @@ var/global/_wall_chisel_skill = SKILL_CONSTRUCTION flooded = /decl/material/liquid/water color = COLOR_LIQUID_WATER +/turf/wall/natural/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt + /turf/wall/natural/get_paint_examine_message() return SPAN_NOTICE("It has been noticeably discoloured by the elements.") @@ -232,4 +236,4 @@ var/global/_wall_chisel_skill = SKILL_CONSTRUCTION if(length(S.base_materials)) return pick(S.base_materials) //Otherwise, just use level strata - return ..() \ No newline at end of file + return ..() diff --git a/code/game/turfs/walls/wall_natural_ramps.dm b/code/game/turfs/walls/wall_natural_ramps.dm index c36d31b5bd87..76526f13e7ad 100644 --- a/code/game/turfs/walls/wall_natural_ramps.dm +++ b/code/game/turfs/walls/wall_natural_ramps.dm @@ -1,5 +1,9 @@ /turf/wall/natural/proc/make_ramp(var/mob/user, var/new_slope, var/skip_icon_update = FALSE) + if(ramp_slope_direction == new_slope) + return + + state_was_modified() ramp_slope_direction = new_slope QDEL_NULL_LIST(engravings) diff --git a/code/game/turfs/walls/wall_natural_subtypes.dm b/code/game/turfs/walls/wall_natural_subtypes.dm index 976b793664f5..dc072ac25193 100644 --- a/code/game/turfs/walls/wall_natural_subtypes.dm +++ b/code/game/turfs/walls/wall_natural_subtypes.dm @@ -1,6 +1,10 @@ /turf/wall/natural/random reinf_material = null +// We want to avoid spawning random ores in Initialize() by serializing a subtype that does that. +/turf/wall/natural/random/GetSerializedType() + return /turf/wall/natural + /turf/wall/natural/random/proc/get_weighted_mineral_list() if(strata_override) var/decl/strata/strata_info = GET_DECL(strata_override) @@ -30,9 +34,15 @@ /turf/wall/natural/random/volcanic strata_override = /decl/strata/igneous +/turf/wall/natural/random/volcanic/GetSerializedType() + return /turf/wall/natural/volcanic + /turf/wall/natural/random/high_chance/volcanic strata_override = /decl/strata/igneous +/turf/wall/natural/random/high_chance/volcanic/GetSerializedType() + return /turf/wall/natural/volcanic + /turf/wall/natural/ice strata_override = /decl/strata/permafrost floor_type = /turf/floor/ice @@ -41,26 +51,56 @@ strata_override = /decl/strata/permafrost floor_type = /turf/floor/ice +/turf/wall/natural/random/ice/GetSerializedType() + return /turf/wall/natural/ice + /turf/wall/natural/random/high_chance/ice strata_override = /decl/strata/permafrost floor_type = /turf/floor/ice +/turf/wall/natural/random/high_chance/ice/GetSerializedType() + return /turf/wall/natural/ice + /turf/wall/natural/dirt material = /decl/material/solid/soil color = "#41311b" floor_type = /turf/floor/dirt -/turf/wall/natural/basalt - material = /decl/material/solid/stone/basalt - color = COLOR_DARK_GRAY - floor_type = /turf/floor/rock/basalt - -/turf/wall/natural/random/basalt - material = /decl/material/solid/stone/basalt - color = COLOR_DARK_GRAY - floor_type = /turf/floor/rock/basalt - -/turf/wall/natural/random/high_chance/basalt - material = /decl/material/solid/stone/basalt - color = COLOR_DARK_GRAY - floor_type = /turf/floor/rock/basalt +#define MATERIAL_NATURAL_TURFS(ID, MAT) \ +/turf/floor/rock/##ID { \ + color = /decl/material/##MAT::color; \ + material = /decl/material/##MAT \ +} \ +/turf/floor/rock/##ID/sand { \ + name = "sand"; \ + icon = 'icons/turf/flooring/sand.dmi'; \ + icon_state = "sand0"; \ + color = "#ae9e66"; \ + _flooring = /decl/flooring/sand; \ +} \ +/turf/wall/natural/##ID { \ + material = /decl/material/##MAT; \ + color = /decl/material/##MAT::color; \ + floor_type = /turf/floor/rock/##ID; \ +} \ +/turf/wall/natural/random/##ID { \ + material = /decl/material/##MAT; \ + color = /decl/material/##MAT::color; \ + floor_type = /turf/floor/rock/##ID; \ +} \ +/turf/wall/natural/random/##ID/GetSerializedType() { \ + return /turf/wall/natural/##ID; \ +} \ +/turf/wall/natural/random/high_chance/##ID { \ + material = /decl/material/##MAT; \ + color = /decl/material/##MAT::color; \ + floor_type = /turf/floor/rock/##ID \ +} \ +/turf/wall/natural/random/high_chance/##ID/GetSerializedType() { \ + return /turf/wall/natural/##ID; \ +} +MATERIAL_NATURAL_TURFS(sandstone, solid/stone/sandstone) +MATERIAL_NATURAL_TURFS(basalt, solid/stone/basalt) +MATERIAL_NATURAL_TURFS(granite, solid/stone/granite) +MATERIAL_NATURAL_TURFS(marble, solid/stone/marble) +#undef MATERIAL_NATURAL_TURFS \ No newline at end of file diff --git a/code/game/turfs/walls/wall_serde.dm b/code/game/turfs/walls/wall_serde.dm new file mode 100644 index 000000000000..53d737fffa2c --- /dev/null +++ b/code/game/turfs/walls/wall_serde.dm @@ -0,0 +1,20 @@ +/turf/wall/Serialize() + . = ..() + + SERIALIZE_DECL_IF_MODIFIED(material, /turf/wall) + SERIALIZE_DECL_IF_MODIFIED(girder_material, /turf/wall) + SERIALIZE_DECL_IF_MODIFIED(shutter_material, /turf/wall) + + SERIALIZE_IF_MODIFIED(shutter_state, /turf/wall) + SERIALIZE_IF_MODIFIED(stripe_color, /turf/wall) + SERIALIZE_IF_MODIFIED(damage, /turf/wall) + SERIALIZE_IF_MODIFIED(can_open, /turf/wall) + +/turf/wall/Deserialize(list/instance_map) + . = ..() + DESERIALIZE_DECL_TO_TYPE(girder_material) + DESERIALIZE_DECL_TO_TYPE(shutter_material) + +/turf/wall/natural/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(ramp_slope_direction, /turf/wall/natural) diff --git a/code/game/turfs/walls/wall_types.dm b/code/game/turfs/walls/wall_types.dm index ed23150edc8c..c0052e2a67f3 100644 --- a/code/game/turfs/walls/wall_types.dm +++ b/code/game/turfs/walls/wall_types.dm @@ -104,5 +104,11 @@ return 'icons/turf/walls/solid.dmi' /turf/wall/shuttle/dark + color = COLOR_GUNMETAL paint_color = COLOR_GUNMETAL stripe_color = COLOR_MAROON + +/turf/wall/shuttle/light + color = COLOR_SILVER + paint_color = COLOR_SILVER + stripe_color = COLOR_SKY_BLUE diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 151a6da37714..f1726ee6ac05 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -1156,35 +1156,8 @@ var/global/BSACooldown = 0 var/out = "Current mode: [SSticker.mode.name] ([SSticker.mode.uid])
" out += "
" - if(SSticker.mode.ert_disabled) - out += "Emergency Response Teams: disabled" - else - out += "Emergency Response Teams: enabled" - out += "
" - - if(SSticker.mode.deny_respawn) - out += "Respawning: disallowed" - else - out += "Respawning: allowed" - out += "
" - - out += "Shuttle delay multiplier: [SSticker.mode.shuttle_delay]
" - - if(SSticker.mode.auto_recall_shuttle) - out += "Shuttle auto-recall: enabled" - else - out += "Shuttle auto-recall: disabled" - out += "

" - - if(SSticker.mode.event_delay_mod_moderate) - out += "Moderate event time modifier: [SSticker.mode.event_delay_mod_moderate]
" - else - out += "Moderate event time modifier: unset
" - - if(SSticker.mode.event_delay_mod_major) - out += "Major event time modifier: [SSticker.mode.event_delay_mod_major]
" - else - out += "Major event time modifier: unset
" + var/list/options = SSticker.get_game_mode_options() + out += jointext(options, "
") out += "
" diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 52587d2a21d2..d4d3e91d6e5f 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -73,7 +73,6 @@ var/global/list/admin_verbs_admin = list( /datum/admins/proc/show_skills, /client/proc/man_up, /client/proc/global_man_up, - /client/proc/response_team, // Response Teams admin verb, /client/proc/toggle_antagHUD_use, /client/proc/toggle_antagHUD_restrictions, /client/proc/allow_character_respawn, // Allows a ghost to respawn , @@ -158,7 +157,8 @@ var/global/list/admin_verbs_server = list( /datum/admins/proc/removeserverwhitelist, /datum/admins/proc/panicbunker, /datum/admins/proc/addbunkerbypass, - /datum/admins/proc/revokebunkerbypass + /datum/admins/proc/revokebunkerbypass, + /datum/admins/proc/force_persistence_save_verb ) var/global/list/admin_verbs_debug = list( /datum/admins/proc/jump_to_fluid_source, diff --git a/code/modules/admin/quantum_mechanic.dm b/code/modules/admin/quantum_mechanic.dm index d9977490a913..4d7bc4f561e4 100644 --- a/code/modules/admin/quantum_mechanic.dm +++ b/code/modules/admin/quantum_mechanic.dm @@ -50,7 +50,7 @@ glasses = /obj/item/clothing/glasses/sunglasses/quantum uniform = /obj/item/clothing/jumpsuit/quantum shoes = /obj/item/clothing/shoes/color/black/quantum - l_ear = /obj/item/radio/headset/ert/quantum + l_ear = /obj/item/radio/headset/quantum back = /obj/item/backpack/holding/quantum head = /obj/item/clothing/head/beret belt = /obj/item/belt/utility/full/quantum @@ -165,15 +165,16 @@ return ..() // Headset -/obj/item/radio/headset/ert/quantum +/obj/item/radio/headset/quantum name = "quantum mechanic's headset" desc = "A quantum mechanic's headset. The letter 'Ω' is stamped on the side." + icon = 'icons/obj/items/device/radio/headsets/headset_admin.dmi' encryption_keys = list( /obj/item/encryptionkey/binary, - /obj/item/encryptionkey/ert + /obj/item/encryptionkey/specops ) -/obj/item/radio/headset/ert/quantum/attack_hand(mob/user) +/obj/item/radio/headset/quantum/attack_hand(mob/user) if(!user) return TRUE diff --git a/code/modules/admin/verbs/atmosdebug.dm b/code/modules/admin/verbs/atmosdebug.dm index 19916f831e9d..2c7947e4a9b0 100644 --- a/code/modules/admin/verbs/atmosdebug.dm +++ b/code/modules/admin/verbs/atmosdebug.dm @@ -7,14 +7,14 @@ SSstatistics.add_field_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! for (var/datum/powernet/PN in SSmachines.powernets) - if (!PN.nodes || !PN.nodes.len) + if (!LAZYLEN(PN.nodes)) if(PN.cables && (PN.cables.len > 1)) var/obj/structure/cable/C = PN.cables[1] var/area/A = get_area(C.loc) to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [A?.proper_name]") - if (!PN.cables || (PN.cables.len < 10)) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - var/area/A = get_area(C.loc) - to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [A?.proper_name]") + var/cable_count = LAZYLEN(PN.cables) + if (cable_count > 1 && cable_count < 10) + var/obj/structure/cable/C = PN.cables[1] + var/area/A = get_area(C.loc) + to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [A?.proper_name]") diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 8ff55c421dea..e39ed6b84fa2 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -313,8 +313,7 @@ for(var/obj/machinery/rad_collector/Rad in SSmachines.machinery) if(Rad.anchored) if(!Rad.loaded_tank) - Rad.loaded_tank = new /obj/item/tank/hydrogen(Rad) - Rad.loaded_tank.air_contents.gas[/decl/material/gas/hydrogen] = 70 + Rad.loaded_tank = new /obj/item/tank/hydrogen/collector(Rad) Rad.drainratio = 0 if(!Rad.active) Rad.toggle_power() diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index d588df659e21..606ac3bedc70 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -5,18 +5,16 @@ if (!check_rights(R_FUN)) return - var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in SSmapping.spawnable_map_templates - if(!map) + var/datum/map_template/template = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in SSmapping.spawnable_map_templates + if(!istype(template)) return - var/datum/map_template/template = SSmapping.get_template(map) - var/turf/T = get_turf(usr) if(!T) return var/list/preview = list() - for(var/S in template.get_affected_turfs(T,centered = TRUE)) + for(var/S in template.get_affected_turfs(T, centered = TRUE)) preview += image('icons/turf/overlays.dmi',S,"greenOverlay") usr.client.images += preview if(alert(usr,"Confirm location.","Template Confirm","Yes","No") == "Yes") @@ -33,21 +31,20 @@ if(!check_rights(R_FUN)) return + if(GAME_STATE < RUNLEVEL_LOBBY) to_chat(usr, "Please wait for the master controller to initialize before loading maps!") return - var/map = input(usr, "Choose a Map Template to place on a new zlevel","Place Map Template") as null|anything in SSmapping.spawnable_map_templates - if(!map) + var/datum/map_template/template = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in SSmapping.spawnable_map_templates + if(!istype(template)) return - var/datum/map_template/template = SSmapping.get_template(map) - if(template.loaded && !(template.template_flags & TEMPLATE_FLAG_ALLOW_DUPLICATES)) to_chat(usr, SPAN_WARNING("That template has already been loaded and is flagged against being loaded again.")) return - var/new_z_centre = template.load_new_z(FALSE) // Don't skip changeturf + var/new_z_centre = template.load_new_z() if (new_z_centre) log_and_message_admins("has placed a map template ([template.name]) on a new zlevel.", location=new_z_centre) else diff --git a/code/modules/atmospherics/components/unary/cold_sink.dm b/code/modules/atmospherics/components/unary/cold_sink.dm index 18ab66f03864..7f4eb8bb7ebe 100644 --- a/code/modules/atmospherics/components/unary/cold_sink.dm +++ b/code/modules/atmospherics/components/unary/cold_sink.dm @@ -1,136 +1,42 @@ //TODO: Put this under a common parent type with heaters to cut down on the copypasta #define FREEZER_PERF_MULT 2.5 -/obj/machinery/atmospherics/unary/freezer +/obj/machinery/atmospherics/unary/temperature/freezer name = "gas cooling system" desc = "Cools gas when connected to a pipe network." icon = 'icons/obj/Cryogenic2.dmi' icon_state = "freezer_0" - layer = STRUCTURE_LAYER - density = TRUE - anchored = TRUE - use_power = POWER_USE_OFF - idle_power_usage = 5 // 5 Watts for thermostat related circuitry - base_type = /obj/machinery/atmospherics/unary/freezer - construct_state = /decl/machine_construction/default/panel_closed - uncreated_component_parts = null - stat_immune = 0 - + base_icon_state = "freezer" + base_type = /obj/machinery/atmospherics/unary/temperature/freezer + ui_title = "Gas Cooling System" + performance_multiplier = FREEZER_PERF_MULT var/heatsink_temperature = T20C // The constant temperature reservoir into which the freezer pumps heat. Probably the hull of the station or something. - var/internal_volume = 600 // L - - var/max_power_rating = 20000 // Power rating when the usage is turned up to 100 - var/power_setting = 100 - - var/set_temperature = T20C // Thermostat - var/cooling = 0 - -/obj/machinery/atmospherics/unary/freezer/on_update_icon() - if(LAZYLEN(nodes_to_networks)) - if(use_power && cooling) - icon_state = "freezer_1" - else - icon_state = "freezer" - else - icon_state = "freezer_0" - -/obj/machinery/atmospherics/unary/freezer/interface_interact(mob/user) - ui_interact(user) - return TRUE -/obj/machinery/atmospherics/unary/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // this is the data which will be sent to the ui - var/data[0] - data["on"] = use_power ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["minGasTemperature"] = 0 - data["maxGasTemperature"] = round(T20C+500) - data["targetGasTemperature"] = round(set_temperature) - data["powerSetting"] = power_setting - - var/temp_class = "good" +/obj/machinery/atmospherics/unary/temperature/freezer/get_temperature_class() + . = "good" if(air_contents.temperature > (T0C - 20)) - temp_class = "bad" + . = "bad" else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) - temp_class = "average" - data["gasTemperatureClass"] = temp_class - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 440, 300) - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) + . = "average" -/obj/machinery/atmospherics/unary/freezer/OnTopic(mob/user, href_list) - if((. = ..())) - return - if(href_list["toggleStatus"]) - update_use_power(!use_power) - . = TOPIC_REFRESH - if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - set_temperature = clamp(set_temperature + amount, 0, 1000) - . = TOPIC_REFRESH - if(href_list["setPower"]) //setting power to 0 is redundant anyways - var/new_setting = clamp(text2num(href_list["setPower"]), 0, 100) - set_power_level(new_setting) - . = TOPIC_REFRESH - -/obj/machinery/atmospherics/unary/freezer/Process() - ..() +/obj/machinery/atmospherics/unary/temperature/freezer/should_modify_gas() + return air_contents.temperature > set_temperature - if(stat & (NOPOWER|BROKEN) || !use_power) - cooling = 0 - update_icon() - return +/obj/machinery/atmospherics/unary/temperature/freezer/modify_gas() + var/heat_transfer = min(air_contents.get_thermal_energy_change(set_temperature - 5), 0) - if(LAZYLEN(nodes_to_networks) && air_contents.temperature > set_temperature) - cooling = 1 + //Assume the heat is being pumped into the hull which is fixed at heatsink_temperature + //not /really/ proper thermodynamics but whatever + var/cop = performance_multiplier * air_contents.temperature/heatsink_temperature //heatpump coefficient of performance from thermodynamics -> power used = heat_transfer/cop + heat_transfer = min(heat_transfer, cop * power_rating) //limit heat transfer by available power - var/heat_transfer = max( -air_contents.get_thermal_energy_change(set_temperature - 5), 0 ) - - //Assume the heat is being pumped into the hull which is fixed at heatsink_temperature - //not /really/ proper thermodynamics but whatever - var/cop = FREEZER_PERF_MULT * air_contents.temperature/heatsink_temperature //heatpump coefficient of performance from thermodynamics -> power used = heat_transfer/cop - heat_transfer = min(heat_transfer, cop * power_rating) //limit heat transfer by available power - - var/removed = -air_contents.add_thermal_energy(-heat_transfer) //remove the heat - if(debug) - visible_message("[src]: Removing [removed] W.") - - use_power_oneoff(power_rating) - - update_networks() - else - cooling = 0 - - update_icon() + var/removed = -air_contents.add_thermal_energy(heat_transfer) //remove the heat + if(debug) + visible_message("[src]: Removing [removed] W.") //upgrading parts -/obj/machinery/atmospherics/unary/freezer/RefreshParts() +/obj/machinery/atmospherics/unary/temperature/freezer/RefreshParts() ..() - var/cap_rating = clamp(total_component_rating_of_type(/obj/item/stock_parts/capacitor), 0, 20) var/manip_rating = clamp(total_component_rating_of_type(/obj/item/stock_parts/manipulator), 1, 10) var/bin_rating = clamp(total_component_rating_of_type(/obj/item/stock_parts/matter_bin), 0, 10) - - power_rating = initial(power_rating) * cap_rating / 2 //more powerful - heatsink_temperature = initial(heatsink_temperature) / ((manip_rating + bin_rating) / 2) //more efficient - air_contents.total_volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating - set_power_level(power_setting) - -/obj/machinery/atmospherics/unary/freezer/proc/set_power_level(var/new_power_setting) - power_setting = new_power_setting - power_rating = max_power_rating * (power_setting/100) - -/obj/machinery/atmospherics/unary/freezer/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - if(panel_open) - . += "The maintenance hatch is open." + heatsink_temperature = initial(heatsink_temperature) / ((manip_rating + bin_rating) / 2) //more efficient \ No newline at end of file diff --git a/code/modules/atmospherics/components/unary/heat_source.dm b/code/modules/atmospherics/components/unary/heat_source.dm index 435bf70e9f1e..c69833d1de16 100644 --- a/code/modules/atmospherics/components/unary/heat_source.dm +++ b/code/modules/atmospherics/components/unary/heat_source.dm @@ -1,123 +1,32 @@ //TODO: Put this under a common parent type with freezers to cut down on the copypasta #define HEATER_PERF_MULT 2.5 -/obj/machinery/atmospherics/unary/heater +/obj/machinery/atmospherics/unary/temperature/heater name = "gas heating system" desc = "Heats gas when connected to a pipe network." icon = 'icons/obj/Cryogenic2.dmi' icon_state = "heater_0" - layer = STRUCTURE_LAYER - density = TRUE - anchored = TRUE - use_power = POWER_USE_OFF - idle_power_usage = 5 //5 Watts for thermostat related circuitry - base_type = /obj/machinery/atmospherics/unary/heater - construct_state = /decl/machine_construction/default/panel_closed - uncreated_component_parts = null - stat_immune = 0 - connect_types = CONNECT_TYPE_REGULAR | CONNECT_TYPE_FUEL + base_icon_state = "heater" + base_type = /obj/machinery/atmospherics/unary/temperature/heater + performance_multiplier = HEATER_PERF_MULT - var/max_temperature = T20C + 680 - var/internal_volume = 600 //L +/obj/machinery/atmospherics/unary/temperature/heater/should_modify_gas() + return air_contents.temperature < set_temperature - var/max_power_rating = 20000 //power rating when the usage is turned up to 100 - var/power_setting = 100 +/obj/machinery/atmospherics/unary/temperature/heater/modify_gas() + // amount of heat needed to heat air_contents to set_temperature + 5 + var/heat_transfer = max(air_contents.get_thermal_energy_change(set_temperature + 5), 0) + heat_transfer = min(heat_transfer, performance_multiplier * power_rating) // don't overshoot + air_contents.add_thermal_energy(heat_transfer) - var/set_temperature = T20C //thermostat - var/heating = 0 //mainly for icon updates - -/obj/machinery/atmospherics/unary/heater/on_update_icon() - if(LAZYLEN(nodes_to_networks)) - if(use_power && heating) - icon_state = "heater_1" - else - icon_state = "heater" - else - icon_state = "heater_0" - -/obj/machinery/atmospherics/unary/heater/Process() - ..() - - if(stat & (NOPOWER|BROKEN) || !use_power) - heating = 0 - update_icon() - return - - if(LAZYLEN(nodes_to_networks) && air_contents.total_moles && air_contents.temperature < set_temperature) - air_contents.add_thermal_energy(power_rating * HEATER_PERF_MULT) - use_power_oneoff(power_rating) - - heating = 1 - update_networks() - else - heating = 0 - - update_icon() - -/obj/machinery/atmospherics/unary/heater/interface_interact(mob/user) - ui_interact(user) - return TRUE - -/obj/machinery/atmospherics/unary/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // this is the data which will be sent to the ui - var/data[0] - data["on"] = use_power ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["minGasTemperature"] = 0 - data["maxGasTemperature"] = round(max_temperature) - data["targetGasTemperature"] = round(set_temperature) - data["powerSetting"] = power_setting - - var/temp_class = "normal" +/obj/machinery/atmospherics/unary/temperature/heater/get_temperature_class() + . = "normal" if(air_contents.temperature > (T20C+40)) - temp_class = "bad" - data["gasTemperatureClass"] = temp_class - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 440, 300) - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/heater/OnTopic(mob/user, href_list) - if((. = ..())) - return - if(href_list["toggleStatus"]) - update_use_power(!use_power) - . = TOPIC_REFRESH - if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - set_temperature = clamp(set_temperature + amount, 0, max_temperature) - . = TOPIC_REFRESH - if(href_list["setPower"]) //setting power to 0 is redundant anyways - var/new_setting = clamp(text2num(href_list["setPower"]), 0, 100) - set_power_level(new_setting) - . = TOPIC_REFRESH + . = "bad" //upgrading parts -/obj/machinery/atmospherics/unary/heater/RefreshParts() +/obj/machinery/atmospherics/unary/temperature/heater/RefreshParts() ..() var/cap_rating = clamp(total_component_rating_of_type(/obj/item/stock_parts/capacitor), 1, 20) var/bin_rating = clamp(total_component_rating_of_type(/obj/item/stock_parts/matter_bin), 0, 10) - - max_power_rating = initial(max_power_rating) * cap_rating / 2 max_temperature = max(initial(max_temperature) - T20C, 0) * ((bin_rating * 4 + cap_rating) / 5) + T20C - air_contents.total_volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating - set_power_level(power_setting) - -/obj/machinery/atmospherics/unary/heater/proc/set_power_level(var/new_power_setting) - power_setting = new_power_setting - power_rating = max_power_rating * (power_setting/100) - -/obj/machinery/atmospherics/unary/heater/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - if(panel_open) - . += "The maintenance hatch is open." diff --git a/code/modules/atmospherics/components/unary/temperature_base.dm b/code/modules/atmospherics/components/unary/temperature_base.dm new file mode 100644 index 000000000000..819c6a161551 --- /dev/null +++ b/code/modules/atmospherics/components/unary/temperature_base.dm @@ -0,0 +1,122 @@ +/obj/machinery/atmospherics/unary/temperature + abstract_type = /obj/machinery/atmospherics/unary/temperature + name = "gas thermoregulation system" + desc = "This should not be visible." + icon = 'icons/obj/Cryogenic2.dmi' + icon_state = "heater_0" + layer = STRUCTURE_LAYER + density = TRUE + anchored = TRUE + use_power = POWER_USE_OFF + idle_power_usage = 5 //5 Watts for thermostat related circuitry + construct_state = /decl/machine_construction/default/panel_closed + uncreated_component_parts = null + stat_immune = 0 + connect_types = CONNECT_TYPE_REGULAR | CONNECT_TYPE_FUEL + var/internal_volume = 600 //L + var/max_power_rating = 20000 //power rating when the usage is turned up to 100 + var/power_setting = 100 + var/set_temperature = T20C //thermostat + var/is_modifying_gas = FALSE //mainly for icon updates + var/base_icon_state = "heater" + var/performance_multiplier = 1 + var/max_temperature = T20C+500 + var/ui_title = "Gas Thermoregulation System" + +/obj/machinery/atmospherics/unary/temperature/on_update_icon() + if(!LAZYLEN(nodes_to_networks)) + icon_state = "[base_icon_state]_0" + else if(use_power && is_modifying_gas) + icon_state = "[base_icon_state]_1" + else + icon_state = base_icon_state + +// Modify air_contents in this proc. +/obj/machinery/atmospherics/unary/temperature/proc/modify_gas() + +/obj/machinery/atmospherics/unary/temperature/proc/should_modify_gas() + return FALSE + +/obj/machinery/atmospherics/unary/temperature/Process() + ..() + + is_modifying_gas = FALSE + if(stat & (NOPOWER|BROKEN) || !use_power) + update_icon() + return + + if(LAZYLEN(nodes_to_networks) && air_contents.total_moles && should_modify_gas()) + modify_gas() + is_modifying_gas = TRUE + use_power_oneoff(power_rating) + update_networks() + + update_icon() + +/obj/machinery/atmospherics/unary/temperature/interface_interact(mob/user) + ui_interact(user) + return TRUE + +/obj/machinery/atmospherics/unary/temperature/proc/get_temperature_class() + PROTECTED_PROC(TRUE) + return "normal" + +/obj/machinery/atmospherics/unary/temperature/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // this is the data which will be sent to the ui + var/data[0] + data["on"] = use_power ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["minGasTemperature"] = 0 + data["maxGasTemperature"] = round(max_temperature) + data["targetGasTemperature"] = round(set_temperature) + data["powerSetting"] = power_setting + + data["gasTemperatureClass"] = get_temperature_class() + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "freezer.tmpl", ui_title, 440, 300) + // when the ui is first opened this is the data it will use + ui.set_initial_data(data) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/temperature/OnTopic(mob/user, href_list) + if((. = ..())) + return + if(href_list["toggleStatus"]) + update_use_power(!use_power) + . = TOPIC_REFRESH + if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + set_temperature = clamp(set_temperature + amount, 0, max_temperature) + . = TOPIC_REFRESH + if(href_list["setPower"]) //setting power to 0 is redundant anyways + var/new_setting = clamp(text2num(href_list["setPower"]), 0, 100) + set_power_level(new_setting) + . = TOPIC_REFRESH + +//upgrading parts +/obj/machinery/atmospherics/unary/temperature/RefreshParts() + ..() + var/cap_rating = clamp(total_component_rating_of_type(/obj/item/stock_parts/capacitor), 1, 20) + var/bin_rating = clamp(total_component_rating_of_type(/obj/item/stock_parts/matter_bin), 0, 10) + + max_power_rating = initial(max_power_rating) * cap_rating / 2 + air_contents.total_volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating + set_power_level(power_setting) + +/obj/machinery/atmospherics/unary/temperature/proc/set_power_level(var/new_power_setting) + power_setting = new_power_setting + power_rating = max_power_rating * (power_setting/100) + +/obj/machinery/atmospherics/unary/temperature/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + if(panel_open) + . += "The maintenance hatch is open." diff --git a/code/modules/augment/passive/armor.dm b/code/modules/augment/passive/armor.dm deleted file mode 100644 index 306de5710645..000000000000 --- a/code/modules/augment/passive/armor.dm +++ /dev/null @@ -1,12 +0,0 @@ -/obj/item/organ/internal/augment/armor - name = "subdermal armor" - allowed_organs = list(BP_AUGMENT_CHEST_ARMOUR) - icon_state = "armor-chest" - desc = "A flexible composite mesh designed to prevent tearing and puncturing of underlying tissue." - material = /decl/material/solid/metal/steel - origin_tech = @'{"materials":4,"engineering":2,"biotech":3}' - var/brute_mult = 0.8 - var/burn_mult = 1 - -/obj/item/organ/internal/augment/armor/reset_matter() - matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) \ No newline at end of file diff --git a/code/modules/backgrounds/citizenship/_citizenship.dm b/code/modules/backgrounds/citizenship/_citizenship.dm index d6c349aa1edd..e16cc9455ac7 100644 --- a/code/modules/backgrounds/citizenship/_citizenship.dm +++ b/code/modules/backgrounds/citizenship/_citizenship.dm @@ -1,6 +1,7 @@ /decl/background_detail/citizenship abstract_type = /decl/background_detail/citizenship category = /decl/background_category/citizenship + var/ruling_body = "Other Faction" var/capital var/size_heading = "Systems" var/size_value diff --git a/code/modules/backgrounds/citizenship/citizenship_other.dm b/code/modules/backgrounds/citizenship/citizenship_other.dm index d121a81af01a..cfeed30a29cf 100644 --- a/code/modules/backgrounds/citizenship/citizenship_other.dm +++ b/code/modules/backgrounds/citizenship/citizenship_other.dm @@ -14,3 +14,16 @@ description = "You do not possess any kind of official citizenship." economic_power = 0 capital = "None" + +/decl/background_detail/citizenship/synthetic + name = "Stateless Drone" + uid = "stateless_drone" + description = "Drones are considered property in most systems. Thus, statelessness is ubiqtuous for them." + secondary_langs = list( + /decl/language/machine, + /decl/language/human/common, + /decl/language/sign + ) + +/decl/background_detail/citizenship/synthetic/sanitize_background_name(new_name) + return sanitize_name(new_name, allow_numbers = TRUE) diff --git a/code/modules/banners/_banner_frame.dm b/code/modules/banners/_banner_frame.dm index 745de870f6ba..fab17e290c66 100644 --- a/code/modules/banners/_banner_frame.dm +++ b/code/modules/banners/_banner_frame.dm @@ -21,12 +21,19 @@ var/obj/item/banner/banner var/accepts_banner_type = /obj/item/banner +// Avoiding random generation behavior on subtypes. +// We don't serialize a reference to the banner item anyway. +/obj/structure/banner_frame/GetSerializedType() + return /obj/structure/banner_frame + /obj/structure/banner_frame/set_dir(ndir) return ..(force_south_facing ? SOUTH : ndir) /obj/structure/banner_frame/Initialize(ml, _mat, _reinf_mat) if(ispath(banner)) set_banner(new banner(src)) + else if(isnull(banner)) + set_banner(locate(/obj/item/banner) in src) . = ..() update_icon() diff --git a/code/modules/banners/sign_post.dm b/code/modules/banners/sign_post.dm index d4b4a0c45a18..f41ebb0157e4 100644 --- a/code/modules/banners/sign_post.dm +++ b/code/modules/banners/sign_post.dm @@ -9,12 +9,18 @@ icon_state = "sign_preview" density = TRUE +/obj/structure/banner_frame/sign/GetSerializedType() + return /obj/structure/banner_frame/sign + /obj/structure/banner_frame/sign/wall base_icon_state = "sign_hanging" icon_state = "sign_hanging_preview" force_south_facing = FALSE density = FALSE +/obj/structure/banner_frame/sign/wall/GetSerializedType() + return /obj/structure/banner_frame/sign/wall + /obj/structure/banner_frame/sign/random/Initialize(ml, _mat, _reinf_mat) material = pick(decls_repository.get_decls_of_subtype(/decl/material/solid/organic/wood)) ..() diff --git a/code/modules/bodytype/_bodytype.dm b/code/modules/bodytype/_bodytype.dm index fd6206eefa45..600043bdc11b 100644 --- a/code/modules/bodytype/_bodytype.dm +++ b/code/modules/bodytype/_bodytype.dm @@ -89,6 +89,8 @@ var/global/list/bodytypes_by_category = list() var/z_flags = 0 /// Amount to shift overlays when lying. TODO: check if this is still needed with KEEP_TOGETHER var/list/prone_overlay_offset + /// Set to TRUE to skip unit testing as a primary bodytype in a human. Generally for partial prosthetic models. + var/skip_organ_validation = FALSE /// Per-bodytype per-zone message strings, see /mob/proc/get_hug_zone_messages var/list/default_hug_message @@ -675,7 +677,7 @@ var/global/list/bodytypes_by_category = list() var/organ_type = has_organ[organ_tag] var/obj/item/organ/O = new organ_type(H, null, supplied_data) if(organ_tag != O.organ_tag) - warning("[O.type] has a default organ tag \"[O.organ_tag]\" that differs from the species' organ tag \"[organ_tag]\". Updating organ_tag to match.") + warning("[O.type] has a default organ tag \"[O.organ_tag]\" that differs from the bodytype organ tag \"[organ_tag]\". Updating organ_tag to match.") O.organ_tag = organ_tag H.add_organ(O, GET_EXTERNAL_ORGAN(H, O.parent_organ), FALSE, FALSE, skip_health_update = TRUE) H.update_health() diff --git a/code/modules/bodytype/bodytype_prosthetic_models.dm b/code/modules/bodytype/bodytype_prosthetic_models.dm index d7156bf6bdc2..6d6f47ff139e 100644 --- a/code/modules/bodytype/bodytype_prosthetic_models.dm +++ b/code/modules/bodytype/bodytype_prosthetic_models.dm @@ -19,9 +19,4 @@ required_map_tech = MAP_TECH_LEVEL_MEDIEVAL uid = "bodytype_prosthetic_wooden" -DEFINE_ROBOLIMB_MODEL_TRAITS(/decl/bodytype/prosthetic/wooden, pirate, 0, "wooden") - -// Dummy/stub prosthetic type for augment implants. -/decl/bodytype/prosthetic/augment - name = "Augment" - uid = "bodytype_prosthetic_augment" +DEFINE_ROBOLIMB_MODEL_TRAITS(/decl/bodytype/prosthetic/wooden, pirate, 0, "wooden") \ No newline at end of file diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 67bb71649839..691812c23018 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -221,12 +221,14 @@ var/global/template_file_name = "all_templates.json" /// Handles adding a directory's templates to the compiled templates list. /datum/asset/nanoui/proc/merge_templates(use_dir) PRIVATE_PROC(TRUE) + var/static/regex/whitespace = new(@"[\n\t]+", "g") var/list/templates = flist(use_dir) for(var/filename in templates) if(copytext(filename, length(filename)) != "/") - templates[filename] = replacetext(replacetext(file2text(use_dir + filename), "\n", ""), "\t", "") + templates[filename] = whitespace.Replace(file2text(use_dir + filename), "") else templates -= filename + CHECK_TICK return templates /datum/asset/nanoui/send(client, uncommon) diff --git a/code/modules/client/lobby_handler.dm b/code/modules/client/lobby_handler.dm index 6bfbe052376c..59a3471aba4d 100644 --- a/code/modules/client/lobby_handler.dm +++ b/code/modules/client/lobby_handler.dm @@ -1,4 +1,6 @@ /decl/lobby_handler + var/browser_width = 560 + var/browser_height = 280 var/list/lobby_options = list( /datum/lobby_option/setup, /datum/lobby_option/view_manifest, diff --git a/code/modules/client/preference_setup/loadout/_defines.dm b/code/modules/client/preference_setup/loadout/_defines.dm deleted file mode 100644 index c98f5c4d3134..000000000000 --- a/code/modules/client/preference_setup/loadout/_defines.dm +++ /dev/null @@ -1,6 +0,0 @@ -#define GEAR_HAS_COLOR_SELECTION BITFLAG(0) -#define GEAR_HAS_TYPE_SELECTION BITFLAG(1) -#define GEAR_HAS_SUBTYPE_SELECTION BITFLAG(2) -#define GEAR_HAS_CUSTOM_SELECTION BITFLAG(3) -#define GEAR_NO_EQUIP BITFLAG(4) -#define GEAR_NO_FINGERPRINTS BITFLAG(5) diff --git a/code/modules/client/preference_setup/loadout/gear_tweaks.dm b/code/modules/client/preference_setup/loadout/gear_tweaks.dm index c7c50e1603f3..b805d002e796 100644 --- a/code/modules/client/preference_setup/loadout/gear_tweaks.dm +++ b/code/modules/client/preference_setup/loadout/gear_tweaks.dm @@ -54,7 +54,7 @@ /datum/gear_tweak/color/markings/tweak_item(mob/user, obj/item/clothing/clothes, metadata) if(valid_colors && !(metadata in valid_colors)) return GEAR_TWEAK_SKIPPED - clothes.markings_color = sanitize_hexcolor(metadata, clothes.markings_color) + clothes.set_markings_color(sanitize_hexcolor(metadata, clothes.markings_color)) return GEAR_TWEAK_SUCCESS /* diff --git a/code/modules/client/preference_setup/loadout/lists/footwear.dm b/code/modules/client/preference_setup/loadout/lists/footwear.dm index 72760aca59c6..b9990a778bac 100644 --- a/code/modules/client/preference_setup/loadout/lists/footwear.dm +++ b/code/modules/client/preference_setup/loadout/lists/footwear.dm @@ -26,7 +26,8 @@ /obj/item/clothing/shoes/workboots, /obj/item/clothing/shoes/jackboots/duty, /obj/item/clothing/shoes/jackboots/jungleboots, - /obj/item/clothing/shoes/jackboots/desertboots + /obj/item/clothing/shoes/jackboots/desertboots, + /obj/item/clothing/shoes/winterboots ) /decl/loadout_option/shoes/color diff --git a/code/modules/client/preference_setup/loadout/lists/suits.dm b/code/modules/client/preference_setup/loadout/lists/suits.dm index 071c2a139cab..660190b9bbb1 100644 --- a/code/modules/client/preference_setup/loadout/lists/suits.dm +++ b/code/modules/client/preference_setup/loadout/lists/suits.dm @@ -64,6 +64,12 @@ path = /obj/item/clothing/suit/jacket/winter uid = "gear_suit_winter_coat" +/decl/loadout_option/suit/parka + name = "parka" + path = /obj/item/clothing/suit/jacket/winter/parka + loadout_flags = GEAR_HAS_COLOR_SELECTION + uid = "gear_suit_parka" + /decl/loadout_option/suit/track name = "track jacket selection" path = /obj/item/clothing/suit/toggle/track @@ -111,7 +117,7 @@ cost = 3 uid = "gear_suit_cloak" -/decl/loadout_option/suit/cloak +/decl/loadout_option/suit/hooded_cloak name = "cloak, hooded" path = /obj/item/clothing/suit/hooded_cloak loadout_flags = GEAR_HAS_COLOR_SELECTION diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index ceb5eaaeac52..d8b2da85e07e 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -406,6 +406,13 @@ var/list/allowed_branches /// Skills required to spawn with this item. var/list/allowed_skills + // The various valid values for loadout_flags. + var/const/GEAR_HAS_COLOR_SELECTION = BITFLAG(0) + var/const/GEAR_HAS_TYPE_SELECTION = BITFLAG(1) + var/const/GEAR_HAS_SUBTYPE_SELECTION = BITFLAG(2) + var/const/GEAR_HAS_CUSTOM_SELECTION = BITFLAG(3) + var/const/GEAR_NO_EQUIP = BITFLAG(4) + var/const/GEAR_NO_FINGERPRINTS = BITFLAG(5) /// Special tweaks in new var/loadout_flags /// Special tweak in New diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm index f6980156009e..952ab86c409f 100644 --- a/code/modules/clothing/_clothing.dm +++ b/code/modules/clothing/_clothing.dm @@ -452,6 +452,14 @@ if(get_vitals_sensor()) LAZYADD(., /decl/interaction_handler/clothing_set_sensors) +/obj/item/clothing/proc/set_markings_color(new_color) + if(markings_color != new_color) + markings_color = new_color + update_icon() + update_clothing_icon() + return TRUE + return FALSE + /decl/interaction_handler/clothing_set_sensors name = "Set Sensors Level" expected_target_type = /obj/item/clothing @@ -464,4 +472,3 @@ /decl/interaction_handler/clothing_set_sensors/invoked(atom/target, mob/user, obj/item/prop) var/obj/item/clothing/clothing = target clothing.set_sensors(user) - diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 50adae6297ca..eb812a3b3b06 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -332,8 +332,14 @@ CHAMELEON_VERB(/obj/item/clothing/chameleon, "Change Accessory Appearance") P.icon = initial(copy_projectile.icon) P.icon_state = initial(copy_projectile.icon_state) P.pass_flags = initial(copy_projectile.pass_flags) + P.fire_sound = initial(copy_projectile.fire_sound) + P.silenced = copy_projectile.silenced + P.hitsound = initial(copy_projectile.hitsound) + P.hitsound_non_mob = initial(copy_projectile.hitsound_non_mob) P.hitscan = initial(copy_projectile.hitscan) P.step_delay = initial(copy_projectile.step_delay) + P.speed = initial(copy_projectile.speed) + P.range = initial(copy_projectile.range) P.muzzle_type = initial(copy_projectile.muzzle_type) P.tracer_type = initial(copy_projectile.tracer_type) P.impact_type = initial(copy_projectile.impact_type) diff --git a/code/modules/clothing/costumes/rank.dm b/code/modules/clothing/costumes/rank.dm index b2bc5a1fc940..f08fdefeb3bd 100644 --- a/code/modules/clothing/costumes/rank.dm +++ b/code/modules/clothing/costumes/rank.dm @@ -15,7 +15,7 @@ name = "head of personnel's suit" icon = 'icons/clothing/uniform_hop_whimsy.dmi' -/obj/item/clothing/costume/hosformalmale +/obj/item/clothing/costume/hosformal name = "head of security's formal uniform" desc = "A male head of security's formal-wear, for special occasions." icon = 'icons/clothing/uniform_hos_formal.dmi' diff --git a/code/modules/clothing/jumpsuits/_jumpsuit.dm b/code/modules/clothing/jumpsuits/_jumpsuit.dm index 205a8af7997a..21be4c2e731a 100644 --- a/code/modules/clothing/jumpsuits/_jumpsuit.dm +++ b/code/modules/clothing/jumpsuits/_jumpsuit.dm @@ -1,6 +1,6 @@ /obj/item/clothing/jumpsuit name = "jumpsuit" - desc = "The latest in space fashion." + desc = "The latest in utilitarian fashion." icon = 'icons/clothing/jumpsuits/jumpsuit.dmi' body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_LEGS|SLOT_ARMS permeability_coefficient = 0.90 diff --git a/code/modules/clothing/masks/monitor.dm b/code/modules/clothing/masks/monitor.dm index afdbbdd64194..46d60d8112c7 100644 --- a/code/modules/clothing/masks/monitor.dm +++ b/code/modules/clothing/masks/monitor.dm @@ -1,6 +1,6 @@ //IPC-face object for FPB. /obj/item/clothing/mask/monitor - name = "display monitor" + name = "unbranded display monitor" desc = "A rather clunky old CRT-style display screen, fit for mounting on an optical output." flags_inv = HIDEEYES body_parts_covered = SLOT_EYES diff --git a/code/modules/clothing/masks/smokable.dm b/code/modules/clothing/masks/smokable.dm index 01cd25482a79..0cb84f4778a1 100644 --- a/code/modules/clothing/masks/smokable.dm +++ b/code/modules/clothing/masks/smokable.dm @@ -135,9 +135,10 @@ return lit = TRUE atom_damage_type = BURN - if(REAGENT_VOLUME(reagents, /decl/material/liquid/fuel)) // the fuel explodes + var/explosive_power = istype(reagents) && reagents.get_explosive_power() + if(explosive_power > 0) var/datum/effect/effect/system/reagents_explosion/e = new() - e.set_up(round(REAGENT_VOLUME(reagents, /decl/material/liquid/fuel) / 5, 1), get_turf(src), 0, 0) + e.set_up(explosive_power, get_turf(src), 0, 0) e.start() qdel(src) return diff --git a/code/modules/clothing/permits/_permit.dm b/code/modules/clothing/permits/_permit.dm index 1f42fce9e5cf..e3bb8aadda70 100644 --- a/code/modules/clothing/permits/_permit.dm +++ b/code/modules/clothing/permits/_permit.dm @@ -66,11 +66,6 @@ name = "bar shotgun permit" desc = "A card indicating that the owner is allowed to carry a shotgun in the bar." -/obj/item/clothing/permit/gun/planetside - name = "planetside weapon permit" - desc = "A card indicating that the owner is allowed to carry a weapon while on the surface." - detail_color = COLOR_PALE_PINK - /obj/item/clothing/permit/gun/paramedic name = "paramedic weapon permit" desc = "A card indicating that the owner is allowed to carry a weapon while on EVA retrieval missions." @@ -80,7 +75,3 @@ name = "holy weapon permit" desc = "A card indicating that the owner is allowed to carry a weapon for religious rites and purposes." detail_color = COLOR_GRAY15 - -/obj/item/clothing/permit/gun/planetside/exploration - name = "explorer weapon permit" - desc = "A card indicating that the owner is allowed to carry weaponry during active exploration missions." \ No newline at end of file diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index 589a7f0d79e1..8b90dd6119a0 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -17,6 +17,7 @@ fallback_slot = slot_shoes_str _base_attack_force = 5 + var/rock_climbing = FALSE var/can_fit_under_magboots = TRUE var/can_add_cuffs = TRUE var/obj/item/handcuffs/attached_cuffs = null diff --git a/code/modules/clothing/spacesuits/rig/suits/light.dm b/code/modules/clothing/spacesuits/rig/suits/light.dm index d36aa54c14dc..3812b8c92068 100644 --- a/code/modules/clothing/spacesuits/rig/suits/light.dm +++ b/code/modules/clothing/spacesuits/rig/suits/light.dm @@ -64,6 +64,9 @@ /obj/item/rig_module/cooling_unit ) +/obj/item/rig/light/hacker/unlocked + req_access = null + //The cybersuit is not space-proof. It does however, have good siemens_coefficient values /obj/item/clothing/head/lightrig/hacker name = "HUD" @@ -90,3 +93,6 @@ /obj/item/rig_module/stealth_field, /obj/item/rig_module/vision ) + +/obj/item/rig/light/stealth/unlocked + req_access = null diff --git a/code/modules/clothing/spacesuits/rig/suits/merc.dm b/code/modules/clothing/spacesuits/rig/suits/merc.dm index 06c837cbe4a0..e43c91fc8454 100644 --- a/code/modules/clothing/spacesuits/rig/suits/merc.dm +++ b/code/modules/clothing/spacesuits/rig/suits/merc.dm @@ -44,6 +44,9 @@ /obj/item/clothing/suit/space/rig/merc icon = 'icons/clothing/rigs/chests/chest_merc.dmi' +/obj/item/rig/merc/empty/unlocked + req_access = null + //Has most of the modules removed /obj/item/rig/merc/empty initial_modules = list( diff --git a/code/modules/clothing/spacesuits/rig/suits/station.dm b/code/modules/clothing/spacesuits/rig/suits/station.dm index fe48b0e24b0d..609bc791eeb7 100644 --- a/code/modules/clothing/spacesuits/rig/suits/station.dm +++ b/code/modules/clothing/spacesuits/rig/suits/station.dm @@ -64,7 +64,7 @@ /obj/item/tank, /obj/item/suit_cooling_unit, /obj/item/stack/flag, - /obj/item/ore, + /obj/item/ore_satchel, /obj/item/t_scanner, /obj/item/tool, /obj/item/rcd @@ -81,6 +81,9 @@ icon = 'icons/clothing/rigs/gloves/gloves.dmi' siemens_coefficient = 0 +/obj/item/rig/industrial/unlocked + req_access = null + /obj/item/rig/industrial/equipped initial_modules = list( @@ -118,6 +121,9 @@ req_access = list(access_engine_equip) +/obj/item/rig/eva/unlocked + req_access = null + /obj/item/clothing/head/helmet/space/rig/eva camera = /obj/machinery/camera/network/engineering icon = 'icons/clothing/rigs/helmets/helmet_eva.dmi' @@ -165,7 +171,7 @@ /obj/item/flashlight, /obj/item/tank, /obj/item/suit_cooling_unit, - /obj/item/ore, + /obj/item/ore_satchel, /obj/item/toolbox, /obj/item/briefcase/inflatable, /obj/item/inflatable_dispenser, @@ -338,6 +344,9 @@ allowed = list(/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/handcuffs,/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/baton) anomaly_shielding = 1 +/obj/item/rig/hazard/unlocked + req_access = null + /obj/item/clothing/head/helmet/space/rig/hazard camera = /obj/machinery/camera/network/security icon = 'icons/clothing/rigs/helmets/helmet_hazard.dmi' diff --git a/code/modules/clothing/spacesuits/void/station.dm b/code/modules/clothing/spacesuits/void/station.dm index 74a7ace494e3..6f6af449a485 100644 --- a/code/modules/clothing/spacesuits/void/station.dm +++ b/code/modules/clothing/spacesuits/void/station.dm @@ -73,7 +73,7 @@ /obj/item/tank, /obj/item/stack/flag, /obj/item/suit_cooling_unit, - /obj/item/ore, + /obj/item/ore_satchel, /obj/item/t_scanner, /obj/item/tool, /obj/item/rcd diff --git a/code/modules/clothing/suits/_suit_hood.dm b/code/modules/clothing/suits/_suit_hood.dm index 3aaaba4e5f7d..42e02944dd17 100644 --- a/code/modules/clothing/suits/_suit_hood.dm +++ b/code/modules/clothing/suits/_suit_hood.dm @@ -1,6 +1,36 @@ /obj/item/clothing/suit var/obj/item/clothing/head/hood +/obj/item/clothing/suit/Initialize() + if(ispath(hood)) + hood = new hood(src) + hood.paint_color = paint_color + hood.markings_color = markings_color + if(isnull(hood.markings_state_modifier)) + hood.markings_state_modifier = markings_state_modifier + hood.update_icon() + return ..() + +/obj/item/clothing/suit/set_color(new_color) + . = ..() + if(istype(hood)) + hood.set_color(new_color) + +/obj/item/clothing/suit/set_markings_color(new_color) + . = ..() + if(istype(hood)) + hood.set_markings_color(new_color) + +/obj/item/clothing/suit/Destroy() + if(istype(hood)) + QDEL_NULL(hood) + return ..() + +/obj/item/clothing/suit/get_contained_external_atoms() + . = ..() + if(hood && .) + LAZYREMOVE(., hood) + /obj/item/clothing/suit/get_hood() if(istype(hood)) return hood @@ -16,16 +46,6 @@ if(. && istype(hood)) hood.set_color(new_color) -/obj/item/clothing/suit/Initialize() - if(ispath(hood)) - hood = new hood(src) - return ..() - -/obj/item/clothing/suit/Destroy() - if(istype(hood)) - QDEL_NULL(hood) - return ..() - /obj/item/clothing/suit/equipped(mob/user, slot) if(slot != slot_wear_suit_str) remove_hood() diff --git a/code/modules/clothing/suits/jackets/wintercoat.dm b/code/modules/clothing/suits/jackets/wintercoat.dm index b34c74a2d43c..ae70473b1823 100644 --- a/code/modules/clothing/suits/jackets/wintercoat.dm +++ b/code/modules/clothing/suits/jackets/wintercoat.dm @@ -127,3 +127,22 @@ /obj/item/clothing/head/winterhood/mining icon = 'icons/clothing/head/hood_winter_mining.dmi' + +/obj/item/clothing/suit/jacket/winter/parka + name = "parka" + desc = "A heavy fur-lined jacket designed to keep you extra warm in sub-zero conditions." + hood = /obj/item/clothing/head/winterhood/parka + icon = 'icons/clothing/suits/wintercoat/parka.dmi' + markings_color = "#a5975c" + markings_state_modifier = "-lining" + paint_color = COLOR_SILVER + +/obj/item/clothing/head/winterhood/parka + name = "parka hood" + desc = "A heavy fur-lined jacket hood.." + icon = 'icons/clothing/head/hood_parka.dmi' + markings_color = "#a5975c" + markings_state_modifier = "-lining" + +/obj/item/clothing/suit/jacket/winter/parka/purple + paint_color = COLOR_PURPLE diff --git a/code/modules/codex/categories/_materials.dm b/code/modules/codex/categories/_materials.dm index 6509270555d4..cc138ce2dd81 100644 --- a/code/modules/codex/categories/_materials.dm +++ b/code/modules/codex/categories/_materials.dm @@ -22,6 +22,9 @@ var/list/reactant_values = list() for(var/reactant_id in reaction.required_reagents) var/decl/material/reactant = GET_DECL(reactant_id) + if(!istype(reactant)) + log_error("Could not find /decl for [reactant_id], reaction type [reactiontype].") + continue var/reactant_name = "[reactant.name]" reactant_values += "[reaction.required_reagents[reactant_id]]u [reactant_name]" mechanics_text += " [jointext(reactant_values, " + ")]" diff --git a/code/modules/codex/categories/category_cultures.dm b/code/modules/codex/categories/category_cultures.dm index 0aef22c0c0cd..84ccc0d6b841 100644 --- a/code/modules/codex/categories/category_cultures.dm +++ b/code/modules/codex/categories/category_cultures.dm @@ -1,5 +1,5 @@ /decl/codex_category/cultures - name = "Places, Factions and Culture" + name = "Places, Factions, and Culture" desc = "Prominent planets, cultures, factions and religions of known space." /decl/codex_category/cultures/Populate() diff --git a/code/modules/codex/categories/category_fusion_reaction.dm b/code/modules/codex/categories/category_fusion_reaction.dm index ff61c4c392fc..715aee90cc2b 100644 --- a/code/modules/codex/categories/category_fusion_reaction.dm +++ b/code/modules/codex/categories/category_fusion_reaction.dm @@ -10,7 +10,15 @@ continue var/decl/material/p_mat = GET_DECL(reaction.p_react) + if(!istype(p_mat)) + log_error("Could not find /decl instance for [rtype]'s primary reactant [reaction.p_react || "NULL"].") + continue + var/decl/material/s_mat = GET_DECL(reaction.s_react) + if(!istype(s_mat)) + log_error("Could not find /decl instance for [rtype]'s secondary reactant [reaction.s_react || "NULL"].") + continue + var/list/reaction_info = list() reaction_info += "Fusion between [p_mat.name] and [s_mat.name] can be achieved with a plasma temperature of [T0C + reaction.minimum_reaction_temperature] Kelvin or higher." reaction_info += "This reaction consumes [initial(reaction.energy_consumption)] heat unit\s and produces [reaction.energy_production] heat unit\s." diff --git a/code/modules/codex/entries/atmospherics.dm b/code/modules/codex/entries/atmospherics.dm index 4c7e8993f078..6bbd45733cee 100644 --- a/code/modules/codex/entries/atmospherics.dm +++ b/code/modules/codex/entries/atmospherics.dm @@ -54,7 +54,7 @@ //Freezers /datum/codex_entry/atmos_freezer - associated_paths = list(/obj/machinery/atmospherics/unary/freezer) + associated_paths = list(/obj/machinery/atmospherics/unary/temperature/freezer) mechanics_text = "Cools down the gas of the pipe it is connected to. It uses massive amounts of electricity while on. \ It can be upgraded by replacing the capacitors, manipulators, and matter bins. It can be deconstructed by screwing the maintenance panel open with a \ screwdriver, and then using a crowbar." @@ -63,7 +63,7 @@ //Heaters /datum/codex_entry/atmos_heater - associated_paths = list(/obj/machinery/atmospherics/unary/heater) + associated_paths = list(/obj/machinery/atmospherics/unary/temperature/heater) mechanics_text = "Heats up the gas of the pipe it is connected to. It uses massive amounts of electricity while on. \ It can be upgraded by replacing the capacitors, manipulators, and matter bins. It can be deconstructed by screwing the maintenance panel open with a \ screwdriver, and then using a crowbar." diff --git a/code/modules/crafting/working/quern.dm b/code/modules/crafting/working/quern.dm index 3ff1832811c7..aa76aafb4034 100644 --- a/code/modules/crafting/working/quern.dm +++ b/code/modules/crafting/working/quern.dm @@ -15,8 +15,8 @@ var/tmp/possible_transfer_amounts = @"[10,25,50,100,500]" /obj/structure/working/quern/Initialize() - . = ..() atom_flags |= ATOM_FLAG_OPEN_CONTAINER + . = ..() /obj/structure/working/quern/try_start_working(mob/user) diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index 96d91b2e784d..4ffb83499d14 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -194,6 +194,14 @@ return if(!can_ignite()) return + //also copied from matches + var/explosive_power= istype(reagents) && reagents.get_explosive_power() + if(explosive_power > 0) + var/datum/effect/effect/system/reagents_explosion/e = new() + e.set_up(explosive_power, get_turf(src), 0, 0) + e.start() + qdel(src) + return START_PROCESSING(SSobj, src) set_light(2, 1, "#e38f46") _on_fire = TRUE diff --git a/code/modules/economy/cael/EFTPOS.dm b/code/modules/economy/cael/EFTPOS.dm index 519938245c6d..d88132261a96 100644 --- a/code/modules/economy/cael/EFTPOS.dm +++ b/code/modules/economy/cael/EFTPOS.dm @@ -41,7 +41,16 @@ //by default, connect to the station account //the user of the EFTPOS device can change the target account though, and no-one will be the wiser (except whoever's being charged) - linked_account = station_account + linked_account = get_default_account() + +/obj/item/eftpos/proc/get_default_account() + return global.station_account + +/obj/item/eftpos/departmental + var/decl/department/default_department + +/obj/item/eftpos/departmental/get_default_account() + return (default_department && global.department_accounts[default_department]) || ..() /obj/item/eftpos/proc/print_reference() var/obj/item/paper/R = new(src.loc, null, diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm index 39f8dc118d92..b32701c519b4 100644 --- a/code/modules/error_handler/error_handler.dm +++ b/code/modules/error_handler/error_handler.dm @@ -8,6 +8,37 @@ var/global/regex/actual_error_file_line log_world("\[[time_stamp()]] Uncaught exception: [E]") return ..() + //this is snowflake because of a byond bug (ID:2306577), do not attempt to call non-builtin procs in this block OR BEFORE IT + if(copytext(E.name, 1, 32) == "Maximum recursion level reached")//32 == length() of that string + 1 + var/list/proc_path_to_count = list() + var/crashed = FALSE + try + var/callee/stack_entry = caller + while(!isnull(stack_entry)) + proc_path_to_count[stack_entry.proc] += 1 + stack_entry = stack_entry.caller + catch + //union job. avoids crashing the stack again + //I just do not trust this construct to work reliably + crashed = TRUE + + var/list/split = splittext(E.desc, "\n") + for (var/i in 1 to split.len) + if (split[i] != "" || copytext(split[1], 1, 2) != " ") + split[i] = " [split[i]]" + split += "--Stack Info [crashed ? "(Crashed, may be missing info)" : ""]:" + for(var/path in proc_path_to_count) + split += " [path] = [proc_path_to_count[path]]" + E.desc = jointext(split, "\n") + // expanding the first line of log_world to avoid hitting the stack limit again + to_file(world.log, "\[[time2text(station_time_in_ticks, "hh:mm:ss")]] Runtime Error: [E.name]\n[E.desc]") + //log to world while intentionally triggering the byond bug. this does not DO anything, it just errors + //(seemingly because log_info_line is deep enough to hit the raised limit 516.1667 introduced) + log_world("runtime error: [E.name]\n[E.desc]\n[log_info_line(usr)]\n[log_info_line(src)]") + //if we got to here without silently ending, the byond bug has been fixed. + log_world("The \"bug\" with recursion runtimes has been fixed. Please remove the snowflake check from world/Error in [__FILE__]:[__LINE__]") + return //this will never happen. + if (!global.actual_error_file_line) global.actual_error_file_line = regex("^%% (.*?),(.*?) %% ") diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index 5400a5e174cf..fa1cb513fef8 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -48,21 +48,21 @@ //See if shields can stop it first var/overmap_only = TRUE - var/list/overmap_sectors = list() + var/list/event_overmap_sectors = list() if(!length(affecting_z)) return for(var/i in affecting_z) - var/obj/effect/overmap/visitable/sector = global.overmap_sectors[num2text(i)] + var/obj/effect/overmap/visitable/sector = global.overmap_sectors[i] if(istype(sector)) - overmap_sectors |= sector + event_overmap_sectors |= sector else overmap_only = FALSE break var/list/shields = list() if(overmap_only) - for(var/obj/effect/overmap/visitable/sector as anything in overmap_sectors) + for(var/obj/effect/overmap/visitable/sector as anything in event_overmap_sectors) var/list/sector_shields = sector.get_linked_machines_of_type(/obj/machinery/shield_generator) if(length(sector_shields)) shields |= sector_shields diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm index 0686eee43d9a..d0964b37e3a5 100644 --- a/code/modules/events/event.dm +++ b/code/modules/events/event.dm @@ -165,5 +165,5 @@ /datum/event/proc/location_name() if(!length(global.using_map.overmap_ids)) return station_name() - var/obj/effect/overmap/visitable/O = global.overmap_sectors[num2text(pick(affecting_z))] + var/obj/effect/overmap/visitable/O = global.overmap_sectors[pick(affecting_z)] return O?.name || "Unknown Location" diff --git a/code/modules/fabrication/designs/general/designs_arms_ammo.dm b/code/modules/fabrication/designs/general/designs_arms_ammo.dm index eb92c1ef0f3f..2fd82b739386 100644 --- a/code/modules/fabrication/designs/general/designs_arms_ammo.dm +++ b/code/modules/fabrication/designs/general/designs_arms_ammo.dm @@ -81,4 +81,8 @@ /datum/fabricator_recipe/arms_ammo/hidden/speedloader_laser name = "ammunition (speedloader, laserbulb)" - path = /obj/item/ammo_magazine/speedloader/laser_revolver \ No newline at end of file + path = /obj/item/ammo_magazine/speedloader/laser_revolver + +/datum/fabricator_recipe/arms_ammo/hidden/mine_assembly + name = "mine assembly" + path = /obj/item/mine/assembly diff --git a/code/modules/fabrication/designs/general/designs_devices_components.dm b/code/modules/fabrication/designs/general/designs_devices_components.dm index aa961aef563e..dd732867b353 100644 --- a/code/modules/fabrication/designs/general/designs_devices_components.dm +++ b/code/modules/fabrication/designs/general/designs_devices_components.dm @@ -5,9 +5,6 @@ /datum/fabricator_recipe/device_component/keyboard path = /obj/item/stock_parts/keyboard -/datum/fabricator_recipe/device_component/cataloguer - path = /obj/item/cataloguer - /datum/fabricator_recipe/device_component/pda path = /obj/item/modular_computer/pda diff --git a/code/modules/fabrication/designs/textile/storage.dm b/code/modules/fabrication/designs/textile/storage.dm index d8ece5c95cb9..aaf9d2e8fc3d 100644 --- a/code/modules/fabrication/designs/textile/storage.dm +++ b/code/modules/fabrication/designs/textile/storage.dm @@ -15,13 +15,13 @@ path= /obj/item/belt/utility /datum/fabricator_recipe/textiles/storage/mining_satchel - path = /obj/item/ore + path = /obj/item/ore_satchel /datum/fabricator_recipe/textiles/storage/botanical_satchel - path = /obj/item/plants + path = /obj/item/plant_satchel /datum/fabricator_recipe/textiles/storage/wallet path = /obj/item/wallet/leather /datum/fabricator_recipe/textiles/storage/money_bag - path = /obj/item/bag/cash \ No newline at end of file + path = /obj/item/bag/cash \ No newline at end of file diff --git a/code/modules/fluids/_fluid.dm b/code/modules/fluids/_fluid.dm index 8dd93caf8988..8e2449f56fc1 100644 --- a/code/modules/fluids/_fluid.dm +++ b/code/modules/fluids/_fluid.dm @@ -117,7 +117,7 @@ var/global/list/_fluid_edge_mask_cache = list() var/list/connections for(var/checkdir in global.alldirs) var/turf/neighbor = get_step_resolving_mimic(loc, checkdir) - if(!neighbor || neighbor.density || REAGENT_TOTAL_VOLUME(neighbor?.reagents) > FLUID_PUDDLE) + if(!neighbor || neighbor.density || !istype(neighbor?.reagents) || REAGENT_TOTAL_VOLUME(neighbor?.reagents) > FLUID_PUDDLE) LAZYADD(connections, checkdir) else LAZYADD(ignored, checkdir) diff --git a/code/modules/fluids/fluid_flood.dm b/code/modules/fluids/fluid_flood.dm index 47a48f0f5e95..7b3f2a333840 100644 --- a/code/modules/fluids/fluid_flood.dm +++ b/code/modules/fluids/fluid_flood.dm @@ -1,5 +1,7 @@ // Permaflood overlay. var/global/list/flood_type_overlay_cache = list() +// TODO: does this need to also take contaminant type as an argument? flooding contaminants are totally untested +// also, do flooded turfs even apply fluid_act and touch effects? /proc/get_flood_overlay(fluid_type) if(!ispath(fluid_type, /decl/material)) return null diff --git a/code/modules/holomap/holomap.dm b/code/modules/holomap/holomap.dm index 9d5fbc13b445..8e4b1179e841 100644 --- a/code/modules/holomap/holomap.dm +++ b/code/modules/holomap/holomap.dm @@ -232,7 +232,7 @@ //This is where the fun begins if(length(global.using_map.overmap_ids)) - var/obj/effect/overmap/visitable/O = global.overmap_sectors["[z]"] + var/obj/effect/overmap/visitable/O = global.overmap_sectors[z] if(isAI) T = get_turf(user.client.eye) diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index d854110a206f..02ae238c326e 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -319,7 +319,7 @@ user.visible_message(SPAN_NOTICE("\The [user] puts \the [used_item] into \the [src].")) return TRUE - if(istype(used_item, /obj/item/plants) && used_item.storage) + if(istype(used_item, /obj/item/plant_satchel) && used_item.storage) var/loaded = 0 for(var/obj/item/seeds/G in storage.get_contents()) ++loaded diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index f31aa5eb279c..960ad52b5ed4 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -464,7 +464,7 @@ plant_seed(user, used_item) return TRUE - if (istype(used_item, /obj/item/plants)) + if (istype(used_item, /obj/item/plant_satchel)) physical_attack_hand(user) // Harvests and clears out dead plants. if(used_item.storage) for (var/obj/item/food/grown/G in get_turf(user)) diff --git a/code/modules/item_effects/item_effect_charges.dm b/code/modules/item_effects/item_effect_charges.dm index 06e8126b16fe..ff68fdb348eb 100644 --- a/code/modules/item_effects/item_effect_charges.dm +++ b/code/modules/item_effects/item_effect_charges.dm @@ -12,52 +12,6 @@ /decl/item_effect/charges/on_examined(obj/item/item, mob/user) to_chat(user, SPAN_NOTICE("\The [item] has [item.get_item_effect_parameter(src, IE_CAT_RANGED, IE_PAR_USES) || 0] charge\s of [effect_descriptor] left.")) -/obj/item/projectile/fireball - name = "fireball" - icon_state = "fireball" - fire_sound = 'sound/effects/bamf.ogg' - damage = 20 - atom_damage_type = BURN - damage_flags = DAM_DISPERSED // burn all over - var/fire_lifetime = 2 SECONDS - var/fire_temperature = (288 CELSIUS) / 0.9 + 1 // hot enough to ignite wood! divided by 0.9 and plus one to ensure we can light firepits - -/obj/effect/fake_fire/variable - name = "fire" - anchored = TRUE - mouse_opacity = MOUSE_OPACITY_UNCLICKABLE - firelevel = 1 - pressure = ONE_ATMOSPHERE - -/obj/effect/fake_fire/variable/Initialize(ml, new_temperature, new_lifetime) - lifetime = new_lifetime - last_temperature = new_temperature - return ..() - -// we deal our damage via fire_act, not via direct burn damage. our burn damage is specifically for mobs -/obj/item/projectile/fireball/get_structure_damage() - return 0 - -/obj/item/projectile/fireball/on_impact(var/atom/A) - . = ..() - var/obj/effect/fake_fire/fire = new /obj/effect/fake_fire/variable(get_turf(A), fire_temperature, fire_lifetime) - fire.Process() // process at least once! - qdel_self() - -/obj/item/projectile/fireball/after_move() - . = ..() - if(!loc) - return - for(var/mob/living/victim in loc) - if(!victim.simulated) - continue - victim.FireBurn(1, fire_temperature, ONE_ATMOSPHERE) - loc.fire_act(1, fire_temperature, ONE_ATMOSPHERE) - for(var/atom/burned in loc) - if(!burned.simulated || burned == src) - continue - burned.fire_act(1, fire_temperature, ONE_ATMOSPHERE) - // Example effect; casts a fireball n times. /decl/item_effect/charges/fireball effect_descriptor = "fireball" diff --git a/code/modules/lighting/_lighting_defs.dm b/code/modules/lighting/_lighting_defs.dm index f43ef762a6b3..a49d409fba9a 100644 --- a/code/modules/lighting/_lighting_defs.dm +++ b/code/modules/lighting/_lighting_defs.dm @@ -7,27 +7,39 @@ // As such this all gets counted as a single line. // The braces and semicolons are there to be able to do this on a single line. -#define APPLY_CORNER(C,now,Tx,Ty,hdiff) \ - . = LUM_FALLOFF(C.x, C.y, Tx, Ty, hdiff) * light_power; \ - var/OLD = effect_str[C]; \ - effect_str[C] = .; \ - C.update_lumcount \ - ( \ - (. * lum_r) - (OLD * applied_lum_r), \ - (. * lum_g) - (OLD * applied_lum_g), \ - (. * lum_b) - (OLD * applied_lum_b), \ - now \ +#define APPLY_CORNER(C,now,Tx,Ty,hdiff) \ + . = LUM_FALLOFF(C.x, C.y, Tx, Ty, hdiff) * light_power; \ + var/OLD = effect_str[C]; \ + effect_str[C] = .; \ + C.update_lumcount \ + ( \ + (. * lum_r) - (OLD * applied_lum_r), \ + (. * lum_g) - (OLD * applied_lum_g), \ + (. * lum_b) - (OLD * applied_lum_b), \ + now \ + ); + +// Like APPLY_CORNER, but very slightly faster, for the cases where we know there's no previous value. +#define INIT_CORNER(C,now,Tx,Ty,hdiff) \ + . = LUM_FALLOFF(C.x, C.y, Tx, Ty, hdiff) * light_power; \ + effect_str[C] = .; \ + C.update_lumcount \ + ( \ + . * lum_r, \ + . * lum_g, \ + . * lum_b, \ + now \ ); // I don't need to explain what this does, do I? -#define REMOVE_CORNER(C,now) \ - . = -effect_str[C]; \ - C.update_lumcount \ - ( \ - . * applied_lum_r, \ - . * applied_lum_g, \ - . * applied_lum_b, \ - now \ +#define REMOVE_CORNER(C,now) \ + . = -effect_str[C]; \ + C.update_lumcount \ + ( \ + . * applied_lum_r, \ + . * applied_lum_g, \ + . * applied_lum_b, \ + now \ ); // Converts two Z levels into a height value for LUM_FALLOFF or HEIGHT_FALLOFF. @@ -41,3 +53,12 @@ corner_height = LIGHTING_HEIGHT; \ } \ APPLY_CORNER(C, now, Sx, Sy, corner_height); + +#define INIT_CORNER_BY_HEIGHT(now) \ + if (C.z != Sz) { \ + corner_height = CALCULATE_CORNER_HEIGHT(C.z, Sz); \ + } \ + else { \ + corner_height = LIGHTING_HEIGHT; \ + } \ + INIT_CORNER(C, now, Sx, Sy, corner_height); \ No newline at end of file diff --git a/code/modules/lighting/ambient_turf.dm b/code/modules/lighting/ambient_turf.dm index 4b34c863ed90..60992f1fcb5d 100644 --- a/code/modules/lighting/ambient_turf.dm +++ b/code/modules/lighting/ambient_turf.dm @@ -4,9 +4,6 @@ /// The power of the above is multiplied by this. Setting too high may drown out normal lights on the same turf. var/ambient_light_multiplier = 0.3 - /// If this is TRUE, an above turf's ambient light is affecting this turf. - var/tmp/ambient_has_indirect = FALSE - // Record-keeping, do not touch -- that means you, admins. var/tmp/ambient_active = FALSE //! Do we have non-zero ambient light? Use [TURF_IS_AMBIENT_LIT] instead of reading this directly. var/tmp/ambient_light_old_r = 0 diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 639b0a023872..ea6f99e9a915 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -272,8 +272,6 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, // We init before Z-Mimic, cannot rely on above/below. while ((T = GET_BELOW(T)) && ((below.t1?.z_flags | below.t2?.z_flags | below.t3?.z_flags | below.t4?.z_flags) & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) - T.ambient_has_indirect = TRUE - if (!T.corners || !T.corners[Ti]) T.generate_missing_corners() diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 5ebc210383ea..84bccc897573 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -225,7 +225,7 @@ REMOVE_CORNER(C,now) effect_str[C] = 0 - var/actual_range = light_range + var/actual_range = (light_angle && facing_opaque) ? light_range * LIGHTING_BLOCKED_FACTOR : light_range var/Sx = pixel_turf.x var/Sy = pixel_turf.y @@ -335,7 +335,6 @@ var/list/datum/lighting_corner/corners = list() var/list/turf/turfs = list() - var/thing var/datum/lighting_corner/C var/turf/T var/list/Tcorners @@ -358,8 +357,9 @@ if ((DETERMINANT(limit_a_x, limit_a_y, test_x, test_y) > 0) || DETERMINANT(test_x, test_y, limit_b_x, limit_b_y) > 0) continue - if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) || T.light_source_solo || T.light_source_multi) - Tcorners = T.corners + Tcorners = T.corners + // These checks are inlined from generate_missing_corners. They must be kept in sync. + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) || T.light_source_solo || T.light_source_multi || (T.z_flags & ZM_ALLOW_LIGHTING)) if (!T.lighting_corners_initialised) T.lighting_corners_initialised = TRUE @@ -373,11 +373,11 @@ Tcorners[i] = new /datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[i], i) - if (!T.has_opaque_atom) - for (var/v in 1 to 4) - var/val = Tcorners[v] - if (val) - corners[val] = 0 + if (Tcorners && !T.has_opaque_atom) + for (var/v in 1 to 4) + var/val = Tcorners[v] + if (val) + corners[val] = 0 turfs += T @@ -390,39 +390,34 @@ var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them. affecting_turfs += L - for (thing in L) - T = thing + for (T as anything in L) LAZYADD(T.affecting_lights, src) L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights. affecting_turfs -= L - for (thing in L) - T = thing + for (T as anything in L) LAZYREMOVE(T.affecting_lights, src) LAZYINITLIST(effect_str) if (needs_update == LIGHTING_VIS_UPDATE) - for (thing in corners - effect_str) - C = thing + for (C as anything in corners - effect_str) // newly added corners LAZYADD(C.affecting, src) if (!C.active) effect_str[C] = 0 continue - APPLY_CORNER_BY_HEIGHT(now) + INIT_CORNER_BY_HEIGHT(now) else L = corners - effect_str - for (thing in L) - C = thing + for (C as anything in L) LAZYADD(C.affecting, src) if (!C.active) effect_str[C] = 0 continue - APPLY_CORNER_BY_HEIGHT(now) + INIT_CORNER_BY_HEIGHT(now) - for (thing in corners - L) - C = thing + for (C as anything in corners - L) if (!C.active) effect_str[C] = 0 continue @@ -430,8 +425,7 @@ APPLY_CORNER_BY_HEIGHT(now) L = effect_str - corners - for (thing in L) - C = thing + for (C as anything in L) REMOVE_CORNER(C, now) LAZYREMOVE(C.affecting, src) diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 08d7fadca5c6..5543d1166c1b 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -143,7 +143,8 @@ // This is inlined in lighting_source.dm. // Update it too if you change this. /turf/proc/generate_missing_corners() - if (!TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && !light_source_solo && !light_source_multi && !(z_flags & ZM_ALLOW_LIGHTING) && !ambient_light && !ambient_has_indirect) + // If a turf is dynamically lit, has a light source, or mimics lighting, it needs to have corners created. + if (!TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && !light_source_solo && !light_source_multi && !(z_flags & ZM_ALLOW_LIGHTING)) return lighting_corners_initialised = TRUE diff --git a/code/modules/maps/_map_template_unit_testing.dm b/code/modules/maps/_map_template_unit_testing.dm index 8b27d9c69267..13e82ac10b78 100644 --- a/code/modules/maps/_map_template_unit_testing.dm +++ b/code/modules/maps/_map_template_unit_testing.dm @@ -1,7 +1,8 @@ /datum/map_template - var/const/NO_APC = 1 - var/const/NO_VENT = 2 - var/const/NO_SCRUBBER = 4 + var/const/NO_APC = BITFLAG(0) + var/const/NO_VENT = BITFLAG(1) + var/const/NO_SCRUBBER = BITFLAG(2) + var/const/SKIP_ALL_TESTS = BITFLAG(3) var/list/area_usage_test_exempted_areas = list() var/list/area_usage_test_exempted_root_areas = list() diff --git a/code/modules/maps/reader.dm b/code/modules/maps/reader.dm index 71bedba36393..9f7dff608187 100644 --- a/code/modules/maps/reader.dm +++ b/code/modules/maps/reader.dm @@ -10,6 +10,7 @@ var/global/dmm_suite/preloader/_preloader = new /datum/map_load_metadata var/bounds var/list/atoms_to_initialise + var/list/turfs_to_mark_modified /dmm_suite // /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"\n*([a-zA-Z\n]*)\n?"\}/g @@ -64,12 +65,19 @@ var/global/dmm_suite/preloader/_preloader = new initialized_areas_by_type = initialized_areas_by_type || list() if(!(world.area in initialized_areas_by_type)) initialized_areas_by_type[world.area] = locate(world.area) - . = load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, clear_contents, lower_crop_x, upper_crop_x, lower_crop_y, upper_crop_y, initialized_areas_by_type, level_data_type) + + var/datum/map_load_metadata/M = load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, clear_contents, lower_crop_x, upper_crop_x, lower_crop_y, upper_crop_y, initialized_areas_by_type, level_data_type) + + if(length(M.turfs_to_mark_modified)) + for(var/turf/turf in M.turfs_to_mark_modified) + turf.state_was_modified() + #ifdef TESTING if(turfsSkipped) testing("Skipped loading [turfsSkipped] default turfs") #endif Master.StopLoadingMap() + return M /dmm_suite/proc/load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, clear_contents, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, initialized_areas_by_type, level_data_type = /datum/level_data/space) var/tfile = dmm_file//the map file we're creating @@ -91,6 +99,7 @@ var/global/dmm_suite/preloader/_preloader = new var/list/atoms_to_initialise = list() var/list/atoms_to_delete = list() + var/list/turfs_to_mark_modified = list() while(dmmRegex.Find(tfile, stored_index)) stored_index = dmmRegex.next @@ -180,7 +189,11 @@ var/global/dmm_suite/preloader/_preloader = new throw EXCEPTION("Undefined model key in DMM.") var/datum/grid_load_metadata/M = parse_grid(grid_models[model_key], model_key, xcrd, ycrd, zcrd, no_changeturf || zexpansion, clear_contents, initialized_areas_by_type) if (M) - atoms_to_initialise += M.atoms_to_initialise + if(length(M.atoms_to_initialise)) + atoms_to_initialise += M.atoms_to_initialise + if(length(M.turfs_to_mark_modified)) + turfs_to_mark_modified += M.turfs_to_mark_modified + atoms_to_delete += M.atoms_to_delete #ifdef TESTING else @@ -205,6 +218,7 @@ var/global/dmm_suite/preloader/_preloader = new var/datum/map_load_metadata/M = new M.bounds = bounds M.atoms_to_initialise = atoms_to_initialise + M.turfs_to_mark_modified = turfs_to_mark_modified return M /** @@ -228,6 +242,7 @@ var/global/dmm_suite/preloader/_preloader = new /datum/grid_load_metadata var/list/atoms_to_initialise var/list/atoms_to_delete + var/list/turfs_to_mark_modified /dmm_suite/proc/parse_grid(model as text, model_key as text, xcrd as num,ycrd as num,zcrd as num, no_changeturf as num, clear_contents as num, initialized_areas_by_type) /*Method parse_grid() @@ -345,23 +360,28 @@ var/global/dmm_suite/preloader/_preloader = new SSatoms.map_loader_begin() //since we've switched off autoinitialisation, record atoms to initialise later var/list/atoms_to_initialise = list() + var/list/turfs_to_mark_modified = list() //instanciate the first /turf var/turf/T if(members[first_turf_index] != /turf/template_noop) is_not_noop = TRUE - T = instance_atom(members[first_turf_index],members_attributes[first_turf_index],crds,no_changeturf) + T = instance_atom(members[first_turf_index], members_attributes[first_turf_index], crds, no_changeturf) atoms_to_initialise += T + if(isturf(T)) + turfs_to_mark_modified += T if(T) //if others /turf are presents, simulates the underlays piling effect index = first_turf_index + 1 while(index < length(members)) // Last item is an /area var/underlay = T.appearance - T = instance_atom(members[index],members_attributes[index],crds,no_changeturf)//instance new turf + T = instance_atom(members[index], members_attributes[index], crds, no_changeturf)//instance new turf T.underlays += underlay index++ atoms_to_initialise += T + if(isturf(T)) + turfs_to_mark_modified += T if (clear_contents && is_not_noop && length(crds.contents)) for (var/atom/movable/pre_existing as anything in crds) @@ -373,13 +393,18 @@ var/global/dmm_suite/preloader/_preloader = new //finally instance all remainings objects/mobs for(index in 1 to first_turf_index-1) - atoms_to_initialise += instance_atom(members[index],members_attributes[index],crds,no_changeturf) + var/atom/thing = instance_atom(members[index], members_attributes[index], crds, no_changeturf) + atoms_to_initialise += thing + if(isturf(thing)) + turfs_to_mark_modified += thing + //Restore initialization to the previous valsue SSatoms.map_loader_stop() var/datum/grid_load_metadata/M = new M.atoms_to_initialise = atoms_to_initialise M.atoms_to_delete = atoms_to_delete + M.turfs_to_mark_modified = turfs_to_mark_modified return M //////////////// @@ -387,12 +412,15 @@ var/global/dmm_suite/preloader/_preloader = new //////////////// //Instance an atom at (x,y,z) and gives it the variables in attributes -/dmm_suite/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf) +/dmm_suite/proc/instance_atom(path, list/attributes, turf/crds, no_changeturf) global._preloader.setup(attributes, path) if(crds) - if(!no_changeturf && ispath(path, /turf)) - . = crds.ChangeTurf(path, FALSE, TRUE) + if(ispath(path, /turf)) + if(no_changeturf) + . = new path(crds) + else + . = crds.ChangeTurf(path, FALSE, TRUE) else . = new path(crds)// preloader called from atom/New diff --git a/code/modules/maps/template_types/random_exoplanet/planet_themes/robotic_guardians.dm b/code/modules/maps/template_types/random_exoplanet/planet_themes/robotic_guardians.dm index 3e8c60a5d1a7..d9d6608eac8a 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_themes/robotic_guardians.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_themes/robotic_guardians.dm @@ -2,7 +2,7 @@ name = "Robotic Guardians" var/list/guardian_types = list( /mob/living/simple_animal/hostile/hivebot, - /mob/living/simple_animal/hostile/hivebot/range, + /mob/living/simple_animal/hostile/hivebot/ranged, /mob/living/simple_animal/hostile/viscerator/hive ) var/list/mega_guardian_types = list( @@ -24,4 +24,4 @@ A.faction = "Ancient Guardian" /datum/exoplanet_theme/robotic_guardians/get_sensor_data() - return "Movement without corresponding lifesigns detected on the surface." \ No newline at end of file + return "Movement without corresponding lifesigns detected on the surface." diff --git a/code/modules/maps/template_types/random_exoplanet/planet_themes/ruined_city.dm b/code/modules/maps/template_types/random_exoplanet/planet_themes/ruined_city.dm index 221ee935055c..869920b3417f 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_themes/ruined_city.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_themes/ruined_city.dm @@ -123,6 +123,9 @@ floor_type = null material = /decl/material/solid/stone/concrete +/turf/wall/concrete/reinforced + reinf_material = /decl/material/solid/metal/steel + //Generic ruin /datum/random_map/maze/concrete wall_type = /turf/wall/concrete diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 920e63862dc2..ada5e9a86c73 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -255,7 +255,8 @@ var/global/list/materials_by_gas_symbol = list() var/burn_temperature = 100 CELSIUS var/burn_product var/list/vapor_products // If splashed, releases these gasses in these proportions. // TODO add to unit test after solvent PR is merged - + /// A divisor applied to volume when calculating explosive force (lower is stronger) - if null, reagent is no explosive + var/explosive_power_divisor var/scent //refer to _scent.dm var/scent_intensity = /decl/scent_intensity/normal var/scent_descriptor = "smell" diff --git a/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm b/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm index f60b1158b6d6..e8e943197826 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_chemistry.dm @@ -30,6 +30,7 @@ lore_text = "Lubricant is a substance introduced between two moving surfaces to reduce the friction and wear between them. giggity." taste_description = "slime" color = SYNTH_BLOOD_COLOR + opacity = 1.0 // liquid default is 0.5, we want oil to be fully opaque so the footsteps are too value = 0.1 slipperiness = 80 exoplanet_rarity_gas = MAT_RARITY_EXOTIC diff --git a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm index 0d6b27a9c82b..366f04608344 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm @@ -227,6 +227,7 @@ taste_mult = 1.2 metabolism = REM * 0.25 exoplanet_rarity_gas = MAT_RARITY_NOWHERE + opacity = 1.0 /decl/material/liquid/hair_remover name = "hair remover" diff --git a/code/modules/materials/definitions/solids/materials_solid_stone.dm b/code/modules/materials/definitions/solids/materials_solid_stone.dm index 3bb5692d7769..d9a0ad5866d2 100644 --- a/code/modules/materials/definitions/solids/materials_solid_stone.dm +++ b/code/modules/materials/definitions/solids/materials_solid_stone.dm @@ -44,6 +44,15 @@ melting_point = T0C + 600 hardness = MAT_VALUE_RIGID + 5 +/decl/material/solid/stone/limestone + name = "limestone" + uid = "solid_limestone" + lore_text = "A pale sedimentary rock, often containing fossils. The cost of boosting it to orbit is almost universally much higher than the actual value of the material." + color = COLOR_OFF_WHITE + value = 1.5 + melting_point = T0C + 600 + hardness = MAT_VALUE_RIGID + 5 + /decl/material/solid/stone/flint name = "flint" uid = "solid_flint" diff --git a/code/modules/materials/material_debris_fragment.dm b/code/modules/materials/material_debris_fragment.dm new file mode 100644 index 000000000000..d34e944b6dc3 --- /dev/null +++ b/code/modules/materials/material_debris_fragment.dm @@ -0,0 +1,47 @@ +/obj/item/debris/salvage + abstract_type = /obj/item/debris/salvage + icon_state = ICON_STATE_WORLD + material_alteration = MAT_FLAG_ALTERATION_NONE + is_spawnable_type = TRUE + +/obj/item/debris/salvage/metal + name = "fragment" + desc = "A large, ragged chunk of some worked material." + icon = 'icons/obj/debris_metal.dmi' + material_alteration = MAT_FLAG_ALTERATION_ALL + material = /decl/material/solid/metal/steel + +/obj/item/debris/salvage/metal/Initialize(ml, material_key) + . = ..() + icon_state = "[icon_state][rand(0, 4)]" + +/obj/item/debris/salvage/metal/plasteel + material = /decl/material/solid/metal/plasteel + +/obj/item/debris/salvage/circuit + name = "broken circuit" + desc = "A burned-out circuitboard. Only good for the base materials now." + icon = 'icons/obj/debris_circuit.dmi' + material = /decl/material/solid/fiberglass + matter = list( + /decl/material/solid/organic/plastic = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/metal/gold = MATTER_AMOUNT_TRACE + ) + +/obj/item/debris/salvage/circuit/Initialize(ml, material_key) + . = ..() + icon_state = "[icon_state][rand(0, 3)]" + +/obj/item/debris/salvage/device + name = "broken device" + desc = "A destroyed device of some kind. Only good for recycling now." + icon = 'icons/obj/debris_device.dmi' + material = /decl/material/solid/metal/aluminium + matter = list( + /decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/metal/gold = MATTER_AMOUNT_TRACE + ) + +/obj/item/debris/salvage/device/Initialize(ml, material_key) + . = ..() + icon_state = "[icon_state][rand(0, 3)]" diff --git a/code/modules/materials/stack_types/material_stack_lump.dm b/code/modules/materials/stack_types/material_stack_lump.dm index 75f373f981b5..a4c0e5957bda 100644 --- a/code/modules/materials/stack_types/material_stack_lump.dm +++ b/code/modules/materials/stack_types/material_stack_lump.dm @@ -35,6 +35,12 @@ crafting_stack_type = /obj/item/stack/material/lump/large matter_multiplier = 3 +/obj/item/stack/material/lump/large/marble + material = /decl/material/solid/stone/marble + +/obj/item/stack/material/lump/large/marble/five + amount = 5 + /obj/item/stack/material/lump/large/clay material = /decl/material/solid/clay is_spawnable_type = TRUE diff --git a/code/modules/materials/stack_types/material_stack_misc.dm b/code/modules/materials/stack_types/material_stack_misc.dm index afd7fbfe51ab..64017b9fdd6e 100644 --- a/code/modules/materials/stack_types/material_stack_misc.dm +++ b/code/modules/materials/stack_types/material_stack_misc.dm @@ -82,6 +82,16 @@ crafting_stack_type = /obj/item/stack/material/puck can_be_pulverized = TRUE +/obj/item/stack/material/crystal + name = "crystal" + singular_name = "crystal" + plural_name = "crystals" + icon_state = "sheet-phoron" + plural_icon_state = "sheet-phoron-mult" + max_icon_state = "sheet-phoron-max" + stack_merge_type = /obj/item/stack/material/crystal + can_be_pulverized = TRUE + /obj/item/stack/material/segment name = "segments" singular_name = "segment" diff --git a/code/modules/materials/stack_types/material_stack_ore.dm b/code/modules/materials/stack_types/material_stack_ore.dm index 4ac278611253..391166a8bb14 100644 --- a/code/modules/materials/stack_types/material_stack_ore.dm +++ b/code/modules/materials/stack_types/material_stack_ore.dm @@ -120,30 +120,56 @@ // Map definitions. /obj/item/stack/material/ore/uranium material = /decl/material/solid/pitchblende +/obj/item/stack/material/ore/uranium/three + amount = 3 /obj/item/stack/material/ore/iron material = /decl/material/solid/hematite +/obj/item/stack/material/ore/iron/ten + amount = 10 /obj/item/stack/material/ore/coal material = /decl/material/solid/graphite +/obj/item/stack/material/ore/coal/ten + amount = 10 /obj/item/stack/material/ore/silver material = /decl/material/solid/metal/silver +/obj/item/stack/material/ore/silver/five + amount = 5 /obj/item/stack/material/ore/gold material = /decl/material/solid/metal/gold +/obj/item/stack/material/ore/gold/five + amount = 5 /obj/item/stack/material/ore/diamond material = /decl/material/solid/gemstone/diamond +/obj/item/stack/material/ore/diamond/three + amount = 3 /obj/item/stack/material/ore/osmium material = /decl/material/solid/metal/platinum +/obj/item/stack/material/ore/osmium/three + amount = 3 /obj/item/stack/material/ore/hydrogen material = /decl/material/solid/metallic_hydrogen +/obj/item/stack/material/ore/hydrogen/two + amount = 2 /obj/item/stack/material/ore/slag material = /decl/material/solid/slag /obj/item/stack/material/ore/phosphorite material = /decl/material/solid/phosphorite -/obj/item/stack/material/ore/aluminium +/obj/item/stack/material/ore/bauxite material = /decl/material/solid/bauxite +/obj/item/stack/material/ore/bauxite/ten + amount = 10 /obj/item/stack/material/ore/rutile material = /decl/material/solid/rutile +/obj/item/stack/material/ore/rutile/five + amount = 5 /obj/item/stack/material/ore/galena material = /decl/material/solid/galena +/obj/item/stack/material/ore/galena/ten + amount = 10 +/obj/item/stack/material/ore/tetrahedrite + material = /decl/material/solid/tetrahedrite +/obj/item/stack/material/ore/tetrahedrite/ten + amount = 10 /obj/item/stack/material/ore/hydrogen_hydrate material = /decl/material/solid/ice/hydrogen // todo: set back to hydrate when clathrate is added to hydrogen hydrate dname /obj/item/stack/material/ore/methane @@ -182,6 +208,9 @@ /obj/item/stack/material/ore/handful/sand material = /decl/material/solid/sand +/obj/item/stack/material/ore/handful/sand/fifteen + amount = 15 + /client/proc/spawn_ore_pile() set name = "Spawn Ore Pile" set category = "Debug" diff --git a/code/modules/mechs/equipment/medical.dm b/code/modules/mechs/equipment/medical.dm index 373af3a552e2..f18d56ea2e0e 100644 --- a/code/modules/mechs/equipment/medical.dm +++ b/code/modules/mechs/equipment/medical.dm @@ -56,6 +56,9 @@ stasis_power = 0 interact_offline = TRUE stat_immune = NOPOWER + // Spawned inside a mech component, not built as a machine. + construct_state = null + base_type = /obj/machinery/sleeper/mounted /obj/machinery/sleeper/mounted/standard/Initialize(mapload, d, populate_parts) . = ..() diff --git a/code/modules/mining/machinery/material_unloader.dm b/code/modules/mining/machinery/material_unloader.dm index 6450fa1d8ba7..420596abfc9b 100644 --- a/code/modules/mining/machinery/material_unloader.dm +++ b/code/modules/mining/machinery/material_unloader.dm @@ -13,13 +13,14 @@ if(!output_turf || !input_turf) return - + if(length(output_turf.contents) >= MAX_UNLOAD_TURF_CONTENTS) return var/ore_this_tick = 0 for(var/obj/structure/ore_box/unloading in input_turf) for(var/obj/item/stack/material/ore in unloading) + unloading.remove_ore(ore) ore.dropInto(output_turf) ore_this_tick++ if(ore_this_tick >= MAX_UNLOAD_ORE_PER_TICK || length(output_turf.contents) >= MAX_UNLOAD_TURF_CONTENTS) diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index bd70d153e5e8..fbad7d1b2351 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -16,7 +16,7 @@ /obj/item/clothing/gloves/thick, /obj/item/clothing/shoes/color/black, /obj/item/scanner/gas, - /obj/item/ore, + /obj/item/ore_satchel, /obj/item/flashlight/lantern, /obj/item/tool/shovel, /obj/item/tool/pickaxe, diff --git a/code/modules/mining/ore_box.dm b/code/modules/mining/ore_box.dm index ab20aa117d11..cc4cb28c8c6d 100644 --- a/code/modules/mining/ore_box.dm +++ b/code/modules/mining/ore_box.dm @@ -127,6 +127,8 @@ return . += "It holds:" for(var/ore in stored_ore) + if(stored_ore[ore] == 0) + continue . += "- [stored_ore[ore]] [ore]" /obj/structure/ore_box/explosion_act(severity) diff --git a/code/modules/mob/grab/grab_object.dm b/code/modules/mob/grab/grab_object.dm index f4861ede315f..c4a5015aa4d5 100644 --- a/code/modules/mob/grab/grab_object.dm +++ b/code/modules/mob/grab/grab_object.dm @@ -77,6 +77,10 @@ if(affecting_mob && assailant?.check_intent(I_FLAG_HARM)) upgrade(TRUE) +/obj/item/grab/ShouldSerialize(_age) + SHOULD_CALL_PARENT(FALSE) + return FALSE + /obj/item/grab/mob_can_unequip(mob/user, slot, disable_warning = FALSE, dropping = FALSE) if(dropping) return TRUE diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index b306780544b9..a565d1e1aae0 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -61,6 +61,14 @@ else prop.dropInto(loc) return TRUE + if(slot == slot_in_wallet_str) + remove_from_mob(prop) + var/obj/item/wallet = get_equipped_item(slot_wear_id_str) + if(wallet) + prop.forceMove(wallet) + else + prop.dropInto(loc) + return TRUE // Attempt to equip accessories if the slot is already blocked. if(!delete_old_item && get_equipped_item(slot)) @@ -126,10 +134,16 @@ /mob/proc/equip_to_storage(obj/item/newitem) // Try put it in their backpack var/obj/item/back = get_equipped_item(slot_back_str) - if(back?.storage?.can_be_inserted(newitem, null, 1)) + if(back?.storage?.can_be_inserted(newitem, null, TRUE)) back.storage.handle_item_insertion(src, newitem) return back + // Or in their wallet + var/obj/item/wallet = get_equipped_item(slot_wear_id_str) + if(wallet?.storage?.can_be_inserted(newitem, null, TRUE)) + wallet.storage.handle_item_insertion(src, newitem) + return wallet + // Try to place it in any item that can store stuff, on the mob. for(var/obj/item/thing in contents) if(thing?.storage?.can_be_inserted(newitem, null, 1)) diff --git a/mods/species/ascent/datum/descriptors.dm b/code/modules/mob/living/human/descriptors/descriptors_body_length.dm similarity index 100% rename from mods/species/ascent/datum/descriptors.dm rename to code/modules/mob/living/human/descriptors/descriptors_body_length.dm diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index a296f4413444..00c520947984 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -9,7 +9,10 @@ /mob/living/human/Initialize(mapload, species_uid, datum/mob_snapshot/supplied_appearance) + // Health is dynamically calculated from organ state, so no point keeping a + // serialized or modified value, it will be recalculated almost immediately. current_health = get_max_health() + species_uid ||= species // Pass our current species in as an arg (in case of serde) reset_hud_overlays() var/list/newargs = args.Copy(2) setup_human(arglist(newargs)) @@ -611,7 +614,7 @@ for(var/obj/item/organ/external/E in get_external_organs()) E.sanitize_sprite_accessories() - for(var/acc_cat in root_bodytype.default_sprite_accessories) + for(var/acc_cat in root_bodytype?.default_sprite_accessories) var/decl/sprite_accessory_category/acc_cat_decl = GET_DECL(acc_cat) if(!acc_cat_decl.always_apply_defaults) continue @@ -1146,3 +1149,6 @@ full_prosthetic = robolimb_count > 0 && (robolimb_count == LAZYLEN(limbs)) //If no organs, no way to tell return full_prosthetic +// Don't tag your crewmates please. +/mob/living/human/is_tagging_suitable() + return FALSE diff --git a/code/modules/mob/living/human/human_blood.dm b/code/modules/mob/living/human/human_blood.dm index 7c0e00da1270..56a25031101d 100644 --- a/code/modules/mob/living/human/human_blood.dm +++ b/code/modules/mob/living/human/human_blood.dm @@ -185,8 +185,6 @@ //Percentage of maximum blood volume, affected by the condition of circulation organs /mob/living/human/proc/get_blood_circulation() - - var/obj/item/organ/internal/heart/heart = get_organ(BP_HEART, /obj/item/organ/internal/heart) if(!heart) return 0.25 * get_blood_volume() @@ -210,8 +208,7 @@ if(PULSE_2FAST, PULSE_THREADY) pulse_mod *= 1.25 blood_volume *= pulse_mod - if(current_posture.prone) - blood_volume *= 1.25 + blood_volume *= current_posture.blood_volume_multiplier var/min_efficiency = recent_pump ? 0.5 : 0.3 blood_volume *= max(min_efficiency, (1-(heart.get_organ_damage() / heart.max_damage))) @@ -236,7 +233,8 @@ else blood_volume = 100 - var/blood_volume_mod = max(0, 1 - getOxyLossPercent()/(species.total_health/2)) + // blood_volume_mod is 1 with no oxyloss, 0 at half species health (50%), and cannot go below 0 + var/blood_volume_mod = max(0, (1 - getOxyLossFraction()*2)) var/oxygenated_mult = 0 switch(GET_CHEMICAL_EFFECT(src, CE_OXYGENATED)) if(1) @@ -245,6 +243,6 @@ oxygenated_mult = 0.7 if(3) oxygenated_mult = 0.9 - blood_volume_mod = blood_volume_mod + oxygenated_mult - (blood_volume_mod * oxygenated_mult) - blood_volume = blood_volume * blood_volume_mod - return min(blood_volume, 100) + blood_volume_mod += oxygenated_mult * (1 - blood_volume_mod) // give us back a fraction of our missing oxygenation + blood_volume *= blood_volume_mod + return clamp(blood_volume, 0, 100) diff --git a/code/modules/mob/living/human/human_damage.dm b/code/modules/mob/living/human/human_damage.dm index d1d64b56f8a7..76378ffb374a 100644 --- a/code/modules/mob/living/human/human_damage.dm +++ b/code/modules/mob/living/human/human_damage.dm @@ -130,7 +130,10 @@ ..() /mob/living/human/proc/getOxyLossPercent() - return (get_damage(OXY) / species.total_health) * 100 + return getOxyLossFraction() * 100 + +/mob/living/human/proc/getOxyLossFraction() + return (get_damage(OXY) / species.total_health) /mob/living/human/getOxyLoss() if(need_breathe()) diff --git a/code/modules/mob/living/inventory.dm b/code/modules/mob/living/inventory.dm index 4d3d35090dc6..c4cc217a30e2 100644 --- a/code/modules/mob/living/inventory.dm +++ b/code/modules/mob/living/inventory.dm @@ -14,10 +14,17 @@ var/list/all_slots = list() for(var/slot in get_inventory_slots()) all_slots += get_inventory_slot_datum(slot) + var/list/low_priority_slots // Slots that will always be added at the end, in the order of their parent slots' priority. + // This is sort of due to technical limitations but mostly due to laziness. for(var/datum/inventory_slot/inv_slot as anything in sortTim(all_slots, /proc/cmp_inventory_slot_desc)) + if(LAZYLEN(inv_slot.additional_quick_equip_slots)) + for(var/extra_slot in inv_slot.additional_quick_equip_slots) + LAZYADD(low_priority_slots, extra_slot) if(isnull(inv_slot.quick_equip_priority)) // Never quick-equip into some slots. continue _inventory_slot_priority += inv_slot.slot_id + if(low_priority_slots) + _inventory_slot_priority += low_priority_slots return _inventory_slot_priority /mob/living/get_inventory_slot_datum(var/slot) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index ff9a8613746d..52d3b884092a 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -244,10 +244,14 @@ break if(still_processing_reagent) continue - var/dose = CHEM_DOSE(src, reagent) - reagent.metabolism*2 - LAZYSET(_chem_doses, reagent, dose) - if(CHEM_DOSE(src, reagent) <= 0) + var/amount_removed = get_adjusted_metabolism(reagent.metabolism*2) // reagents metabolize out twice as fast as they metabolize in + if(!(reagent.flags & IGNORE_MOB_SIZE)) + amount_removed *= (MOB_SIZE_MEDIUM/mob_size) + var/dose = CHEM_DOSE(src, reagent) - amount_removed + if(dose <= 0) LAZYREMOVE(_chem_doses, reagent) + else + LAZYSET(_chem_doses, reagent, dose) if(apply_chemical_effects()) update_health() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 956ae903000c..24bb4de3f4b9 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1,6 +1,8 @@ /mob/living/Initialize() - current_health = get_max_health() + if(isnull(current_health) || current_health == INFINITY) + current_health = get_max_health() + original_fingerprint_seed = sequential_id(/mob) fingerprint = md5(num2text(original_fingerprint_seed)) original_genetic_seed = sequential_id(/mob) @@ -2001,3 +2003,17 @@ default behaviour is: /mob/living/is_cloaked() return has_mob_modifier(/decl/mob_modifier/cloaked) + +/mob/living/proc/apply_random_mutation(radiation_amount) + set_unique_enzymes(num2text(random_id(/mob, 1000000, 9999999))) + if(prob(98)) + add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/disability))) + else + add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/superpower))) + if(radiation_amount) + apply_damage(radiation_amount, IRRADIATE, armor_pen = 100) + +// Used by specimen taggers to avoid tagging/overwriting players or named mobs like Runtime. +/mob/living/proc/is_tagging_suitable() + return !key && !client + diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index a870b6c2c263..e94c963c06fd 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -28,7 +28,7 @@ visible_message(SPAN_WARNING("[src] triggers their deadman's switch!")) signaler.signal() //Armor - var/damage = P.damage + var/damage = P.get_projectile_damage(src) var/flags = P.damage_flags() var/damaged if(!P.nodamage) @@ -42,7 +42,8 @@ // For visuals and blood splatters etc /mob/living/proc/bullet_impact_visuals(var/obj/item/projectile/P, var/def_zone, var/damage) - var/list/impact_sounds = LAZYACCESS(P.impact_sounds, get_bullet_impact_effect_type(def_zone)) + var/list/all_impact_sounds = P.get_impact_sounds() + var/list/impact_sounds = LAZYACCESS(all_impact_sounds, get_bullet_impact_effect_type(def_zone)) if(length(impact_sounds)) playsound(src, pick(impact_sounds), 75) if(get_bullet_impact_effect_type(def_zone) != BULLET_IMPACT_MEAT) diff --git a/code/modules/mob/living/living_tail.dm b/code/modules/mob/living/living_tail.dm index a18bd6148c4e..31761055946b 100644 --- a/code/modules/mob/living/living_tail.dm +++ b/code/modules/mob/living/living_tail.dm @@ -64,16 +64,14 @@ // These values may be null and are generally optional. var/hair_colour = GET_HAIR_COLOR(src) - var/tail_hair = tail_organ.get_tail_hair() var/tail_blend = tail_organ.get_tail_blend() - var/tail_hair_blend = tail_organ.get_tail_hair_blend() var/list/tail_colors = tail_organ.get_tail_metadata() if(!islist(tail_colors) || !length(tail_colors)) return var/tail_color = LAZYACCESS(tail_colors, SAM_COLOR) var/tail_inner_color = LAZYACCESS(tail_colors, SAM_COLOR_INNER) - var/icon_key = "[tail_state][tail_icon][tail_blend][tail_color][tail_inner_color][tail_hair][tail_hair_blend][hair_colour]" + var/icon_key = "[tail_state][tail_icon][tail_blend][tail_color][tail_inner_color][hair_colour]" var/icon/blended_tail_icon = global.tail_icon_cache[icon_key] if(!blended_tail_icon) @@ -89,14 +87,6 @@ inner_tail.Blend(tail_inner_color, tail_blend) blended_tail_icon.Blend(inner_tail, ICON_OVERLAY) - // The following will not work with animated tails. - if(tail_hair) - var/tail_hair_state = "[tail_state]_[tail_hair]" - if(check_state_in_icon(tail_hair_state, tail_icon)) - var/icon/hair_icon = icon(tail_icon, tail_hair_state) - if(hair_colour && !isnull(tail_hair_blend)) // 0 is a valid blend mode - hair_icon.Blend(hair_colour, tail_hair_blend) - blended_tail_icon.Blend(hair_icon, ICON_OVERLAY) global.tail_icon_cache[icon_key] = blended_tail_icon return blended_tail_icon diff --git a/code/modules/mob/living/silicon/robot/flying/module_flying_cultivator.dm b/code/modules/mob/living/silicon/robot/flying/module_flying_cultivator.dm index 9d2b5f06f826..a8773d85d500 100644 --- a/code/modules/mob/living/silicon/robot/flying/module_flying_cultivator.dm +++ b/code/modules/mob/living/silicon/robot/flying/module_flying_cultivator.dm @@ -8,7 +8,7 @@ module_sprites = list("Drone" = 'icons/mob/robots/flying/flying_hydro.dmi') equipment = list( - /obj/item/plants, + /obj/item/plant_satchel, /obj/item/wirecutters/clippers, /obj/item/tool/hoe/mini/unbreakable, /obj/item/tool/axe/hatchet/unbreakable, diff --git a/code/modules/mob/living/silicon/robot/modules/module_clerical.dm b/code/modules/mob/living/silicon/robot/modules/module_clerical.dm index e09c3548c825..0e76a2737495 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_clerical.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_clerical.dm @@ -31,7 +31,7 @@ /obj/item/tool/hoe/mini, /obj/item/tool/axe/hatchet, /obj/item/scanner/plant, - /obj/item/plants, + /obj/item/plant_satchel, /obj/item/robot_harvester, /obj/item/rollingpin, /obj/item/knife/kitchen, diff --git a/code/modules/mob/living/silicon/robot/modules/module_miner.dm b/code/modules/mob/living/silicon/robot/modules/module_miner.dm index d1e76232d72f..5aaaf39486ab 100644 --- a/code/modules/mob/living/silicon/robot/modules/module_miner.dm +++ b/code/modules/mob/living/silicon/robot/modules/module_miner.dm @@ -24,7 +24,7 @@ /obj/item/borg/sight/meson, /obj/item/wrench, /obj/item/screwdriver, - /obj/item/ore, + /obj/item/ore_satchel, /obj/item/tool/drill/advanced, /obj/item/sheetsnatcher/borg, /obj/item/gripper/miner, diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index bcc2945bf461..e60938603389 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -700,7 +700,7 @@ /mob/living/silicon/robot/Move(a, b, flag) . = ..() if(. && module && isturf(loc)) - var/obj/item/ore/orebag = locate() in get_held_items() + var/obj/item/stack/material/ore/orebag = locate() in get_held_items() if(orebag) loc.attackby(orebag, src) module.handle_turf(loc, src) diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index c8da4e16f144..de977e45c639 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -193,7 +193,7 @@ name = "personal shielding" desc = "A powerful experimental module that turns aside or absorbs incoming attacks at the cost of charge." icon = 'icons/obj/signs/warnings.dmi' - icon_state = "shock" + icon_state = "shock-large" var/shield_level = 0.5 //Percentage of damage absorbed by the shield. /obj/item/borg/combat/shield/verb/set_shield_level() @@ -209,7 +209,7 @@ name = "mobility module" desc = "By retracting limbs and tucking in its head, a combat android can roll at high speeds." icon = 'icons/obj/signs/warnings.dmi' - icon_state = "shock" + icon_state = "shock-large" /obj/item/inflatable_dispenser name = "inflatables dispenser" diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 36f1ba370c4c..b5508d6d2217 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -127,9 +127,9 @@ if(!Proj.nodamage) switch(Proj.atom_damage_type) if(BRUTE) - take_damage(Proj.damage) + take_damage(Proj.get_projectile_damage(src)) if(BURN) - take_damage(Proj.damage, BURN) + take_damage(Proj.get_projectile_damage(src), BURN) Proj.on_hit(src,100) //wow this is a terrible hack return 100 diff --git a/code/modules/mob/living/simple_animal/_simple_animal.dm b/code/modules/mob/living/simple_animal/_simple_animal.dm index ab53e5630e3f..7b62adcc2ed0 100644 --- a/code/modules/mob/living/simple_animal/_simple_animal.dm +++ b/code/modules/mob/living/simple_animal/_simple_animal.dm @@ -1,4 +1,3 @@ - /mob/living/simple_animal name = "animal" max_health = 20 @@ -71,9 +70,6 @@ var/bleed_colour = COLOR_BLOOD_HUMAN var/can_bleed = TRUE - // contained in a cage - var/in_stasis = 0 - //for simple animals with abilities, mostly megafauna var/ability_cooldown @@ -114,6 +110,11 @@ /mob/living/simple_animal/Initialize() . = ..() + // Deserialize any JSON payload for our overlays. + if(istext(draw_visible_overlays)) + draw_visible_overlays = cached_json_decode(draw_visible_overlays) + if(!islist(draw_visible_overlays)) + draw_visible_overlays = null if(isnull(draw_visible_overlays)) var/list/defaults = get_default_animal_colours() draw_visible_overlays = defaults?.Copy() // do not mutate static list diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 99e38a6baa01..cfaaab05eb04 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -194,6 +194,9 @@ butchery_data = /decl/butchery_data/animal/cat/black holder_type = /obj/item/holder/runtime +/mob/living/simple_animal/passive/cat/fluff/runtime/is_tagging_suitable() + return FALSE + /obj/item/holder/runtime origin_tech = @'{"programming":1,"biotech":1}' diff --git a/code/modules/mob/living/simple_animal/friendly/possum.dm b/code/modules/mob/living/simple_animal/friendly/possum.dm index ef8c318ade27..9b77858189e3 100644 --- a/code/modules/mob/living/simple_animal/friendly/possum.dm +++ b/code/modules/mob/living/simple_animal/friendly/possum.dm @@ -106,6 +106,9 @@ can_buckle = TRUE var/aaa_words = list("delaminat", "meteor", "fire", "breach") +/mob/living/simple_animal/opossum/poppy/is_tagging_suitable() + return FALSE + /mob/living/simple_animal/opossum/poppy/hear_broadcast(decl/language/language, mob/speaker, speaker_name, message) . = ..() addtimer(CALLBACK(src, PROC_REF(check_keywords), message), rand(1 SECOND, 3 SECONDS)) diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm index d958928874ef..6566f241e2f0 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm @@ -1,11 +1,9 @@ /mob/living/simple_animal/hostile/hivebot name = "hivebot" desc = "A junky looking robot with four spiky legs." - icon = 'icons/mob/simple_animal/hivebot.dmi' + icon = 'icons/mob/simple_animal/hivebots/hivebot_green.dmi' max_health = 55 natural_weapon = /obj/item/natural_weapon/drone_slicer - projectilesound = 'sound/weapons/gunshot/gunshot_pistol.ogg' - projectiletype = /obj/item/projectile/beam/smalllaser faction = "hivebot" min_gas = null max_gas = null diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm index ead786ab340c..4dec711741ca 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm @@ -5,7 +5,7 @@ /mob/living/simple_animal/hostile/hivebot/mega name = "hivemind" desc = "A huge quadruped robot equipped with a myriad of weaponry." - icon = 'icons/mob/simple_animal/megabot.dmi' + icon = 'icons/mob/simple_animal/hivebots/megabot.dmi' max_health = 440 natural_weapon = /obj/item/natural_weapon/circular_saw natural_armor = list( diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/melee/_melee.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/melee/_melee.dm new file mode 100644 index 000000000000..42d3f588fbec --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/melee/_melee.dm @@ -0,0 +1,20 @@ +/mob/living/simple_animal/hostile/hivebot/melee + natural_weapon = /obj/item/natural_weapon/drone_slicer/prod + projectiletype = null // To force the AI to melee. + base_movement_delay = 10 + +/obj/item/natural_weapon/drone_slicer/prod + name = "hivebot prod" + attack_verb = list("prodded") + hitsound = 'sound/weapons/Egloves.ogg' + +/mob/living/simple_animal/hostile/hivebot/melee/has_ranged_attack() + return FALSE + +// This one is tanky by having a massive amount of health. +/mob/living/simple_animal/hostile/hivebot/melee/meatshield + name = "bulky hivebot" + desc = "A large robot." + max_health = 300 + icon_scale_x = 1.1 + icon_scale_y = 1.1 diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/melee/armored.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/melee/armored.dm new file mode 100644 index 000000000000..6556736ec9c0 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/melee/armored.dm @@ -0,0 +1,61 @@ +// This one is tanky by having armor. +/mob/living/simple_animal/hostile/hivebot/melee/armored + name = "armored hivebot" + desc = "A robot clad in heavy armor." + icon = 'icons/mob/simple_animal/hivebots/hivebot_yellow.dmi' + max_health = 150 + icon_scale_x = 1.1 + icon_scale_y = 1.1 + natural_armor = list( + (ARMOR_MELEE) = ARMOR_MELEE_MAJOR, + (ARMOR_BULLET) = ARMOR_BALLISTIC_PISTOL, + (ARMOR_LASER) = ARMOR_LASER_HANDGUNS, + (ARMOR_ENERGY) = ARMOR_ENERGY_RESISTANT, + (ARMOR_BOMB) = ARMOR_BOMB_PADDED, + (ARMOR_BIO) = ARMOR_BIO_SHIELDED, + (ARMOR_RAD) = ARMOR_RAD_SHIELDED + ) + +/mob/living/simple_animal/hostile/hivebot/melee/armored/anti_melee + name = "riot hivebot" + desc = "A robot specialized in close quarters combat." + natural_armor = list( + (ARMOR_MELEE) = ARMOR_MELEE_VERY_HIGH, + (ARMOR_BIO) = ARMOR_BIO_SHIELDED, + (ARMOR_RAD) = ARMOR_RAD_SHIELDED + ) + +/mob/living/simple_animal/hostile/hivebot/melee/armored/anti_bullet + name = "bulletproof hivebot" + desc = "A robot specialized in ballistic defense." + natural_armor = list( + (ARMOR_BULLET) = ARMOR_BALLISTIC_RIFLE, + (ARMOR_BIO) = ARMOR_BIO_SHIELDED, + (ARMOR_RAD) = ARMOR_RAD_SHIELDED + ) + +/mob/living/simple_animal/hostile/hivebot/melee/armored/anti_laser + name = "ablative hivebot" + desc = "A robot specialized in photonic defense." + natural_armor = list( + (ARMOR_LASER) = ARMOR_LASER_RIFLES, + (ARMOR_BIO) = ARMOR_BIO_SHIELDED, + (ARMOR_RAD) = ARMOR_RAD_SHIELDED + ) + var/reflect_chance = 40 + +// Ablative Hivebots can reflect lasers just like humans. +/mob/living/simple_animal/hostile/hivebot/melee/armored/anti_laser/bullet_act(obj/item/projectile/P) + if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) + var/reflect_prob = reflect_chance - round(P.damage/3) + if(prob(reflect_prob)) + visible_message( + SPAN_DANGER("\The [P] is reflected by \the [src]'s armor!"), + SPAN_DANGER("\The [P] gets reflected by \the [src]'s armor!") + ) + if(P.starting) + var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + P.redirect(new_x, new_y, get_turf(src), src) + return PROJECTILE_CONTINUE + return (..(P)) diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/range.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/range.dm deleted file mode 100644 index 3d1ecb678aab..000000000000 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/range.dm +++ /dev/null @@ -1,6 +0,0 @@ -/mob/living/simple_animal/hostile/hivebot/range - desc = "A junky looking robot with four spiky legs. It's equipped with some kind of small-bore gun." - base_movement_delay = 2 - -/mob/living/simple_animal/hostile/hivebot/range/has_ranged_attack() - return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/ranged/_ranged.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/ranged/_ranged.dm new file mode 100644 index 000000000000..800b7b0fe90f --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/ranged/_ranged.dm @@ -0,0 +1,29 @@ +/mob/living/simple_animal/hostile/hivebot/ranged + desc = "A junky looking robot with four spiky legs. It's equipped with some kind of small-bore gun." + base_movement_delay = 2 + icon = 'icons/mob/simple_animal/hivebots/hivebot_white.dmi' + projectiletype = /obj/item/projectile/bullet/pellet + projectilesound = 'sound/weapons/gunshot/gunshot_pistol.ogg' + +/mob/living/simple_animal/hostile/hivebot/ranged/has_ranged_attack() + return TRUE + +/mob/living/simple_animal/hostile/hivebot/ranged/rapid + name = "rapid hivebot" + desc = "A robot with a crude but deadly integrated rifle." + attack_delay = 5 // Two attacks a second or so. + burst_projectile = TRUE + +/mob/living/simple_animal/hostile/hivebot/ranged/laser + name = "laser hivebot" + desc = "A robot with a photonic weapon integrated into itself." + projectiletype = /obj/item/projectile/beam/blue + projectilesound = 'sound/weapons/Laser.ogg' + +/mob/living/simple_animal/hostile/hivebot/ranged/heat + name = "ember hivebot" + desc = "A robot that appears to utilize fire to cook their enemies." + icon_state = "red" + icon = 'icons/mob/simple_animal/hivebots/hivebot_red.dmi' + projectiletype = /obj/item/projectile/fireball + projectilesound = 'sound/effects/bamf.ogg' diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/rapid.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/rapid.dm deleted file mode 100644 index 6cdc6ffbd322..000000000000 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/rapid.dm +++ /dev/null @@ -1,5 +0,0 @@ -/mob/living/simple_animal/hostile/hivebot/rapid - burst_projectile = TRUE - -/mob/living/simple_animal/hostile/hivebot/rapid/has_ranged_attack() - return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/strong.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/strong.dm deleted file mode 100644 index 4105339c6bd1..000000000000 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/strong.dm +++ /dev/null @@ -1,13 +0,0 @@ -/mob/living/simple_animal/hostile/hivebot/strong - desc = "A junky looking robot with four spiky legs - this one has thick armour plating." - max_health = 120 - natural_armor = list( - ARMOR_MELEE = ARMOR_MELEE_RESISTANT - ) - ai = /datum/mob_controller/aggressive/hivebot_strong - -/datum/mob_controller/aggressive/hivebot_strong - can_escape_buckles = TRUE - -/mob/living/simple_animal/hostile/hivebot/strong/has_ranged_attack() - return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index 3e450fd82b2c..97345aeccd78 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -4,7 +4,6 @@ name = "combat drone" desc = "An automated combat drone armed with state of the art weaponry and shielding." icon = 'icons/mob/simple_animal/drone_combat.dmi' - burst_projectile = 0 max_health = 300 move_intents = list( /decl/move_intent/walk/animal_slow, diff --git a/code/modules/mob/living/simple_animal/passive/deer.dm b/code/modules/mob/living/simple_animal/passive/deer.dm index c9590c54714d..cdb5d4895cbe 100644 --- a/code/modules/mob/living/simple_animal/passive/deer.dm +++ b/code/modules/mob/living/simple_animal/passive/deer.dm @@ -96,10 +96,10 @@ desc = "A fleet-footed forest animal known for a love of vtubers." /mob/living/simple_animal/passive/deer/sparkle/Initialize() - draw_visible_overlays = list( + draw_visible_overlays ||= list( "base" = get_random_colour(), "markings" = get_random_colour(TRUE), "socks" = get_random_colour() ) - eye_color = get_random_colour(TRUE) + eye_color ||= get_random_colour(TRUE) . = ..() diff --git a/code/modules/mob/living/simple_animal/passive/fox.dm b/code/modules/mob/living/simple_animal/passive/fox.dm index 09567e7abf19..d8f8ad225271 100644 --- a/code/modules/mob/living/simple_animal/passive/fox.dm +++ b/code/modules/mob/living/simple_animal/passive/fox.dm @@ -81,11 +81,11 @@ desc = "A cunning and graceful predatory mammal, known for being really into hardstyle." /mob/living/simple_animal/passive/fox/sparkle/Initialize() - draw_visible_overlays = list( + draw_visible_overlays ||= list( "base" = get_random_colour(), "markings" = get_random_colour(TRUE), "socks" = get_random_colour() ) - eye_color = get_random_colour(TRUE) + eye_color ||= get_random_colour(TRUE) . = ..() diff --git a/code/modules/mob/living/simple_animal/passive/mouse.dm b/code/modules/mob/living/simple_animal/passive/mouse.dm index 72d3815596e2..5139242d27ed 100644 --- a/code/modules/mob/living/simple_animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_animal/passive/mouse.dm @@ -117,6 +117,9 @@ SetName(initial(name)) real_name = name +/mob/living/simple_animal/passive/mouse/brown/Tom/is_tagging_suitable() + return FALSE + // rats, they're the rats (from Polaris) /mob/living/simple_animal/passive/mouse/rat name = "rat" diff --git a/code/modules/mob/living/simple_animal/passive/rabbit.dm b/code/modules/mob/living/simple_animal/passive/rabbit.dm index 4fe5386ebc36..32676d4ab40d 100644 --- a/code/modules/mob/living/simple_animal/passive/rabbit.dm +++ b/code/modules/mob/living/simple_animal/passive/rabbit.dm @@ -60,10 +60,10 @@ desc = "A hopping mammal with long ears and a love for raves." /mob/living/simple_animal/passive/rabbit/sparkle/Initialize() - draw_visible_overlays = list( + draw_visible_overlays ||= list( "base" = get_random_colour(), "markings" = get_random_colour(TRUE), "socks" = get_random_colour() ) - eye_color = get_random_colour(TRUE) + eye_color ||= get_random_colour(TRUE) . = ..() diff --git a/code/modules/mob/living/simple_animal/passive/wolf.dm b/code/modules/mob/living/simple_animal/passive/wolf.dm index aae33697bfb8..cbe5c004b259 100644 --- a/code/modules/mob/living/simple_animal/passive/wolf.dm +++ b/code/modules/mob/living/simple_animal/passive/wolf.dm @@ -35,10 +35,10 @@ desc = "A predatory canine commonly known to watch speedruns and take party drugs." /mob/living/simple_animal/passive/wolf/sparkle/Initialize() - draw_visible_overlays = list( + draw_visible_overlays ||= list( "base" = get_random_colour(), "markings" = get_random_colour(TRUE), "socks" = get_random_colour() ) - eye_color = get_random_colour(TRUE) + eye_color ||= get_random_colour(TRUE) . = ..() diff --git a/code/modules/mob/living/simple_animal/simple_animal_serde.dm b/code/modules/mob/living/simple_animal/simple_animal_serde.dm new file mode 100644 index 000000000000..377ffd04b6ce --- /dev/null +++ b/code/modules/mob/living/simple_animal/simple_animal_serde.dm @@ -0,0 +1,34 @@ +// Very basic serde for simple animals for things like the Shaded Hills submap. +/mob/living/simple_animal/ShouldSerialize(_age) + return simulated + +/mob/living/simple_animal/GetPossiblySerializableInstances() + return list(src) + +/mob/living/simple_animal/Serialize() + . = ..() + + SERIALIZE_IF_MODIFIED(name, /mob/living/simple_animal) + SERIALIZE_IF_MODIFIED(desc, /mob/living/simple_animal) + SERIALIZE_IF_MODIFIED(icon_state, /mob/living/simple_animal) + + SERIALIZE_IF_MODIFIED(purge, /mob/living/simple_animal) + SERIALIZE_IF_MODIFIED(eye_color, /mob/living/simple_animal) + SERIALIZE_IF_MODIFIED(brute_damage, /mob/living/simple_animal) + SERIALIZE_IF_MODIFIED(burn_damage, /mob/living/simple_animal) + SERIALIZE_IF_MODIFIED(gene_damage, /mob/living/simple_animal) + + var/list/defaults = get_default_animal_colours() + var/changed_from_defaults = length(defaults) != length(draw_visible_overlays) + if(!changed_from_defaults && islist(draw_visible_overlays)) + for(var/animal_color in defaults) + if(!(animal_color in draw_visible_overlays) || defaults[animal_color] != draw_visible_overlays[animal_color]) + changed_from_defaults = TRUE + break + if(!changed_from_defaults) + for(var/animal_color in draw_visible_overlays) + if(!(animal_color in defaults) || defaults[animal_color] != draw_visible_overlays[animal_color]) + changed_from_defaults = TRUE + break + if(changed_from_defaults) + SERIALIZE_VALUE(draw_visible_overlays, /mob/living/simple_animal, json_encode(draw_visible_overlays)) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index dadb2de1b99d..286b28d1689a 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -713,7 +713,7 @@ var/global/const/ACTION_DANGER_ALL = 2 return TRUE if(!anchored && istype(over, /obj/vehicle/train)) var/obj/vehicle/train/beep = over - if(!beep.load(src)) + if(!beep.load_onto_vehicle(src)) to_chat(user, SPAN_WARNING("You were unable to load \the [src] onto \the [over].")) return TRUE . = ..() @@ -1603,4 +1603,3 @@ var/global/const/ACTION_DANGER_ALL = 2 // Returns true if the mob is cloaked, otherwise false /mob/proc/is_cloaked() return FALSE - diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 27ae2d426d93..360f26b75557 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -5,6 +5,16 @@ if(DoMove(direction, src) & MOVEMENT_HANDLED) return TRUE // Doesn't necessarily mean the atom physically moved +/mob/living/SelfMove(var/direction) + // If on walk intent, don't willingly step into hazardous tiles. + // Unless the walker is confused. + var/turf/destination = get_step(src, direction) + if(istype(destination) && MOVING_DELIBERATELY(src) && !HAS_STATUS(src, STAT_CONFUSE)) + if(!destination.is_safe_to_enter(src)) + to_chat(src, SPAN_WARNING("\The [destination] is dangerous to move into.")) + return FALSE // In case any code wants to know if movement happened. + return ..() // Parent call should make the mob move. + /mob/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) . = current_posture.prone || ..() || !mover.density diff --git a/code/modules/mob/mob_serde.dm b/code/modules/mob/mob_serde.dm new file mode 100644 index 000000000000..d6bb36e8c970 --- /dev/null +++ b/code/modules/mob/mob_serde.dm @@ -0,0 +1,8 @@ +// Prevent all mob serde for the time being. +// Equipment handling and the like needs a lot of work to implement. +/mob/ShouldSerialize(_age) + SHOULD_CALL_PARENT(FALSE) + return FALSE + +/mob/GetPossiblySerializableInstances() + return null diff --git a/code/modules/mob/mob_snapshot.dm b/code/modules/mob/mob_snapshot.dm index 01398160314a..c876a47857c6 100644 --- a/code/modules/mob/mob_snapshot.dm +++ b/code/modules/mob/mob_snapshot.dm @@ -19,16 +19,19 @@ /datum/mob_snapshot/New(mob/living/donor, genetic_info_only = FALSE) - real_name = donor?.real_name || "unknown" - eye_color = donor?.get_eye_colour() || COLOR_BLACK - blood_type = donor?.get_blood_type() - unique_enzymes = donor?.get_unique_enzymes() - skin_color = donor?.get_skin_colour() - skin_tone = donor?.get_skin_tone() - fingerprint = donor?.get_full_print(ignore_blockers = TRUE) + if(!istype(donor)) + return - root_species = donor?.get_species() || decls_repository.get_decl_by_id(global.using_map.default_species) - root_bodytype = donor?.get_bodytype() || root_species.default_bodytype + real_name = donor.real_name || "unknown" + eye_color = donor.get_eye_colour() || COLOR_BLACK + blood_type = donor.get_blood_type() + unique_enzymes = donor.get_unique_enzymes() + skin_color = donor.get_skin_colour() + skin_tone = donor.get_skin_tone() + fingerprint = donor.get_full_print(ignore_blockers = TRUE) + + root_species = donor.get_species() || decls_repository.get_decl_by_id(global.using_map.default_species) + root_bodytype = donor.get_bodytype() || root_species.default_bodytype for(var/obj/item/organ/external/limb in donor?.get_external_organs()) // Discard anything not relating to our core/original bodytype and species. @@ -113,4 +116,5 @@ return TRUE /mob/proc/get_mob_snapshot(check_dna = FALSE) + RETURN_TYPE(/datum/mob_snapshot) return (!check_dna || has_genetic_information()) ? new /datum/mob_snapshot(src, genetic_info_only = check_dna) : null diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 77ef427f3e49..0b7f6c091e9f 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -54,7 +54,7 @@ INITIALIZE_IMMEDIATE(/mob/new_player) output += "" if(!panel) - panel = new(src, "Welcome","Welcome to [global.using_map.full_name]", 560, 280, src) + panel = new(src, "Welcome","Welcome to [global.using_map.full_name]", lobby_handler.browser_width, lobby_handler.browser_height, src) panel.set_window_options("can_close=0") panel.set_content(JOINTEXT(output)) panel.open() diff --git a/code/modules/mob_modifiers/definitions/modifiers_nanoswarm.dm b/code/modules/mob_modifiers/definitions/modifiers_nanoswarm.dm deleted file mode 100644 index ddb6d6023eae..000000000000 --- a/code/modules/mob_modifiers/definitions/modifiers_nanoswarm.dm +++ /dev/null @@ -1,34 +0,0 @@ -//This handy augment protects you to a degree, keeping it online after critical damage however is bad -/decl/mob_modifier/nanoswarm - name = "Defensive Nanoswarm" - desc = "You are surrounded by nanomachines that harden in response to projectiles." - hud_icon_state = "nanomachines" - on_add_message_1p = SPAN_NOTICE("Your skin tingles as the nanites spread over your body.") - on_end_message_1p = SPAN_WARNING("The nanites dissolve!") - -/decl/mob_modifier/nanoswarm/on_modifier_datum_added(mob/living/_owner, decl/mob_modifier/modifier) - . = ..() - playsound(_owner.loc,'sound/weapons/flash.ogg',35,1) - -/decl/mob_modifier/nanoswarm/check_modifiers_block_attack(mob/living/_owner, list/modifiers, attack_type, atom/movable/attacker, additional_data) - if(attack_type != MM_ATTACK_TYPE_PROJECTILE) - return ..() - - var/obj/item/organ/internal/augment/active/nanounit/unit - for(var/datum/mob_modifier/modifier in modifiers) - var/obj/item/organ/internal/augment/active/nanounit/implant = modifier.source?.resolve() - if(istype(implant) && implant.active && implant.charges >= 0) // active with 0 charges means it's about to critically fail. - unit = implant - break - - if(!istype(unit)) - return ..() - - _owner.visible_message(SPAN_WARNING("The nanomachines harden as a response to physical trauma!")) - playsound(_owner, 'sound/effects/basscannon.ogg',35,1) - unit.charges-- - if(unit.charges <= 0) - to_chat(_owner, SPAN_DANGER("Warning: Critical damage threshold passed. Shut down unit to avoid further damage.")) - else - unit.catastrophic_failure() - return MM_ATTACK_RESULT_BLOCKED|MM_ATTACK_RESULT_DEFLECTED diff --git a/code/modules/modular_computers/computers/subtypes/preset_console.dm b/code/modules/modular_computers/computers/subtypes/preset_console.dm index 1bc6fd711de0..8450e7d86a2e 100644 --- a/code/modules/modular_computers/computers/subtypes/preset_console.dm +++ b/code/modules/modular_computers/computers/subtypes/preset_console.dm @@ -161,17 +161,6 @@ ) autorun_program = /datum/computer_file/program/supply -/obj/machinery/computer/modular/preset/full/ert - default_software = list( - /datum/computer_file/program/camera_monitor/ert, - /datum/computer_file/program/email_client, - /datum/computer_file/program/alarm_monitor, - /datum/computer_file/program/comm, - /datum/computer_file/program/aidiag, - /datum/computer_file/program/records, - /datum/computer_file/program/wordprocessor - ) - /obj/machinery/computer/modular/preset/full/merc default_software = list( /datum/computer_file/program/camera_monitor/hacked, diff --git a/code/modules/modular_computers/computers/subtypes/preset_pda.dm b/code/modules/modular_computers/computers/subtypes/preset_pda.dm index 0783c94d1fc6..1ad6d0aceb08 100644 --- a/code/modules/modular_computers/computers/subtypes/preset_pda.dm +++ b/code/modules/modular_computers/computers/subtypes/preset_pda.dm @@ -28,13 +28,6 @@ "stripe" = COLOR_RED, ) -/obj/item/modular_computer/pda/ert - color = COLOR_OFF_WHITE - decals = list( - "stripe" = COLOR_DARK_BLUE_GRAY, - "stripe2" = COLOR_GOLD - ) - /obj/item/modular_computer/pda/heads color = COLOR_NAVY_BLUE diff --git a/code/modules/modular_computers/file_system/programs/generic/camera.dm b/code/modules/modular_computers/file_system/programs/generic/camera.dm index 6f167e4682b3..b38d86878e23 100644 --- a/code/modules/modular_computers/file_system/programs/generic/camera.dm +++ b/code/modules/modular_computers/file_system/programs/generic/camera.dm @@ -26,14 +26,6 @@ ) return forbidden_channels -/datum/nano_module/program/camera_monitor/ert - name = "ERT Camera Monitoring program" - -/datum/nano_module/program/camera_monitor/ert/get_forbidden_channels() - var/static/list/forbidden_channels = list( - (CAMERA_CHANNEL_MERCENARY) - ) - /datum/nano_module/program/camera_monitor/mercenary name = "Mercenary Camera Monitoring program" @@ -230,32 +222,6 @@ reset_current() return viewflag -// ERT Variant of the program -/datum/computer_file/program/camera_monitor/ert - filename = "ntcammon" - filedesc = "Advanced Camera Monitoring" - extended_desc = "This program allows remote access to a camera system. This version has an integrated database with additional encrypted keys." - size = 14 - nanomodule_path = /datum/nano_module/program/camera_monitor/ert - available_on_network = 0 - -/datum/nano_module/program/camera_monitor/ert - name = "Advanced Camera Monitoring Program" - available_to_ai = FALSE - - bypass_access = TRUE - -// ERT program ignores network connection requirement. -/datum/nano_module/program/camera_monitor/ert/can_connect_to_camera(datum/extension/network_device/camera/camera_device) - if(!camera_device) - return FALSE - if(!camera_device.is_functional()) - return FALSE - return TRUE - -/datum/nano_module/program/camera_monitor/ert/get_cameras_by_channel() - return camera_repository.get_devices_by_channel() - /datum/nano_module/program/camera_monitor/apply_visual(mob/M) if(current_camera) var/datum/camera_device = current_camera.resolve() diff --git a/code/modules/modular_computers/networking/device_types/_network_device.dm b/code/modules/modular_computers/networking/device_types/_network_device.dm index 292b7c9b0c87..b6167cf74191 100644 --- a/code/modules/modular_computers/networking/device_types/_network_device.dm +++ b/code/modules/modular_computers/networking/device_types/_network_device.dm @@ -219,7 +219,7 @@ // Overmap isn't used, a modem alone provides internet connection. if(!length(global.using_map.overmap_ids)) return TRUE - var/obj/effect/overmap/visitable/sector = global.overmap_sectors[num2text(get_z(holder))] + var/obj/effect/overmap/visitable/sector = global.overmap_sectors[get_z(holder)] if(!istype(sector)) return return sector.has_internet_connection(connecting_network) diff --git a/code/modules/modular_computers/networking/machinery/telecomms.dm b/code/modules/modular_computers/networking/machinery/telecomms.dm index 00d213dd49de..7afe7441668f 100644 --- a/code/modules/modular_computers/networking/machinery/telecomms.dm +++ b/code/modules/modular_computers/networking/machinery/telecomms.dm @@ -154,7 +154,7 @@ var/global/list/telecomms_hubs = list() if(!send_overmap_object) var/turf/T = get_turf(src) - send_overmap_object = istype(T) && global.overmap_sectors["[T.z]"] + send_overmap_object = istype(T) && global.overmap_sectors[T.z] if(channel.secured) encryption |= channel.secured @@ -179,7 +179,7 @@ var/global/list/telecomms_hubs = list() // If we're sending from an overmap object AND our overmap object transmits its identity AND it's different than the listener's // then append the overmap object name to it, so they know where we're from - var/listener_overmap_object = istype(speaking_from) && global.overmap_sectors[num2text(speaking_from.z)] + var/listener_overmap_object = istype(speaking_from) && global.overmap_sectors[speaking_from.z] var/send_overmap = send_overmap_object && send_overmap_object.ident_transmitter && send_overmap_object != listener_overmap_object for(var/mob/listener as anything in radio.get_radio_listeners()) listeners[listener] = send_overmap @@ -203,7 +203,7 @@ var/global/list/telecomms_hubs = list() // Find the z-chunks we can chain the transmission to, which is our local chunk plus any overmap sites in range, // assuming we have a functioning comms maser and the overmap site has a telecomms hub and comms antenna. var/list/levels = SSmapping.get_connected_levels(z) - var/obj/effect/overmap/O = global.overmap_sectors["[z]"] + var/obj/effect/overmap/O = global.overmap_sectors[z] for(var/obj/machinery/shipcomms/broadcaster/our_maser in O?.comms_masers) levels |= our_maser.get_available_z_levels() diff --git a/code/modules/modular_computers/networking/network_cable.dm b/code/modules/modular_computers/networking/network_cable.dm index 9522183087de..4ea853d32197 100644 --- a/code/modules/modular_computers/networking/network_cable.dm +++ b/code/modules/modular_computers/networking/network_cable.dm @@ -156,7 +156,8 @@ randpixel = 2 amount = 20 max_amount = 20 - singular_name = "length" + singular_name = "length of network cable" + plural_name = "lengths of network cable" w_class = ITEM_SIZE_NORMAL throw_speed = 2 throw_range = 5 diff --git a/code/modules/multiz/level_data.dm b/code/modules/multiz/level_data.dm index 389f3e24923b..e9d994279e32 100644 --- a/code/modules/multiz/level_data.dm +++ b/code/modules/multiz/level_data.dm @@ -158,7 +158,11 @@ ///This is set to prevent spamming the log when a turf has tried to grab our strata before we've been initialized var/tmp/_has_warned_uninitialized_strata = FALSE - VAR_PROTECTED/UT_turf_exceptions_by_door_type // An associate list of door types/list of allowed turfs + /// An associate list of door types/list of allowed turfs + VAR_PROTECTED/UT_turf_exceptions_by_door_type = list( + /obj/machinery/door/firedoor = list(/turf/open), + /obj/machinery/door/firedoor/border = list(/turf/open), + ) ///Determines if edge turfs should be centered on the map dimensions. var/origin_is_world_center = TRUE /// If not null, this level will register with a daycycle id/type on New(). @@ -170,9 +174,6 @@ /// Note that this is more or less unnecessary if you are using a mapped area that doesn't stretch to the edge of the level. var/template_edge_padding = 15 - // Whether or not this level permits things like graffiti and filth to persist across rounds. - var/permit_persistence = FALSE - // Submap loading values, passed back via getters like get_subtemplate_budget(). /// A point budget to spend on subtemplates (see template costs) var/subtemplate_budget = 0 @@ -181,6 +182,9 @@ /// A specific area to use when determining where to place subtemplates. var/subtemplate_area = null + /// Set to TRUE if this level persistence data, FALSE if not. Check is only done if _has_serde_data is null. + VAR_PRIVATE/_has_serde_data = null + /datum/level_data/New(var/_z_level, var/defer_level_setup = FALSE) . = ..() level_z = _z_level @@ -232,6 +236,7 @@ ///Prepare level for being used. Setup borders, lateral z connections, ambient lighting, atmosphere, etc.. /datum/level_data/proc/setup_level_data(var/skip_gen = FALSE) + if(_level_setup_completed) log_debug("level_data for [src], on z [level_z], had setup_level_data called more than once!") return //Since we can defer setup, make sure we only setup once @@ -444,24 +449,35 @@ if(base_area) return list(base_area) +// If we do serde, that implies we don't want to apply another layer of procgen over what's already saved. +// Specific levels should override this proc as needed for generation independent of serde. +/datum/level_data/proc/should_generate_level() + if(!is_persistent()) + return TRUE + if(!get_config_value(/decl/config/toggle/roundstart_level_generation) || !(get_subtemplate_budget() || length(level_generators))) + return FALSE + if(isnull(_has_serde_data)) + var/decl/serialization_handler/handler = RESOLVE_TO_DECL(persistence_handler) + _has_serde_data = fexists(handler.get_data_path(persistent_data_location, global.using_map.path, ckey(level_id))) + return !_has_serde_data + ///Called when setting up the level. Apply generators and anything that modifies the turfs of the level. /datum/level_data/proc/generate_level() - - if(!get_config_value(/decl/config/toggle/roundstart_level_generation)) + if(!should_generate_level()) + report_progress("Skipping [_has_serde_data ? "post-serde " : ""]level generation for [level_id].") return - var/origx = level_inner_min_x var/origy = level_inner_min_y var/endx = level_inner_min_x + level_inner_width var/endy = level_inner_min_y + level_inner_height - // Run level generators. for(var/gen_type in level_generators) + report_progress("Placing [gen_type] on [level_id]...") new gen_type(origx, origy, level_z, endx, endy, FALSE, TRUE, get_base_area_instance()) - // Place points of interest. var/budget = get_subtemplate_budget() if(budget) + report_progress("Placing subtemplates on [level_id]...") spawn_subtemplates(budget, get_subtemplate_category(), get_subtemplate_blacklist(), get_subtemplate_whitelist()) ///Apply the parent entity's map generators. (Planets generally) @@ -605,7 +621,7 @@ /datum/level_data/proc/get_display_name() if(!name) - var/obj/effect/overmap/overmap_entity = global.overmap_sectors[num2text(level_z)] + var/obj/effect/overmap/overmap_entity = global.overmap_sectors[level_z] if(overmap_entity?.name) name = overmap_entity.name else @@ -741,14 +757,14 @@ INITIALIZE_IMMEDIATE(/obj/abstract/level_data_spawner) /datum/level_data/main_level level_flags = (ZLEVEL_STATION|ZLEVEL_CONTACT|ZLEVEL_PLAYER) - permit_persistence = TRUE + permit_legacy_persistence = TRUE /datum/level_data/admin_level level_flags = (ZLEVEL_ADMIN|ZLEVEL_SEALED) /datum/level_data/player_level level_flags = (ZLEVEL_CONTACT|ZLEVEL_PLAYER) - permit_persistence = TRUE + permit_legacy_persistence = TRUE /datum/level_data/unit_test level_flags = (ZLEVEL_CONTACT|ZLEVEL_PLAYER|ZLEVEL_SEALED) diff --git a/code/modules/multiz/level_persistence_handler.dm b/code/modules/multiz/level_persistence_handler.dm new file mode 100644 index 000000000000..39337f5c246d --- /dev/null +++ b/code/modules/multiz/level_persistence_handler.dm @@ -0,0 +1,14 @@ +/decl/serialization_handler/proc/get_data_path(location, map, level) + return "[location]/[map]/[level]" + +/decl/serialization_handler/proc/save_level_data(datum/level_data/level_data, location, map, level) + return // Unimplemented, so return null to indicate a failure + +/decl/serialization_handler/proc/save_data_to_file(filepath, save_data, report_id) + return // Unimplemented, so return null to indicate a failure + +/decl/serialization_handler/proc/load_level_data(location, map, level) + return // Unimplemented, so return null to indicate a failure + +/decl/serialization_handler/proc/load_data_from_file(filepath) + return // Unimplemented, so return null to indicate a failure diff --git a/code/modules/multiz/level_persistence_handler_json.dm b/code/modules/multiz/level_persistence_handler_json.dm new file mode 100644 index 000000000000..bebf101a2b78 --- /dev/null +++ b/code/modules/multiz/level_persistence_handler_json.dm @@ -0,0 +1,46 @@ +/decl/serialization_handler/json/get_data_path(location, map, level) + return "[..()].json" + +/decl/serialization_handler/json/save_level_data(datum/level_data/level_data, location, map, level) + return save_data_to_file(get_data_path(location, map, level), level_data.get_persistent_data(), level_data.level_id) + +/decl/serialization_handler/json/save_data_to_file(filepath, save_data, report_id) + try + if(!length(save_data)) + return 1 + + var/write_data = json_encode(save_data) + var/write_file = file(filepath) + + // Do a backup (at the end to avoid overwriting then throwing an exception) + if(fexists(filepath)) + var/backup_contents = file2text(filepath) + var/backup_file = file("[filepath].[time2text(REALTIMEOFDAY, "YY-MM-DD_hh-mm")].backup") + to_file(backup_file, backup_contents) + // Clear old file to avoid appending data. + // TODO: remove old backups? Leave as an exercise for the admin? + fdel(filepath) + + // Finally, write out our new json. + to_file(write_file, write_data) + report_progress("Saved [length(save_data)] record\s for [report_id].") + + catch(var/exception/E) + error("Exception when saving persistent level data to [filepath]: [EXCEPTION_TEXT(E)]") + return null + + return 1 // Return a non-null value just to show we didn't throw an exception. + +/decl/serialization_handler/json/load_level_data(location, map, level) + return load_data_from_file(get_data_path(location, map, level)) + +/decl/serialization_handler/json/load_data_from_file(filepath) + try + if(fexists(filepath)) // done separately to avoid generating an error for levels with no saved data + var/loaded_json = safe_file2text(filepath) + if(loaded_json) + return json_decode(loaded_json) // do not cache this giant blob pls + catch(var/exception/E) + error("Exception when loading persistent level data from [filepath]: [EXCEPTION_TEXT(E)]") + return null + return 1 // Return a non-null value just to show we didn't throw an exception. \ No newline at end of file diff --git a/code/modules/multiz/level_persistence_serialization.dm b/code/modules/multiz/level_persistence_serialization.dm new file mode 100644 index 000000000000..a72affa8dede --- /dev/null +++ b/code/modules/multiz/level_persistence_serialization.dm @@ -0,0 +1,99 @@ +// General flow of level persistence: +// Saving: +// - SSpersistence periodically iterates the z-level list, finds levels that want to serde, and calls save_persistent_data() +// - Levels return a list of instances to get_persistent_instances(), instances have Serialize() called and return a list of modified fields. +// - Fields are serialized (to JSON with the default handler) and written to disk. +// Loading: +// - SSmapping initializes and calls preload_persistent_data() and load_persistent_data() on relevant /datum/level_data z-level objects. +// - load_persistent_data() creates the base instances and (for atoms) sets __init_deserialisation_payload with the data loaded from tile. +// - SSatoms flush calls Preload() on all deserialized atoms which pre-populates vars on the atom. +// - Ssatoms proceeds to Initialize() atoms as normal. + +var/global/list/level_persistence_ref_map = list() +/datum/level_data + /// String indicating a location for use in serde. Typically a filepath, + /// but not strictly required to be. Implementation is on the handler. + /// If set, will automatically suffix map path and level name. + /// Leave null to opt out of any persistence for this level. + // Example setting would be: + // persistent_data_location = "data/level_data" + VAR_PROTECTED/persistent_data_location + /// Decl handler, mostly forcing myself to keep this general so it can be optimized with a DB or something down the track. + VAR_PRIVATE/persistence_handler = /decl/serialization_handler/json + /// 2D list of coordinates for turfs to serialize. + var/list/changed_turfs + /// Legacy bool. Whether or not this level permits things like graffiti and filth to persist across rounds. + var/permit_legacy_persistence = FALSE + +/datum/level_data/proc/is_persistent() + return !isnull(persistent_data_location) && !isnull(persistence_handler) && !isnull(level_id) + +/datum/level_data/proc/get_persistent_data() + . = list() + var/list/instances_to_save = get_persistent_instances() + if(!length(instances_to_save)) + return + for(var/datum/thing as anything in get_persistent_instances()) + var/serialized_instance = thing.Serialize() + if(length(serialized_instance)) + .[thing.get_run_uid()] = serialized_instance + +// Returns a linear list of instances that we are interested in saving. +/datum/level_data/proc/get_persistent_instances() + for(var/coord in changed_turfs) + + var/list/coord_list = cached_json_decode(coord) + if(!islist(coord_list) || length(coord_list) < 2) + changed_turfs -= coord + continue + + var/turf/turf = locate(coord_list[1], coord_list[2], level_z) + if(!istype(turf) || !turf.ShouldSerialize()) + continue + + for(var/datum/instance in turf.UnpackSerializableInstances()) + if(instance.ShouldSerialize()) + LAZYDISTINCTADD(., instance) + +// First load all the raw data into memory so every reference is populated. +/datum/level_data/proc/preload_persistent_data() + + // Basic sanity check. + if(persistent_data_location && !level_id) + persistent_data_location = null + PRINT_STACK_TRACE("Level data [type] tried to initialize persistent data but had no level_id.") + return FALSE + + // Don't bother if we aren't configured for it at all. + if(!is_persistent()) + return FALSE + + // Atoms on a map are expected to be returned as an associative list with some specific text keys. + try + var/decl/serialization_handler/load_handler = GET_DECL(persistence_handler) + var/list/loaded_data = load_handler?.load_level_data(persistent_data_location, global.using_map.path, ckey(level_id)) + if(islist(loaded_data) && length(loaded_data)) + var/list/instance_map = list() + global.level_persistence_ref_map[level_id] = instance_map + for(var/uid in loaded_data) + instance_map[uid] = loaded_data[uid] + return TRUE + catch(var/exception/E) + PRINT_STACK_TRACE("Exception during '[level_id]' preload: [EXCEPTION_TEXT(E)]") + + return FALSE + +// Now create the instances and register them in the global map. Note that levels +// with no level_id or no persistence handling set will not reach this proc. +// Returns TRUE if it loaded anything; this may imply not needing to run level +// generation for this level (random maps, etc) +/datum/level_data/proc/load_persistent_data() + _has_serde_data = length(instantiate_serialized_data(level_z, "[level_id]/[name]", global.level_persistence_ref_map[level_id])) > 0 + return _has_serde_data + +// Write any data out if we need to. +/datum/level_data/proc/save_persistent_data() + // TODO: block any changes to persistent data structures while save is running? + if(is_persistent()) + var/decl/serialization_handler/save_handler = GET_DECL(persistence_handler) + save_handler?.save_level_data(src, persistent_data_location, global.using_map.path, ckey(level_id)) diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 4240ed507b22..5461aee03b7b 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -48,29 +48,27 @@ /mob/proc/can_overcome_gravity() return FALSE -/mob/living/human/can_overcome_gravity() +/mob/living/can_overcome_gravity() //First do species check - if(species && species.can_overcome_gravity(src)) - return 1 - else - var/turf/T = loc - if(((T.get_physical_height() + T.get_fluid_depth()) >= FLUID_DEEP) || T.get_fluid_depth() >= FLUID_MAX_DEPTH) - if(can_float()) - return 1 - - for(var/atom/a in src.loc) - if(a.atom_flags & ATOM_FLAG_CLIMBABLE) - return 1 - - //Last check, list of items that could plausibly be used to climb but aren't climbable themselves - var/list/objects_to_stand_on = list( - /obj/item/stool, - /obj/structure/bed, - ) - for(var/type in objects_to_stand_on) - if(locate(type) in src.loc) - return 1 - return 0 + var/decl/species/my_species = get_species() + if(my_species?.can_overcome_gravity(src)) + return TRUE + var/turf/T = loc + if(((T.get_physical_height() + T.get_fluid_depth()) >= FLUID_DEEP) || T.get_fluid_depth() >= FLUID_MAX_DEPTH) + if(can_float()) + return TRUE + for(var/atom/climbable in src.loc) + if((climbable.atom_flags & ATOM_FLAG_CLIMBABLE) && climbable.can_climb(src, silent = TRUE)) + return TRUE + //Last check, list of items that could plausibly be used to climb but aren't climbable themselves + var/static/list/objects_to_stand_on = list( + /obj/item/stool, + /obj/structure/bed, + ) + for(var/type in objects_to_stand_on) + if(locate(type) in src.loc) + return TRUE + return FALSE //FALLING STUFF @@ -323,6 +321,7 @@ /mob/living/verb/check_sky() set name = "Check Sky" + set category = "IC" if(!client || is_physically_disabled() || !isturf(loc)) to_chat(src, SPAN_WARNING("You can't check the sky right now.")) return diff --git a/code/modules/multiz/turf_mimic_edge.dm b/code/modules/multiz/turf_mimic_edge.dm index df31a9f6bdfc..fb33c094c7c0 100644 --- a/code/modules/multiz/turf_mimic_edge.dm +++ b/code/modules/multiz/turf_mimic_edge.dm @@ -27,7 +27,6 @@ //////////////////////////////// // Simulated Mimic Edges //////////////////////////////// - ///Simulated turf meant to replicate the appearence of another. /turf/mimic_edge name = MIMIC_EDGE_NAME @@ -61,6 +60,13 @@ QDEL_NULL(click_eater) //Make sure we get rid of it if the turf is somehow replaced by map gen to prevent them accumulating. return ..() +/turf/mimic_edge/ShouldSerialize(_age) + SHOULD_CALL_PARENT(FALSE) + return FALSE + +/turf/mimic_edge/GetPossiblySerializableInstances() + return null + /turf/mimic_edge/Crossed(atom/movable/O) . = ..() if(isobserver(O)) @@ -208,6 +214,10 @@ /turf/mimic_edge/transition/flooded flooded = /decl/material/liquid/water +/turf/mimic_edge/transition/flooded/salt + contaminant_reagent_type = /decl/material/solid/sodiumchloride + contaminant_proportion = 0.10 // 1:10 salt:water, NOT 10% salt + //////////////////////////////// // Loop Edges //////////////////////////////// diff --git a/code/modules/organs/ailments/faults/fault_locking_thumbs.dm b/code/modules/organs/ailments/faults/fault_locking_thumbs.dm index c689d9c477db..61678c157ead 100644 --- a/code/modules/organs/ailments/faults/fault_locking_thumbs.dm +++ b/code/modules/organs/ailments/faults/fault_locking_thumbs.dm @@ -5,20 +5,18 @@ BP_L_ARM, BP_L_HAND, BP_R_ARM, - BP_R_HAND, - BP_AUGMENT_R_ARM, - BP_AUGMENT_L_ARM, - BP_AUGMENT_R_HAND, - BP_AUGMENT_L_HAND + BP_R_HAND ) +/datum/ailment/fault/locking_thumbs/proc/resolve_tag_to_slot(organ_tag) + switch(organ_tag) + if(BP_L_ARM, BP_L_HAND) + return BP_L_HAND + if(BP_R_ARM, BP_R_HAND) + return BP_R_HAND + /datum/ailment/fault/locking_thumbs/on_ailment_event() - var/slot = null - switch (organ.organ_tag) - if (BP_L_ARM, BP_L_HAND, BP_AUGMENT_L_HAND, BP_AUGMENT_L_ARM) - slot = BP_L_HAND - if (BP_R_ARM, BP_R_HAND, BP_AUGMENT_R_HAND, BP_AUGMENT_R_ARM) - slot = BP_R_HAND + var/slot = resolve_tag_to_slot(organ.organ_tag) var/obj/item/thing = organ.owner.get_equipped_item(slot) if(thing && organ.owner.try_unequip(thing)) var/decl/pronouns/pronouns = organ.owner.get_pronouns() diff --git a/code/modules/organs/ailments/faults/fault_visual_impairment.dm b/code/modules/organs/ailments/faults/fault_visual_impairment.dm index 3d54995fc284..c7b1c8fe87ef 100644 --- a/code/modules/organs/ailments/faults/fault_visual_impairment.dm +++ b/code/modules/organs/ailments/faults/fault_visual_impairment.dm @@ -1,10 +1,9 @@ /datum/ailment/fault/visual_impair name = "visual impairment" applies_to_organ = list( - BP_HEAD, - BP_AUGMENT_HEAD + BP_HEAD, ) - + /datum/ailment/fault/visual_impair/New() ..() if(organ?.owner) diff --git a/code/modules/organs/external/_external_damage.dm b/code/modules/organs/external/_external_damage.dm index 6ca145d43361..2431326554db 100644 --- a/code/modules/organs/external/_external_damage.dm +++ b/code/modules/organs/external/_external_damage.dm @@ -11,8 +11,11 @@ if(!owner) return ..() - var/brute = damage_type == BRUTE ? round(damage * get_brute_mod(damage_flags), 0.1) : 0 - var/burn = damage_type == BURN ? round(damage * get_burn_mod(damage_flags), 0.1) : 0 + var/final_brute_mod = get_brute_mod(damage_flags) + (0.2 * burn_dam/max_damage) // extra brute taken if you have burn damage. why? ask whoever originally coded it. + var/final_burn_mod = get_burn_mod(damage_flags) + + var/brute = damage_type == BRUTE ? round(damage * final_brute_mod, 0.1) : 0 + var/burn = damage_type == BURN ? round(damage * final_burn_mod, 0.1) : 0 if((brute <= 0) && (burn <= 0)) return 0 @@ -318,29 +321,20 @@ return FALSE /obj/item/organ/external/proc/get_brute_mod(var/damage_flags) - var/obj/item/organ/internal/augment/armor/A = owner?.get_organ(BP_AUGMENT_CHEST_ARMOUR, /obj/item/organ/internal/augment/armor) - var/B = 1 - if(A) - B = A.brute_mult + . = 1 if(!BP_IS_PROSTHETIC(src)) - B *= species.get_brute_mod(owner) - var/blunt = !(damage_flags & DAM_EDGE|DAM_SHARP) - if(blunt && BP_IS_BRITTLE(src)) - B *= 1.5 + . *= species.get_brute_mod(owner) + if(!(damage_flags & DAM_EDGE|DAM_SHARP) && BP_IS_BRITTLE(src)) + . *= 1.5 if(BP_IS_CRYSTAL(src)) - B *= 0.8 - return B + (0.2 * burn_dam/max_damage) //burns make you take more brute damage + . *= 0.8 /obj/item/organ/external/proc/get_burn_mod(var/damage_flags) - var/obj/item/organ/internal/augment/armor/A = owner?.get_organ(BP_AUGMENT_CHEST_ARMOUR, /obj/item/organ/internal/augment/armor) - var/B = 1 - if(A) - B = A.burn_mult + . = 1 if(!BP_IS_PROSTHETIC(src)) - B *= species.get_burn_mod(owner) + . *= species.get_burn_mod(owner) if(BP_IS_CRYSTAL(src)) - B *= 0.1 - return B + . *= 0.1 //organs can come off in three cases //1. If the damage source is edge_eligible and the brute damage dealt exceeds the edge threshold, then the organ is cut off. diff --git a/code/modules/organs/external/diagnostics.dm b/code/modules/organs/external/diagnostics.dm index b78c313b71c7..d7047a14aa82 100644 --- a/code/modules/organs/external/diagnostics.dm +++ b/code/modules/organs/external/diagnostics.dm @@ -107,9 +107,6 @@ unknown_body++ if(unknown_body) . += "Unknown body present" - for(var/obj/item/organ/internal/augment/aug in internal_organs) - if(istype(aug) && aug.known) - . += "[capitalize(aug.name)] implanted" var/obj/item/organ/internal/lungs/L = locate() in src if( L && L.is_bruised()) . += "Lung ruptured" diff --git a/code/modules/organs/external/tail.dm b/code/modules/organs/external/tail.dm index 981048c93c86..656d6e533c6b 100644 --- a/code/modules/organs/external/tail.dm +++ b/code/modules/organs/external/tail.dm @@ -15,16 +15,13 @@ skip_body_icon_draw = TRUE force_limb_dir = WEST - /// Name of tail state in species effects icon file. Used as a prefix for animated states. - var/tail_state = BP_TAIL - /// Icon file to use for tail states (including animations) - var/tail_icon = 'icons/mob/human_races/species/default_tail.dmi' + icon = 'icons/mob/human_races/species/default_tail.dmi' + icon_state = BP_TAIL + + /// Icon to use for tail states; separate to icon above because external organs generated a blended icon for themselves to replace it. + var/tail_icon = 'icons/mob/human_races/species/default_tail.dmi' /// Blend mode for overlaying colour on the tail. var/tail_blend = ICON_ADD - /// State modifier for hair overlays. - var/tail_hair - /// Blend mode for hair overlays. - var/tail_hair_blend = ICON_ADD /// How many random tail states are available for animations. var/tail_animation_states = 0 /// If we have an animation playing, it will be this state. @@ -111,7 +108,7 @@ /obj/item/organ/external/tail/proc/get_tail_state() var/decl/sprite_accessory/tail/tail_data = get_accessory_data() - return tail_data?.draw_accessory ? tail_data.icon_state : tail_state + return tail_data?.draw_accessory ? tail_data.icon_state : icon_state /obj/item/organ/external/tail/proc/get_tail_animation_states() var/decl/sprite_accessory/tail/tail_data = get_accessory_data() @@ -121,14 +118,6 @@ var/decl/sprite_accessory/tail/tail_data = get_accessory_data() return tail_data?.draw_accessory ? tail_data.color_blend : tail_blend -/obj/item/organ/external/tail/proc/get_tail_hair() - var/decl/sprite_accessory/tail/tail_data = get_accessory_data() - return tail_data?.draw_accessory ? tail_data.hair_state : tail_hair - -/obj/item/organ/external/tail/proc/get_tail_hair_blend() - var/decl/sprite_accessory/tail/tail_data = get_accessory_data() - return tail_data?.draw_accessory ? tail_data.hair_blend : tail_hair_blend - /obj/item/organ/external/tail/proc/get_tail_metadata() var/decl/sprite_accessory/tail/tail_data = get_accessory_data() if(tail_data?.draw_accessory) diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm index fa5cbde82863..aeea20125422 100644 --- a/code/modules/organs/internal/lungs.dm +++ b/code/modules/organs/internal/lungs.dm @@ -34,10 +34,20 @@ QDEL_NULL(inhaled) . = ..() -/obj/item/organ/internal/lungs/initialize_reagents(populate) +/obj/item/organ/internal/lungs/Serialize() + . = ..() + SERIALIZE_REAGENTS(inhaled, /obj/item/organ/internal/lungs, "inhaled") + +/obj/item/organ/internal/lungs/Deserialize(list/instance_map) + . = ..() + DESERIALIZE_REAGENTS(inhaled, "inhaled") + +/obj/item/organ/internal/lungs/initialize_reagents() + FINALIZE_REAGENTS_SERDE(inhaled) if(!inhaled) - inhaled = new/datum/reagents/metabolism(240, (owner || src), CHEM_INHALE) - REAGENT_SET_ATOM(inhaled, src) + inhaled = new/datum/reagents/metabolism(240, src, CHEM_INHALE) + var/owner_atom = owner || src + REAGENT_SET_ATOM(inhaled, owner_atom) . = ..() /obj/item/organ/internal/lungs/do_install(mob/living/human/target, obj/item/organ/external/affected, in_place) diff --git a/code/modules/organs/internal/stomach.dm b/code/modules/organs/internal/stomach.dm index 5c98aeea901d..52016ae3967b 100644 --- a/code/modules/organs/internal/stomach.dm +++ b/code/modules/organs/internal/stomach.dm @@ -12,6 +12,14 @@ QDEL_NULL(ingested) . = ..() +/obj/item/organ/internal/stomach/Serialize() + . = ..() + SERIALIZE_REAGENTS(ingested, /obj/item/organ/internal/stomach, "ingested") + +/obj/item/organ/internal/stomach/Deserialize(list/instance_map) + . = ..() + DESERIALIZE_REAGENTS(ingested, "ingested") + /obj/item/organ/internal/stomach/set_species(species_uid) if(species?.gluttonous) verbs -= /obj/item/organ/internal/stomach/proc/throw_up @@ -21,10 +29,12 @@ if(species && !stomach_capacity) stomach_capacity = species.stomach_capacity -/obj/item/organ/internal/stomach/initialize_reagents(populate) +/obj/item/organ/internal/stomach/initialize_reagents() + FINALIZE_REAGENTS_SERDE(ingested) if(!ingested) - ingested = new/datum/reagents/metabolism(240, (owner || src), CHEM_INGEST) - REAGENT_SET_ATOM(ingested, src) + ingested = new/datum/reagents/metabolism(240, src, CHEM_INGEST) + var/owner_atom = owner || src + REAGENT_SET_ATOM(ingested, owner_atom) . = ..() /obj/item/organ/internal/stomach/do_install() diff --git a/code/modules/overmap/_overmap.dm b/code/modules/overmap/_overmap.dm index 6f63a9edf783..96d9180a2402 100644 --- a/code/modules/overmap/_overmap.dm +++ b/code/modules/overmap/_overmap.dm @@ -61,7 +61,7 @@ if (!T || !A) return - var/obj/effect/overmap/visitable/M = global.overmap_sectors[num2text(T.z)] + var/obj/effect/overmap/visitable/M = global.overmap_sectors[T.z] if (!M) return diff --git a/code/modules/overmap/contacts/contact_sensors.dm b/code/modules/overmap/contacts/contact_sensors.dm index 7ebe9b07788e..68f0907ac833 100644 --- a/code/modules/overmap/contacts/contact_sensors.dm +++ b/code/modules/overmap/contacts/contact_sensors.dm @@ -74,7 +74,7 @@ // Find all sectors with a tracker on their z-level. Only works on ships when they are in space. for(var/obj/item/ship_tracker/tracker in trackers) if(tracker.enabled) - var/obj/effect/overmap/visitable/tracked_effect = global.overmap_sectors[num2text(get_z(tracker))] + var/obj/effect/overmap/visitable/tracked_effect = global.overmap_sectors[get_z(tracker)] if(tracked_effect && istype(tracked_effect) && tracked_effect != linked && tracked_effect.requires_contact) objects_in_current_view[tracked_effect] = TRUE objects_in_view[tracked_effect] = 100 diff --git a/code/modules/overmap/ftl_shunt/computer.dm b/code/modules/overmap/ftl_shunt/computer.dm index e971f2d5a38f..989d9f1be8ee 100644 --- a/code/modules/overmap/ftl_shunt/computer.dm +++ b/code/modules/overmap/ftl_shunt/computer.dm @@ -24,7 +24,7 @@ /obj/machinery/computer/ship/ftl/proc/recalc_cost() if(!linked_core) return INFINITY - var/obj/effect/overmap/visitable/sector = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/sector = global.overmap_sectors[z] if(!istype(sector)) return INFINITY var/jump_dist = get_dist(linked, locate(linked_core.shunt_x, linked_core.shunt_y, sector.z)) @@ -35,7 +35,7 @@ if(!linked_core) return INFINITY - var/obj/effect/overmap/visitable/sector = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/sector = global.overmap_sectors[z] if(!istype(sector)) return INFINITY diff --git a/code/modules/overmap/ftl_shunt/core.dm b/code/modules/overmap/ftl_shunt/core.dm index a74d6a20a280..5ad561f38a01 100644 --- a/code/modules/overmap/ftl_shunt/core.dm +++ b/code/modules/overmap/ftl_shunt/core.dm @@ -237,7 +237,7 @@ return FTL_START_CONFIRMED /obj/machinery/ftl_shunt/core/proc/calculate_jump_requirements() - var/obj/effect/overmap/visitable/site = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/site = global.overmap_sectors[z] if(site) var/shunt_distance var/vessel_mass = ftl_computer.linked.get_vessel_mass() @@ -274,7 +274,7 @@ cancel_shunt() return //If for some reason we don't have fuel now, just return. - var/obj/effect/overmap/visitable/site = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/site = global.overmap_sectors[z] if(site) var/destination = locate(shunt_x, shunt_y, site.z) var/jumpdist = get_dist(get_turf(ftl_computer.linked), destination) @@ -554,7 +554,7 @@ /decl/material/gas/hydrogen/deuterium = 25000, /decl/material/gas/hydrogen = 25000, /decl/material/solid/exotic_matter = 50000 - ) + ) var/obj/item/fuel_assembly/fuel var/obj/machinery/ftl_shunt/core/master var/max_fuel = 0 @@ -654,4 +654,7 @@ icon = 'icons/obj/items/stock_parts/stock_parts.dmi' icon_state = "smes_coil" color = COLOR_YELLOW - matter = list(/decl/material/solid/exotic_matter = MATTER_AMOUNT_REINFORCEMENT, /decl/material/solid/metal/plasteel = MATTER_AMOUNT_PRIMARY) + matter = list( + /decl/material/solid/exotic_matter = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/metal/plasteel = MATTER_AMOUNT_PRIMARY + ) diff --git a/code/modules/overmap/internet/internet_repeater.dm b/code/modules/overmap/internet/internet_repeater.dm index 29ae2a58e6c0..e8dde83b7da7 100644 --- a/code/modules/overmap/internet/internet_repeater.dm +++ b/code/modules/overmap/internet/internet_repeater.dm @@ -51,7 +51,7 @@ var/global/list/internet_repeaters = list() var/data = list() data["powered"] = (use_power == POWER_USE_ACTIVE) - var/obj/effect/overmap/visitable/sector = global.overmap_sectors[num2text(get_z(src))] + var/obj/effect/overmap/visitable/sector = global.overmap_sectors[get_z(src)] if(sector) var/list/internet_connections = sector.get_internet_connections() diff --git a/code/modules/overmap/overmap_shuttle.dm b/code/modules/overmap/overmap_shuttle.dm index 2a7e5b3fb646..03f019080ec8 100644 --- a/code/modules/overmap/overmap_shuttle.dm +++ b/code/modules/overmap/overmap_shuttle.dm @@ -1,4 +1,4 @@ -#define waypoint_sector(waypoint) global.overmap_sectors[num2text(waypoint.z)] +#define waypoint_sector(waypoint) global.overmap_sectors[waypoint.z] /datum/shuttle/autodock/overmap warmup_time = 10 diff --git a/code/modules/overmap/radio_beacon.dm b/code/modules/overmap/radio_beacon.dm index 4d6f541b3f75..7ccc75501352 100644 --- a/code/modules/overmap/radio_beacon.dm +++ b/code/modules/overmap/radio_beacon.dm @@ -46,7 +46,7 @@ to_chat(user, SPAN_NOTICE("You deactivate \the [src], cutting short it's radio broadcast.")) QDEL_NULL(signal) return - var/obj/effect/overmap/visitable/O = global.overmap_sectors[num2text(get_z(src))] + var/obj/effect/overmap/visitable/O = global.overmap_sectors[get_z(src)] if(!O) to_chat(user, SPAN_WARNING("You cannot deploy \the [src] here.")) return diff --git a/code/modules/overmap/sectors.dm b/code/modules/overmap/sectors.dm index 891382ad00ac..f6988ec8a794 100644 --- a/code/modules/overmap/sectors.dm +++ b/code/modules/overmap/sectors.dm @@ -131,7 +131,7 @@ var/global/list/known_overmap_sectors var/datum/overmap/overmap = global.overmaps_by_z[num2text(z)] if(istype(overmap)) for(var/zlevel in map_z) - global.overmap_sectors[num2text(zlevel)] = src + global.overmap_sectors[zlevel] = src SSmapping.player_levels |= map_z if(!(sector_flags & OVERMAP_SECTOR_IN_SPACE)) @@ -149,7 +149,7 @@ var/global/list/known_overmap_sectors // Returns the /obj/effect/overmap/visitable to which the atom belongs based on localtion, or null /atom/proc/get_owning_overmap_object() var/z = get_z(src) - var/initial_sector = global.overmap_sectors[num2text(z)] + var/initial_sector = global.overmap_sectors[z] if(!initial_sector) return diff --git a/code/modules/overmap/ships/computers/comms.dm b/code/modules/overmap/ships/computers/comms.dm index 3978b1a4c58f..221fa1200d5a 100644 --- a/code/modules/overmap/ships/computers/comms.dm +++ b/code/modules/overmap/ships/computers/comms.dm @@ -120,7 +120,7 @@ var/turf/T = get_turf(src) if(!T) return - var/obj/effect/overmap/O = global.overmap_sectors["[z]"] + var/obj/effect/overmap/O = global.overmap_sectors[z] if(!O) return if(stat & (BROKEN|NOPOWER)) @@ -140,7 +140,7 @@ /obj/machinery/shipcomms/proc/get_nearby_entities() . = list() - var/obj/effect/overmap/O = global.overmap_sectors["[z]"] + var/obj/effect/overmap/O = global.overmap_sectors[z] if(!O) return var/turf/origin = get_turf(O) @@ -173,7 +173,7 @@ /obj/machinery/shipcomms/Destroy() var/turf/T = get_turf(src) if(istype(T)) - var/obj/effect/overmap/O = global.overmap_sectors["[T.z]"] + var/obj/effect/overmap/O = global.overmap_sectors[T.z] if(O) unregister(O) . = ..() @@ -181,8 +181,8 @@ /obj/machinery/shipcomms/update_power_on_move(atom/movable/mover, atom/old_loc, atom/new_loc) ..() if(istype(old_loc) && old_loc != new_loc && (!istype(new_loc) || new_loc.z != old_loc.z)) - var/obj/effect/overmap/lastsector = global.overmap_sectors["[old_loc.z]"] - var/obj/effect/overmap/currentsector = istype(new_loc) && global.overmap_sectors["[new_loc.z]"] + var/obj/effect/overmap/lastsector = global.overmap_sectors[old_loc.z] + var/obj/effect/overmap/currentsector = istype(new_loc) && global.overmap_sectors[new_loc.z] if(istype(lastsector) && lastsector != currentsector) unregister(lastsector.comms_masers) refresh_overmap_registration() diff --git a/code/modules/overmap/ships/computers/shuttle.dm b/code/modules/overmap/ships/computers/shuttle.dm index 4f2e43fbe4cf..b855f1b207d6 100644 --- a/code/modules/overmap/ships/computers/shuttle.dm +++ b/code/modules/overmap/ships/computers/shuttle.dm @@ -71,7 +71,7 @@ to_chat(user, SPAN_WARNING("The manual controls look hopelessly complex to you!")) /obj/machinery/computer/shuttle_control/explore/proc/start_landing(var/mob/user, var/datum/shuttle/autodock/overmap/shuttle) - var/obj/effect/overmap/visitable/current_sector = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/current_sector = global.overmap_sectors[z] var/obj/effect/overmap/visitable/target_sector if(current_sector && istype(current_sector)) @@ -110,7 +110,7 @@ var/mob/observer/eye/landing/landing_eye = eye_extension.extension_eye var/turf/lz_turf = eye_extension.get_eye_turf() - var/obj/effect/overmap/visitable/sector = global.overmap_sectors[num2text(lz_turf.z)] + var/obj/effect/overmap/visitable/sector = global.overmap_sectors[lz_turf.z] if(!sector.allow_free_landing()) // Additional safety check to ensure the sector permits landing. to_chat(user, SPAN_WARNING("Invalid landing zone!")) return diff --git a/code/modules/overmap/ships/landable.dm b/code/modules/overmap/ships/landable.dm index 24f4d999c0ac..93adc8097675 100644 --- a/code/modules/overmap/ships/landable.dm +++ b/code/modules/overmap/ships/landable.dm @@ -53,7 +53,7 @@ if(!child_shuttle || !istype(child_shuttle)) return if(child_shuttle.current_location.flags & SLANDMARK_FLAG_DISCONNECTED) // Keep an eye on the distance between the shuttle and the sector if we aren't fully docked. - var/obj/effect/overmap/visitable/ship/landable/encounter = global.overmap_sectors[num2text(child_shuttle.current_location.z)] + var/obj/effect/overmap/visitable/ship/landable/encounter = global.overmap_sectors[child_shuttle.current_location.z] if((get_dist(src, encounter) > min(child_shuttle.range, 1))) // Some leeway so 0 range shuttles are still able to chase. child_shuttle.attempt_force_move(landmark) if(istype(encounter)) @@ -145,7 +145,7 @@ ADJUST_TAG_VAR(shuttle_name, map_hash) /obj/effect/shuttle_landmark/ship/Destroy() - var/obj/effect/overmap/visitable/ship/landable/ship = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/ship/landable/ship = global.overmap_sectors[z] if(istype(ship) && ship.landmark == src) ship.landmark = null . = ..() @@ -209,7 +209,7 @@ on_landing(from, into) /obj/effect/overmap/visitable/ship/landable/proc/on_landing(obj/effect/shuttle_landmark/from, obj/effect/shuttle_landmark/into) - var/obj/effect/overmap/visitable/target = global.overmap_sectors[num2text(into.z)] + var/obj/effect/overmap/visitable/target = global.overmap_sectors[into.z] var/datum/shuttle/shuttle_datum = SSshuttle.shuttles[shuttle] if(into.landmark_tag == shuttle_datum.motherdock) // If our motherdock is a landable ship, it won't be found properly here so we need to find it manually. for(var/obj/effect/overmap/visitable/ship/landable/landable in SSshuttle.ships) @@ -237,7 +237,7 @@ return "Docked with an unknown object." if(SHIP_STATUS_ENCOUNTER) var/datum/shuttle/autodock/overmap/child_shuttle = SSshuttle.shuttles[shuttle] - var/obj/effect/overmap/visitable/location = global.overmap_sectors[num2text(child_shuttle.current_location.z)] + var/obj/effect/overmap/visitable/location = global.overmap_sectors[child_shuttle.current_location.z] return "Maneuvering nearby \the [location]." if(SHIP_STATUS_TRANSIT) return "Maneuvering under secondary thrust." diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/_paper.dm similarity index 99% rename from code/modules/paperwork/paper.dm rename to code/modules/paperwork/_paper.dm index 034c88c3ae93..af411daf7cd4 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/_paper.dm @@ -43,7 +43,7 @@ /obj/item/paper/Initialize(mapload, material_key, var/_text, var/_title, var/list/md = null) . = ..(mapload, material_key) - set_content(_text ? _text : info, _title) + set_content(_text ? _text : info, _title ? _title : name) if(md) LAZYDISTINCTADD(metadata, md) //Merge them if(!mapload && persist_on_init) diff --git a/code/modules/paperwork/_paper_serde.dm b/code/modules/paperwork/_paper_serde.dm new file mode 100644 index 000000000000..8fbfe734b9bb --- /dev/null +++ b/code/modules/paperwork/_paper_serde.dm @@ -0,0 +1,30 @@ +/obj/item/paper/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(age, /obj/item/paper) + SERIALIZE_IF_MODIFIED(is_crumpled, /obj/item/paper) + SERIALIZE_IF_MODIFIED(last_modified_ckey, /obj/item/paper) + SERIALIZE_IF_MODIFIED(name, /obj/item/paper) + SERIALIZE_IF_MODIFIED(info, /obj/item/paper) + +/obj/item/paper/Deserialize() + . = ..() + SSpersistence.track_value(src, /decl/persistence_handler/paper) + +/obj/item/paper/ShouldSerialize(_age) + return ..() && (isnull(_age) || age < _age) + +/obj/item/paper/GetPossiblySerializableInstances() + . = ..() + if(istype(loc, /obj/structure/noticeboard)) + LAZYDISTINCTADD(., loc) + +// If it's old enough we start to trim down any textual information and scramble strings. +#define SERDE_MESSAGE nameof(/obj/item/paper::info) +/obj/item/paper/HandlePersistentDecay(entries_decay_at, entry_decay_weight) + __deserialization_payload[SERDE_MESSAGE] = apply_serde_message_decay( + __deserialization_payload[SERDE_MESSAGE], + __deserialization_payload[nameof(/obj/item/paper::age)], + entry_decay_weight, + entries_decay_at + ) +#undef SERDE_MESSAGE diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index 185771010d86..b5f0fdc5e78a 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -131,3 +131,15 @@ . += "Species: [record.get_species_name()]" . += "Details: [record.get_employment_record()]" return jointext(., "
") + +/obj/structure/filing_cabinet/records/security + name = "security record archive" + archive_name = "security record" + +/obj/structure/filing_cabinet/records/security/collate_data(var/datum/computer_file/report/crew_record/record) + . = list() + . += "Name: [record.get_name()]" + . += "Gender: [record.get_gender()]" + . += "Species: [record.get_species_name()]" + . += "Details: [record.get_security_record()]" + return jointext(., "
") diff --git a/code/modules/persistence/filth.dm b/code/modules/persistence/filth.dm index ab432d17a7b1..07af39cfc8d9 100644 --- a/code/modules/persistence/filth.dm +++ b/code/modules/persistence/filth.dm @@ -1,12 +1,12 @@ /obj/effect/decal/cleanable/filth - name = "filth" - desc = "Disgusting. Someone from last shift didn't do their job properly." - icon = 'icons/effects/blood.dmi' - icon_state = "mfloor1" - random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") - color = "#464f33" - persistent = TRUE - anchored = TRUE + name = "filth" + desc = "Disgusting. Someone from last shift didn't do their job properly." + icon = 'icons/effects/blood.dmi' + icon_state = "mfloor1" + random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") + color = "#464f33" + use_legacy_persistence = TRUE + anchored = TRUE /obj/effect/decal/cleanable/filth/Initialize() . = ..() diff --git a/code/modules/persistence/graffiti.dm b/code/modules/persistence/graffiti.dm index cb65ce6ca0c3..6c6515ec6ba8 100644 --- a/code/modules/persistence/graffiti.dm +++ b/code/modules/persistence/graffiti.dm @@ -10,10 +10,39 @@ anchored = TRUE var/message - var/graffiti_age = 0 var/author = "unknown" +/obj/effect/decal/writing/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(message, /obj/effect/decal/writing) + SERIALIZE_IF_MODIFIED(author, /obj/effect/decal/writing) + +// If it's old enough we start to trim down any textual information and scramble strings. +#define SERDE_MESSAGE nameof(/obj/effect/decal/writing::message) +/obj/effect/decal/writing/HandlePersistentDecay(entries_decay_at, entry_decay_weight) + var/original_message = __deserialization_payload[SERDE_MESSAGE] + var/decayed_message = apply_serde_message_decay( + __deserialization_payload[SERDE_MESSAGE], + __deserialization_payload[nameof(/obj/effect/decal::age)], + entry_decay_weight, + entries_decay_at + ) + to_world_log("decayed graffifi: [original_message] -> [decayed_message]") + __deserialization_payload[SERDE_MESSAGE] = decayed_message +#undef SERDE_MESSAGE + /obj/effect/decal/writing/Initialize(mapload, var/_age, var/_message, var/_author) + + var/turf/checking_turf = loc + if(istype(checking_turf) && !checking_turf.can_engrave()) + return INITIALIZE_HINT_QDEL + + var/too_much_graffiti = 0 + for(var/obj/effect/decal/writing/writing in loc) + too_much_graffiti++ + if(too_much_graffiti >= 5) + return INITIALIZE_HINT_QDEL + var/list/random_icon_states = get_states_in_icon(icon) for(var/obj/effect/decal/writing/writing in loc) random_icon_states -= writing.icon_state @@ -22,9 +51,10 @@ SSpersistence.track_value(src, /decl/persistence_handler/graffiti) . = ..(mapload) if(!isnull(_age)) - graffiti_age = _age - message = _message - if(!isnull(author)) + age = _age + if(_message && !message) + message = _message + if(_author && !author) author = _author /obj/effect/decal/writing/Destroy() diff --git a/code/modules/persistence/noticeboards.dm b/code/modules/persistence/noticeboards.dm index 9ec2db2954f9..02daf6cc11c2 100644 --- a/code/modules/persistence/noticeboards.dm +++ b/code/modules/persistence/noticeboards.dm @@ -19,10 +19,15 @@ // Grab any mapped notices. if(ml) - for(var/obj/item/paper/note in get_turf(src)) + for(var/obj/item/paper/note in contents) add_paper(note, skip_icon_update = TRUE) if(LAZYLEN(notices) >= max_notices) break + if(LAZYLEN(notices) < max_notices) + for(var/obj/item/paper/note in get_turf(src)) + add_paper(note, skip_icon_update = TRUE) + if(LAZYLEN(notices) >= max_notices) + break // Automatically place noticeboards that aren't mapped to specific positions. if(default_pixel_x == 0 && default_pixel_y == 0) diff --git a/code/modules/persistence/persistence_datum.dm b/code/modules/persistence/persistence_datum.dm index 08acd039c57a..db43dff3f8a2 100644 --- a/code/modules/persistence/persistence_datum.dm +++ b/code/modules/persistence/persistence_datum.dm @@ -11,6 +11,11 @@ var/has_admin_data // If set, shows up on the admin persistence panel. var/ignore_area_flags = FALSE // Set to TRUE to skip area flag checks such as nonpersistent areas. var/ignore_invalid_loc = FALSE // Set to TRUE to skip checking for a non-null station turf for the entry. + var/list/legacy_map_values // A list of legacy keys to new keys. + var/legacy_type + var/serialization_handler = /decl/serialization_handler/json // Which serialization handler to use for load/save + var/area_restricted = TRUE // Can this item persist outside of a flagged area? + var/station_restricted = TRUE // Can this item persist outside of a station level? /decl/persistence_handler/proc/SetFilename() if(name) @@ -18,80 +23,22 @@ if(!isnull(entries_decay_at) && !isnull(entries_expire_at)) entries_decay_at = floor(entries_expire_at * entries_decay_at) -/decl/persistence_handler/proc/GetValidTurf(var/turf/T, var/list/tokens) - if(T && isStationLevel(T.z) && CheckTurfContents(T, tokens)) - return T - -/decl/persistence_handler/proc/CheckTurfContents(var/turf/T, var/list/tokens) - return TRUE - -/decl/persistence_handler/proc/CheckTokenSanity(var/list/tokens) - if(!islist(tokens)) - return FALSE - if(isnull(tokens["x"]) || isnull(tokens["y"]) || isnull(tokens["z"])) - return FALSE - if(!isnull(entries_expire_at)) - if(isnull(tokens["age"])) - return FALSE - if(tokens["age"] > entries_expire_at) - return FALSE - return TRUE - -/decl/persistence_handler/proc/CreateEntryInstance(var/turf/creating, var/list/tokens) - return - -/decl/persistence_handler/proc/ProcessAndApplyTokens(var/list/tokens) - - // If it's old enough we start to trim down any textual information and scramble strings. - if(tokens["message"] && !isnull(entries_decay_at) && !isnull(entry_decay_weight)) - var/_n = tokens["age"] - var/_message = tokens["message"] - if(_n >= entries_decay_at) - var/decayed_message = "" - for(var/i = 1 to length(_message)) - var/char = copytext(_message, i, i + 1) - if(prob(round(_n * entry_decay_weight))) - if(prob(99)) - decayed_message += pick(".",",","-","'","\\","/","\"",":",";") - else - decayed_message += char - _message = decayed_message - if(length(_message)) - tokens["message"] = _message - else - return - - . = GetValidTurf(locate(tokens["x"], tokens["y"], tokens["z"]), tokens) - if(.) - . = CreateEntryInstance(., tokens) - /decl/persistence_handler/proc/IsValidEntry(var/atom/entry) if(!istype(entry)) return FALSE - if(!isnull(entries_expire_at) && GetEntryAge(entry) >= entries_expire_at) + if(!entry.ShouldSerialize(entries_expire_at)) return FALSE var/turf/T = get_turf(entry) if(!ignore_invalid_loc && (!T || !isStationLevel(T.z))) return FALSE var/area/A = get_area(T) - if(!ignore_area_flags && (!A || (A.area_flags & AREA_FLAG_IS_NOT_PERSISTENT))) + if(!ignore_area_flags && (!A || (A.area_flags & AREA_FLAG_NO_LEGACY_PERSISTENCE))) return FALSE return TRUE /decl/persistence_handler/proc/GetEntryAge(var/atom/entry) return 0 -/decl/persistence_handler/proc/CompileEntry(var/atom/entry) - var/turf/T = get_turf(entry) - . = list() - .["x"] = T?.x || 0 - .["y"] = T?.y || 0 - .["z"] = T?.z || 0 - .["age"] = GetEntryAge(entry) - -/decl/persistence_handler/proc/FinalizeTokens(var/list/tokens) - . = tokens || list() - /decl/persistence_handler/Initialize() SetFilename() @@ -101,39 +48,67 @@ if(!fexists(filename)) return - var/list/entries = cached_json_decode(safe_file2text(filename, FALSE)) + var/decl/serialization_handler/handler = GET_DECL(serialization_handler) + var/list/entries = handler.load_data_from_file(filename) + if(!length(entries)) return - var/list/encoding_flag = entries[1] - if(encoding_flag && ("url_encoded" in encoding_flag)) - entries -= encoding_flag - for(var/list/entry in entries) - for(var/i in 1 to entry.len) - var/item = entry[i] - var/decoded_value = (istext(entry[item]) ? url_decode(entry[item]) : entry[item]) - var/decoded_key = url_decode(item) - entry[i] = decoded_key - entry[decoded_key] = decoded_value - - for(var/list/entry in entries) - entry = FinalizeTokens(entry) - if(CheckTokenSanity(entry)) - ProcessAndApplyTokens(entry) + // Check for old-style persistence data and generate a key. + if(length(entries) && !istext(entries[1])) + try + // Save a backup of the old file just in case we cook it. + fcopy(filename, "[filename]-legacy.[time2text(REALTIMEOFDAY, "YY-MM-DD_hh-mm")].backup") + catch(var/exception/e) + log_error("Exception during saving backup of legacy file [filename]: [EXCEPTION_TEXT(e)]") + + // Update the data to match the expected format of the new system. + var/list/entries_with_key = list() + var/i = 1 + for(var/entry in entries) + entries_with_key[num2text(i)] = UpdateFromLegacyFormat(entry) + entries = entries_with_key + + instantiate_serialized_data(null, name, entries, entries_decay_at, entry_decay_weight) /decl/persistence_handler/proc/Shutdown() var/list/entries = list() - for(var/thing in SSpersistence.tracking_values[type]) + for(var/atom/thing in SSpersistence.tracking_values[type]) if(IsValidEntry(thing)) - entries += list(CompileEntry(thing)) - - if(fexists(filename)) - fdel(filename) - to_file(file(filename), json_encode(entries)) + var/list/things_to_serialize = thing.GetPossiblySerializableInstances() + for(var/datum/subthing in things_to_serialize) + entries[subthing.get_run_uid()] = subthing.Serialize() + var/decl/serialization_handler/handler = GET_DECL(serialization_handler) + handler.save_data_to_file(filename, entries, name) /decl/persistence_handler/proc/RemoveValue(var/atom/value) qdel(value) +/decl/persistence_handler/proc/UpdateFromLegacyFormat(list/_entry) + + // Convert any old values to the new indices. + for(var/map_key in legacy_map_values) + if(map_key in _entry) + var/value = _entry[map_key] + _entry -= map_key + _entry[legacy_map_values[map_key]] = value + + // Convert entry coords into new format. + if(("x" in _entry) || ("y" in _entry) || ("z" in _entry)) + _entry["loc"] = list( + _entry["x"] || 1, + _entry["y"] || 1, + _entry["z"] || 1 + ) + _entry -= "x" + _entry -= "y" + _entry -= "z" + + if(legacy_type && !(nameof(/datum::type) in _entry)) + _entry[nameof(/datum::type)] = legacy_type + + return _entry + /decl/persistence_handler/proc/GetAdminSummary(var/mob/user, var/can_modify) . = list("[capitalize(name)]") . += "
" diff --git a/code/modules/persistence/persistence_datum_book.dm b/code/modules/persistence/persistence_datum_book.dm index 839f0ed29a63..9b96bca8dff5 100644 --- a/code/modules/persistence/persistence_datum_book.dm +++ b/code/modules/persistence/persistence_datum_book.dm @@ -3,28 +3,13 @@ has_admin_data = TRUE ignore_area_flags = TRUE ignore_invalid_loc = TRUE - -/decl/persistence_handler/book/CreateEntryInstance(var/turf/creating, var/list/tokens) - - var/book_type = tokens["book_type"] - if(book_type) - book_type = text2path(book_type) - if(!ispath(book_type)) - book_type = /obj/item/book - - var/obj/item/book/book = new book_type(creating) - book.dat = tokens["message"] - book.title = tokens["title"] - book.author = tokens["writer"] - book.icon_state = tokens["icon_state"] - book.last_modified_ckey = tokens["author"] || "unknown" - book.unique = TRUE - book.SetName(book.title) - var/obj/structure/bookcase/case = locate() in creating - if(case) - book.forceMove(case) - case.update_icon() - return book + legacy_type = /obj/item/book + legacy_map_values = list( + "author" = nameof(/obj/item/book::last_modified_ckey), + "writer" = nameof(/obj/item/book::author), + "book_type" = nameof(/obj/item/book::type), + "message" = nameof(/obj/item/book::dat) + ) /decl/persistence_handler/book/IsValidEntry(var/atom/entry) . = ..() @@ -32,23 +17,6 @@ var/obj/item/book/book = entry . = istype(book) && book.dat && book.last_modified_ckey -/decl/persistence_handler/book/CompileEntry(var/atom/entry) - . = ..() - - var/obj/item/book/book = entry - .["author"] = book.last_modified_ckey || "" - .["message"] = book.dat || "dat" - .["title"] = book.title || "Untitled" - .["writer"] = book.author || "unknown" - .["icon_state"] = book.icon_state || "book" - .["book_type"] = "[book.type]" - - var/turf/T = get_turf(entry) - if(!T || !isStationLevel(T.z)) - .["x"] = 0 - .["y"] = 0 - .["z"] = 0 - /decl/persistence_handler/book/RemoveValue(var/atom/movable/value) var/obj/structure/bookcase/bookcase = value.loc if(istype(bookcase)) @@ -57,24 +25,6 @@ bookcase.update_icon() ..() -/decl/persistence_handler/book/GetValidTurf(var/turf/T, var/list/tokens) - - if(T) - var/area/A = get_area(T) - if(!A || (A.area_flags & AREA_FLAG_IS_NOT_PERSISTENT)) - T = null - - if(!T) - if(length(global.station_bookcases)) - T = get_turf(pick(global.station_bookcases)) - else - T = get_random_spawn_turf(SPAWN_FLAG_PERSISTENCE_CAN_SPAWN) - - . = ..() - -/decl/persistence_handler/book/GetEntryAge(var/atom/entry) - . = -1 - /decl/persistence_handler/book/GetAdminDataStringFor(var/thing, var/can_modify, var/mob/user) var/obj/item/book/book = thing if(can_modify) diff --git a/code/modules/persistence/persistence_datum_filth.dm b/code/modules/persistence/persistence_datum_filth.dm index 9f877efeab0d..249535eed99b 100644 --- a/code/modules/persistence/persistence_datum_filth.dm +++ b/code/modules/persistence/persistence_datum_filth.dm @@ -1,44 +1,11 @@ /decl/persistence_handler/filth name = "filth" entries_expire_at = 5 + legacy_type = /obj/effect/decal/cleanable/filth + legacy_map_values = list( + "path" = nameof(/obj/effect/decal/cleanable::type), + "filthiness" = nameof(/obj/effect/decal/cleanable/dirt::dirt_amount) + ) /decl/persistence_handler/filth/IsValidEntry(var/atom/entry) . = ..() && entry.invisibility == 0 - -/decl/persistence_handler/filth/CheckTokenSanity(var/list/tokens) - return ..() && ispath(tokens["path"]) - -/decl/persistence_handler/filth/CheckTurfContents(var/turf/T, var/list/tokens) - return !(locate(tokens["path"]) in T) - -/decl/persistence_handler/filth/FinalizeTokens(var/list/tokens) - . = ..() - if(.["path"] && !ispath(.["path"])) - .["path"] = text2path(.["path"]) - if(isnull(.["filthiness"])) - .["filthiness"] = 0 - -/decl/persistence_handler/filth/CreateEntryInstance(var/turf/creating, var/list/tokens) - var/_path = tokens["path"] - var/obj/effect/decal/cleanable/dirt/dirt = new _path(creating, tokens["age"]+1) - if(istype(dirt)) - dirt.dirt_amount = tokens["filthiness"] - dirt.update_icon() - return dirt - -/decl/persistence_handler/filth/GetEntryAge(var/atom/entry) - var/obj/effect/decal/cleanable/filth = entry - return filth.age - -/decl/persistence_handler/filth/proc/GetEntryPath(var/atom/entry) - var/obj/effect/decal/cleanable/filth = entry - return filth.generic_filth ? /obj/effect/decal/cleanable/filth : filth.type - -/decl/persistence_handler/filth/CompileEntry(var/atom/entry) - . = ..() - .["path"] = "[GetEntryPath(entry)]" - if(istype(entry, /obj/effect/decal/cleanable/dirt)) - var/obj/effect/decal/cleanable/dirt/dirt = entry - .["filthiness"] = dirt.dirt_amount - else - .["filthiness"] = 0 diff --git a/code/modules/persistence/persistence_datum_filth_trash.dm b/code/modules/persistence/persistence_datum_filth_trash.dm index 8dd10f4806d3..dab2fcfed354 100644 --- a/code/modules/persistence/persistence_datum_filth_trash.dm +++ b/code/modules/persistence/persistence_datum_filth_trash.dm @@ -1,17 +1,3 @@ /decl/persistence_handler/filth/trash name = "trash" - -/decl/persistence_handler/filth/trash/CheckTurfContents(var/turf/T, var/list/tokens) - var/too_much_trash = 0 - for(var/obj/item/trash/trash in T) - too_much_trash++ - if(too_much_trash >= 5) - return FALSE - return TRUE - -/decl/persistence_handler/filth/trash/GetEntryAge(var/atom/entry) - var/obj/item/trash/trash = entry - return trash.age - -/decl/persistence_handler/filth/trash/GetEntryPath(var/atom/entry) - return entry.type + legacy_type = /obj/random/trash diff --git a/code/modules/persistence/persistence_datum_graffiti.dm b/code/modules/persistence/persistence_datum_graffiti.dm index 0bc733ab2f07..3e30ab2c4ecb 100644 --- a/code/modules/persistence/persistence_datum_graffiti.dm +++ b/code/modules/persistence/persistence_datum_graffiti.dm @@ -2,22 +2,7 @@ name = "graffiti" entries_expire_at = 50 has_admin_data = TRUE - -/decl/persistence_handler/graffiti/GetValidTurf(var/turf/T, var/list/tokens) - var/turf/checking_turf = ..() - if(istype(checking_turf) && checking_turf.can_engrave()) - return checking_turf - -/decl/persistence_handler/graffiti/CheckTurfContents(var/turf/T, var/list/tokens) - var/too_much_graffiti = 0 - for(var/obj/effect/decal/writing/writing in .) - too_much_graffiti++ - if(too_much_graffiti >= 5) - return FALSE - return TRUE - -/decl/persistence_handler/graffiti/CreateEntryInstance(var/turf/creating, var/list/tokens) - return new /obj/effect/decal/writing(creating, tokens["age"]+1, tokens["message"], tokens["author"]) + legacy_type = /obj/effect/decal/writing /decl/persistence_handler/graffiti/IsValidEntry(var/atom/entry) . = ..() @@ -25,16 +10,6 @@ var/turf/T = entry.loc . = T.can_engrave() -/decl/persistence_handler/graffiti/GetEntryAge(var/atom/entry) - var/obj/effect/decal/writing/save_graffiti = entry - return save_graffiti.graffiti_age - -/decl/persistence_handler/graffiti/CompileEntry(var/atom/entry) - . = ..() - var/obj/effect/decal/writing/save_graffiti = entry - .["author"] = save_graffiti.author || "unknown" - .["message"] = save_graffiti.message - /decl/persistence_handler/graffiti/GetAdminDataStringFor(var/thing, var/can_modify, var/mob/user) var/obj/effect/decal/writing/save_graffiti = thing if(can_modify) diff --git a/code/modules/persistence/persistence_datum_paper.dm b/code/modules/persistence/persistence_datum_paper.dm index 94749f01deda..a14cb7d79878 100644 --- a/code/modules/persistence/persistence_datum_paper.dm +++ b/code/modules/persistence/persistence_datum_paper.dm @@ -2,41 +2,12 @@ name = "paper" entries_expire_at = 50 has_admin_data = TRUE - var/paper_type = /obj/item/paper - -/decl/persistence_handler/paper/CreateEntryInstance(var/turf/creating, var/list/tokens) - - var/obj/item/paper/paper = new paper_type(creating) - paper.set_content(tokens["message"], tokens["title"]) - paper.last_modified_ckey = tokens["author"] - - if("has_noticeboard" in tokens) - var/obj/structure/noticeboard/board = locate() in creating - if(!board) - var/decl/material/mat = decls_repository.get_decl_by_id_or_var(tokens["noticeboard_material"], /decl/material, "name") - board = new(creating, (mat?.type || /decl/material/solid/organic/wood/oak)) - if("noticeboard_direction" in tokens) - board.set_dir(tokens["noticeboard_direction"]) - if(LAZYLEN(board.notices) < board.max_notices) - board.add_paper(paper) - - return paper - -/decl/persistence_handler/paper/GetEntryAge(var/atom/entry) - var/obj/item/paper/paper = entry - return paper.age - -/decl/persistence_handler/paper/CompileEntry(var/atom/entry) - . = ..() - var/obj/item/paper/paper = entry - .["author"] = paper.last_modified_ckey || "unknown" - .["message"] = paper.info || "" - .["title"] = paper.name || "paper" - var/obj/structure/noticeboard/board = entry.loc - if(istype(board)) - .["has_noticeboard"] = TRUE - .["noticeboard_direction"] = board.dir - .["noticeboard_material"] = board.material.uid + legacy_map_values = list( + "author" = nameof(/obj/item/paper::last_modified_ckey), + "message" = nameof(/obj/item/paper::info), + "title" = nameof(/obj/item/paper::name) + ) + legacy_type = /obj/item/paper /decl/persistence_handler/paper/GetAdminDataStringFor(var/thing, var/can_modify, var/mob/user) var/obj/item/paper/paper = thing diff --git a/code/modules/persistence/persistence_datum_paper_sticky.dm b/code/modules/persistence/persistence_datum_paper_sticky.dm index 4b6f849ee0b2..2eeee9e7a371 100644 --- a/code/modules/persistence/persistence_datum_paper_sticky.dm +++ b/code/modules/persistence/persistence_datum_paper_sticky.dm @@ -1,19 +1,3 @@ /decl/persistence_handler/paper/sticky name = "stickynotes" - paper_type = /obj/item/paper/sticky - -/decl/persistence_handler/paper/sticky/CreateEntryInstance(var/turf/creating, var/list/tokens) - var/atom/paper = ..() - if(paper) - paper.default_pixel_x = tokens["offset_x"] - paper.default_pixel_y = tokens["offset_y"] - paper.reset_offsets(0) - paper.color = tokens["color"] - return paper - -/decl/persistence_handler/paper/sticky/CompileEntry(var/atom/entry) - . = ..() - var/obj/item/paper/sticky/paper = entry - .["offset_x"] = paper.pixel_x - .["offset_y"] = paper.pixel_y - .["color"] = paper.color + legacy_type = /obj/item/paper/sticky diff --git a/code/modules/posture/_posture.dm b/code/modules/posture/_posture.dm index 3ed7700fd6fa..ffa26d4d0f29 100644 --- a/code/modules/posture/_posture.dm +++ b/code/modules/posture/_posture.dm @@ -14,6 +14,8 @@ var/selectable_type /// String to use in Change Posture. var/posture_change_message + /// Postural multiplier to effective blood circulation volume, a generalization of the old feature of 'laying down increases your effective blood volume'. + var/blood_volume_multiplier = 1 /decl/posture/proc/can_be_selected_by(mob/mob) return is_user_selectable diff --git a/code/modules/posture/posture_subtypes.dm b/code/modules/posture/posture_subtypes.dm index 576a53032a4f..77d53d3fa7d1 100644 --- a/code/modules/posture/posture_subtypes.dm +++ b/code/modules/posture/posture_subtypes.dm @@ -9,6 +9,7 @@ prone = TRUE posture_change_message = "lying down" selectable_type = /decl/posture/lying/deliberate + blood_volume_multiplier = 1.25 /decl/posture/lying/deliberate name = "resting" @@ -21,3 +22,4 @@ is_user_selectable = TRUE deliberate = TRUE prone = TRUE + blood_volume_multiplier = 1.1 // sitting is a little less intense than standing diff --git a/code/modules/power/apc/_apc.dm b/code/modules/power/apc/_apc.dm index 0339f9ce702d..b6721a8aa9cf 100644 --- a/code/modules/power/apc/_apc.dm +++ b/code/modules/power/apc/_apc.dm @@ -874,3 +874,15 @@ var/global/list/all_apcs = list() if(area && !(processing_flags & MACHINERY_PROCESS_SELF)) START_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF) +/obj/machinery/apc/unlocked + initial_access = list() + req_access = list() + +/obj/machinery/apc/unlocked/Initialize(mapload, ndir, populate_parts) + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/apc/unlocked/LateInitialize() + . = ..() + for(var/obj/item/stock_parts/access_lock/lock in get_all_components_of_type(/obj/item/stock_parts/access_lock)) + lock.locked = FALSE diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index b18e5bd16812..7067ecc7974b 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -418,9 +418,9 @@ var/global/list/obj/structure/cable/all_cables = list() . += C if(cable_dir & (cable_dir - 1)) // Diagonal, check for /\/\/\ style cables along cardinal directions for(var/pair in list(NORTH|SOUTH, EAST|WEST)) - T = get_step_resolving_mimic(src, cable_dir & pair) + T = get_step_resolving_mimic(src, cable_dir & pair) // move either vertically or horizontally if(T) - var/req_dir = cable_dir ^ pair + var/req_dir = cable_dir ^ pair // flip along the direction we moved, so if we're NORTHEAST we want a cable to our east that's NORTHWEST for(var/obj/structure/cable/C in T) if(C.d1 == req_dir || C.d2 == req_dir) . += C @@ -452,10 +452,10 @@ var/global/list/obj/structure/cable/all_cables = list() var/turf/T1 = loc if(!T1) return - var/list/powerlist = power_list(T1,src,0,0) //find the other cables that ended in the centre of the turf, with or without a powernet - if(powerlist.len>0) + var/obj/structure/cable/other_cable = get_matching_cable(T1, src, 0) // find a cable to start a replacement network from, if it exists + if(other_cable) var/datum/powernet/PN = new() - propagate_network(powerlist[1],PN) //propagates the new powernet beginning at the source cable + propagate_network(other_cable,PN) //propagates the new powernet beginning at the source cable if(PN.is_empty()) //can happen with machines made nodeless when smoothing cables qdel(PN) @@ -463,16 +463,16 @@ var/global/list/obj/structure/cable/all_cables = list() // cut the cable's powernet at this cable and updates the powergrid /obj/structure/cable/proc/cut_cable_from_powernet() var/turf/T1 = loc - var/list/P_list + var/obj/structure/cable/other_cable if(!T1) return if(d1) T1 = get_zstep_resolving_mimic(T1, d1) - P_list = power_list(T1, src, turn(d1,180),0,cable_only = 1) // what adjacently joins on to cut cable... + other_cable = get_matching_cable(T1, src, d1) // check our adjacent turf for connecting cables first + if(!other_cable) + other_cable = get_matching_cable(loc, src, d1) // and fall back to our own turf if we don't find one - P_list += power_list(loc, src, d1, 0, cable_only = 1)//... and on turf - - if(P_list.len == 0)//if nothing in both list, then the cable was a lone cable, just delete it and its powernet + if(!other_cable) // if we didn't find another cable, then the cable was a lone cable, just delete it and its powernet powernet.remove_cable(src) for(var/obj/machinery/power/P in T1)//check if it was powering a machine @@ -485,7 +485,7 @@ var/global/list/obj/structure/cable/all_cables = list() powernet.remove_cable(src) //remove the cut cable from its powernet var/datum/powernet/newPN = new()// creates a new powernet... - propagate_network(P_list[1], newPN)//... and propagates it to the other side of the cable + propagate_network(other_cable, newPN)//... and propagates it to the other side of the cable // Disconnect machines connected to nodes if(d1 == 0) // if we cut a node (O-X) cable @@ -515,7 +515,8 @@ var/global/list/obj/structure/cable/all_cables = list() color = COLOR_MAROON paint_color = COLOR_MAROON desc = "A coil of wiring, suitable for both delicate electronics and heavy-duty power supply." - singular_name = "length" + singular_name = "length of cable" + plural_name = "lengths of cable" w_class = ITEM_SIZE_NORMAL throw_speed = 2 throw_range = 5 @@ -620,11 +621,11 @@ var/global/list/obj/structure/cable/all_cables = list() if(distance > 1) return if(get_amount() == 1) - . += "\A [singular_name] of cable." + . += "\A [singular_name]." else if(get_amount() == 2) - . += "Two [plural_name] of cable." + . += "Two [plural_name]." else - . += "A coil of power cable. There are [get_amount()] [plural_name] of cable in the coil." + . += "A coil of power cable. There are [get_amount()] [plural_name] in the coil." /obj/item/stack/cable_coil/verb/make_restraint() set name = "Make Cable Restraints" @@ -634,11 +635,11 @@ var/global/list/obj/structure/cable/all_cables = list() if(ishuman(M) && !M.incapacitated()) if(!isturf(usr.loc)) return if(!src.use(15)) - to_chat(usr, SPAN_WARNING("You need at least 15 [plural_name] of cable to make restraints!")) + to_chat(usr, SPAN_WARNING("You need at least 15 [plural_name] to make restraints!")) return var/obj/item/handcuffs/cable/B = new /obj/item/handcuffs/cable(usr.loc) B.set_color(color) - to_chat(usr, SPAN_NOTICE("You wind some [plural_name] of cable together to make some restraints.")) + to_chat(usr, SPAN_NOTICE("You wind some [plural_name] together to make some restraints.")) else to_chat(usr, SPAN_NOTICE("You cannot do that.")) @@ -676,7 +677,7 @@ var/global/list/obj/structure/cable/all_cables = list() return if(get_amount() < 1) // Out of cable - to_chat(user, SPAN_WARNING("There is no [plural_name] of cable left.")) + to_chat(user, SPAN_WARNING("There is no [plural_name] left.")) return if(get_dist(F,user) > 1) // Too far @@ -696,7 +697,7 @@ var/global/list/obj/structure/cable/all_cables = list() var/end_dir = 0 if(istype(F) && F.is_open()) if(!can_use(2)) - to_chat(user, SPAN_WARNING("You don't have enough [plural_name] of cable to do this!")) + to_chat(user, SPAN_WARNING("You don't have enough [plural_name] to do this!")) return end_dir = DOWN @@ -840,6 +841,8 @@ var/global/list/obj/structure/cable/all_cables = list() ////////////////////////////// // Misc. ///////////////////////////// +/obj/item/stack/cable_coil/five + amount = 5 /obj/item/stack/cable_coil/cut item_state = "coil2" diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index dc9234516221..d9e93ff2da18 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -330,6 +330,9 @@ maxcharge = 500 w_class = ITEM_SIZE_SMALL //Perhaps unwise. +/obj/item/cell/gun/empty + charge = 0 + /obj/item/cell/gun/on_update_icon() . = ..() //Color the battery charging overlay against the percentage of the battery capacity. However the index of gradient() is set to 1, instead of 100, so we divide it by 100. Colors were chosen by the sprite artist. diff --git a/code/modules/power/debug_items.dm b/code/modules/power/debug_items.dm index dcb428ebdf8e..35a6efc12070 100644 --- a/code/modules/power/debug_items.dm +++ b/code/modules/power/debug_items.dm @@ -1,17 +1,18 @@ -/obj/machinery/power/debug_items - abstract_type = /obj/machinery/power/debug_items +/obj/machinery/debug_items + abstract_type = /obj/machinery/debug_items icon = 'icons/obj/power.dmi' icon_state = "tracker" anchored = TRUE density = TRUE var/show_extended_information = 1 // Set to 0 to disable extra information on examining (for example, when used on admin events) -/obj/machinery/power/debug_items/examined_by(mob/user) +/obj/machinery/debug_items/examined_by(mob/user) . = ..() if(show_extended_information) show_info(user) -/obj/machinery/power/debug_items/proc/show_info(var/mob/user) +/obj/machinery/debug_items/proc/show_info(var/mob/user) + var/datum/powernet/powernet = get_powernet() if(!powernet) to_chat(user, "This device is not connected to a powernet.") return @@ -25,42 +26,43 @@ // An infinite power generator. Adds energy to connected cable. -/obj/machinery/power/debug_items/infinite_generator +/obj/machinery/debug_items/infinite_generator name = "Fractal Energy Reactor" desc = "An experimental power generator" var/power_generation_rate = 1000000 -/obj/machinery/power/debug_items/infinite_generator/Process() - add_avail(power_generation_rate) +/obj/machinery/debug_items/infinite_generator/Process() + generate_power(power_generation_rate) -/obj/machinery/power/debug_items/infinite_generator/show_info(var/mob/user) +/obj/machinery/debug_items/infinite_generator/show_info(var/mob/user) ..() to_chat(user, "Generator is providing [num2text(power_generation_rate, 20)] W") // A cable powersink, without the explosion/network alarms normal powersink causes. -/obj/machinery/power/debug_items/infinite_cable_powersink +/obj/machinery/debug_items/infinite_cable_powersink name = "Null Point Core" desc = "An experimental device that disperses energy, used for grid testing purposes." var/power_usage_rate = 0 var/last_used = 0 -/obj/machinery/power/debug_items/infinite_cable_powersink/Process() - last_used = draw_power(power_usage_rate) +/obj/machinery/debug_items/infinite_cable_powersink/Process() + var/datum/powernet/powernet = get_powernet() + last_used = powernet.draw_power(power_usage_rate) -/obj/machinery/power/debug_items/infinite_cable_powersink/show_info(var/mob/user) +/obj/machinery/debug_items/infinite_cable_powersink/show_info(var/mob/user) ..() to_chat(user, "Power sink is demanding [num2text(power_usage_rate, 20)] W") to_chat(user, "[num2text(last_used, 20)] W was actually used last tick") -/obj/machinery/power/debug_items/infinite_apc_powersink +/obj/machinery/debug_items/infinite_apc_powersink name = "APC Dummy Load" desc = "A dummy load that connects to an APC, used for load testing purposes." use_power = POWER_USE_ACTIVE active_power_usage = 0 -/obj/machinery/power/debug_items/infinite_apc_powersink/show_info(var/mob/user) +/obj/machinery/debug_items/infinite_apc_powersink/show_info(var/mob/user) ..() to_chat(user, "Dummy load is using [num2text(active_power_usage, 20)] W") to_chat(user, "Powered: [!(stat & NOPOWER) ? "YES" : "NO"]") diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index ff6d5274e982..8a49627444c2 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -2,6 +2,8 @@ // POWER MACHINERY BASE CLASS // This subtype is for machinery which needs to be directly referenced by its parent powernet during powernet processing. // This subtype does not encompass all power generating machinery, or machinery that needs to draw from a powernet in general. +// If you try using this, make sure you can't just use get_powernet() instead. Ideally either the powernet needs to know about it (APCs) +// or we need to run logic when we connect or disconnect from a powernet (solar panels/trackers). ////////////////////////////// ///////////////////////////// @@ -100,30 +102,26 @@ // GLOBAL PROCS for powernets handling ////////////////////////////////////////// - -// returns a list of all power-related objects (nodes, cable, junctions) in turf, -// excluding source, that match the direction d -// if unmarked==1, only return those with no powernet -/proc/power_list(var/turf/T, var/source, var/d, var/unmarked=0, var/cable_only = 0) +/// Returns all cables in target_turf matching target_direction, excluding excluded_cable. +/// If only_no_powernet is TRUE, only cables with no powernet will be returned. +/// Unused, because get_maching_cable or get_connected_cables is usually preferable, but kept just in case. +/proc/cable_list(var/turf/target_turf, var/obj/structure/cable/excluded_cable = null, var/target_direction) . = list() - - var/reverse = d ? global.reverse_dir[d] : 0 - for(var/AM in T) - if(AM == source) continue //we don't want to return source - - if(!cable_only && istype(AM,/obj/machinery/power)) - var/obj/machinery/power/P = AM - if(!unmarked || !P.powernet) //if unmarked=1 we only return things with no powernet - if(d == 0) - . += P - - else if(istype(AM,/obj/structure/cable)) - var/obj/structure/cable/C = AM - - if(!unmarked || !C.powernet) - if(C.d1 == d || C.d2 == d || C.d1 == reverse || C.d2 == reverse ) - . += C - return . + var/reverse_direction = target_direction ? global.reverse_dir[target_direction] : 0 + for(var/obj/structure/cable/other_cable in target_turf) + if(other_cable == excluded_cable) + continue + if(other_cable.d1 == target_direction || other_cable.d2 == target_direction || other_cable.d1 == reverse_direction || other_cable.d2 == reverse_direction) + . += other_cable + +/// Like cable_list, but only returns the first cable, since that's all most uses of it check. +/proc/get_matching_cable(var/turf/target_turf, var/obj/structure/cable/excluded_cable = null, var/target_direction) + var/reverse_direction = target_direction ? global.reverse_dir[target_direction] : 0 + for(var/obj/structure/cable/other_cable in target_turf) + if(other_cable == excluded_cable) + continue + if(other_cable.d1 == target_direction || other_cable.d2 == target_direction || other_cable.d1 == reverse_direction || other_cable.d2 == reverse_direction) + return other_cable //remove the old powernet and replace it with a new one throughout the network. /proc/propagate_network(var/obj/structure/cable/cable, var/datum/powernet/PN) diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index 6220dc117d20..6727118ba2f8 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -101,10 +101,9 @@ if(problem > 0) problem = max(problem - 1, 0) - if(LAZYLEN(nodes)) // Added to fix a bad list bug -- TLE - for(var/obj/machinery/power/terminal/term in nodes) - if( istype( term.master_machine(), /obj/machinery/apc ) ) - numapc++ + for(var/obj/machinery/power/terminal/term in nodes) + if( istype( term.master_machine(), /obj/machinery/apc ) ) + numapc++ netexcess = avail - load diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index f85c32b16eb0..cd70fdbc44a5 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -4,14 +4,14 @@ var/global/list/rad_collectors = list() /obj/machinery/rad_collector name = "radiation collector array" - desc = "A device which uses radiation and hydrogen to produce power." + desc = "A device which uses radiation and a reactant to produce power." icon = 'icons/obj/machines/rad_collector.dmi' icon_state = "ca" anchored = FALSE density = TRUE initial_access = list(access_engine_equip) max_health = 100 - var/obj/item/tank/hydrogen/loaded_tank = null + var/obj/item/tank/loaded_tank = null var/max_safe_temp = 1000 + T0C var/melted @@ -80,22 +80,22 @@ var/global/list/rad_collectors = list() return FALSE . = TRUE if((stat & BROKEN) || melted) - to_chat(user, "\The [src] is completely destroyed!") + to_chat(user, SPAN_WARNING("\The [src] is completely destroyed!")) if(!src.locked) toggle_power() user.visible_message("[user.name] turns \the [src] [active? "on":"off"].", \ "You turn \the [src] [active? "on":"off"].") investigate_log("turned [active?"on":"off"] by [user.key]. [loaded_tank?"Fuel: [round(loaded_tank.air_contents.gas[/decl/material/gas/hydrogen]/0.29)]%":"It is empty"].","singulo") else - to_chat(user, "The controls are locked!") + to_chat(user, SPAN_WARNING("The controls are locked!")) /obj/machinery/rad_collector/attackby(obj/item/used_item, mob/user) - if(istype(used_item, /obj/item/tank/hydrogen)) + if(istype(used_item, /obj/item/tank)) if(!src.anchored) - to_chat(user, "\The [src] needs to be secured to the floor first.") + to_chat(user, SPAN_WARNING("\The [src] needs to be secured to the floor first.")) return TRUE if(src.loaded_tank) - to_chat(user, "There's already a tank loaded.") + to_chat(user, SPAN_WARNING("There's already a tank loaded.")) return TRUE if(!user.try_unequip(used_item, src)) return TRUE @@ -108,11 +108,11 @@ var/global/list/rad_collectors = list() return TRUE else if(IS_WRENCH(used_item)) if(loaded_tank) - to_chat(user, "Remove the tank first.") + to_chat(user, SPAN_NOTICE("Remove the tank first.")) return TRUE for(var/obj/machinery/rad_collector/R in get_turf(src)) if(R != src) - to_chat(user, "You cannot install more than one collector on the same spot.") + to_chat(user, SPAN_WARNING("You cannot install more than one collector on the same spot.")) return TRUE playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) src.anchored = !src.anchored @@ -129,7 +129,7 @@ var/global/list/rad_collectors = list() src.locked = 0 //just in case it somehow gets locked to_chat(user, SPAN_WARNING("The controls can only be locked when \the [src] is active.")) else - to_chat(user, "Access denied!") + to_chat(user, SPAN_WARNING("Access denied!")) return TRUE return ..() @@ -164,12 +164,11 @@ var/global/list/rad_collectors = list() /obj/machinery/rad_collector/proc/eject() locked = 0 - var/obj/item/tank/hydrogen/Z = src.loaded_tank - if (!Z) + if (!loaded_tank) return - Z.dropInto(loc) - Z.reset_plane_and_layer() - src.loaded_tank = null + loaded_tank.dropInto(loc) + loaded_tank.reset_plane_and_layer() + loaded_tank = null if(active) toggle_power() else diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index e76803494d6a..29c458486a8b 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -194,7 +194,7 @@ var/global/list/solars_list = list() // On planets, we take fewer steps because the light is mostly up // Also, many planets barely have any spots with enough clear space around if(isturf(loc)) - var/obj/effect/overmap/visitable/sector/planetoid/E = overmap_sectors[num2text(loc.z)] + var/obj/effect/overmap/visitable/sector/planetoid/E = global.overmap_sectors[loc.z] if(istype(E)) steps = 5 diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 91c1085e4327..13b739cf041a 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -46,6 +46,8 @@ pickup_sound = 'sound/foley/pickup2.ogg' can_be_twohanded = TRUE // also checks one_hand_penalty needs_attack_dexterity = DEXTERITY_WEAPONS + wieldsound = 'sound/weapons/TargetOn.ogg' + unwieldsound = 'sound/weapons/TargetOff.ogg' var/fire_verb = "fire" var/waterproof = FALSE @@ -94,6 +96,7 @@ /obj/item/gun/Initialize() // must have firemodes initialized prior to any update_icon_calls // including reconsider_single_icon(), which is done in ..() + LAZYINITLIST(firemodes) for(var/i in 1 to firemodes.len) firemodes[i] = new /datum/firemode(src, firemodes[i]) . = ..() @@ -544,7 +547,7 @@ shot_sound = P.fire_sound shot_sound_vol = P.fire_sound_vol if(silencer) - shot_sound_vol = 10 + shot_sound_vol = P.fire_sound_vol_silenced playsound(firer, shot_sound, shot_sound_vol, 1) diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index f7d2efa622be..7671a3a759ca 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -5,7 +5,7 @@ var/global/list/registered_cyborg_weapons = list() name = "energy gun" desc = "A basic energy-based gun." icon = 'icons/obj/guns/basic_energy.dmi' - icon_state = "energy" + icon_state = ICON_STATE_WORLD fire_sound = 'sound/weapons/Taser.ogg' fire_sound_text = "laser blast" accuracy = 1 diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index cf4b659eb7e1..a9af3a3c1e46 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -54,18 +54,29 @@ desc += " The optical pathway is melted and useless." projectile_type = null -/obj/item/gun/energy/captain - name = "antique laser gun" +/obj/item/gun/energy/retro + name = "retro laser pistol" icon = 'icons/obj/guns/caplaser.dmi' + desc = "A now-obsolete handheld laser weapon, still popular with some for ease of maintenance." icon_state = ICON_STATE_WORLD - desc = "A rare weapon, handcrafted by a now defunct specialty manufacturer on Luna for a small fortune. It's certainly aged well." - slot_flags = SLOT_LOWER_BODY //too unusually shaped to fit in a holster w_class = ITEM_SIZE_NORMAL projectile_type = /obj/item/projectile/beam origin_tech = null max_shots = 5 //to compensate a bit for self-recharging one_hand_penalty = 1 //a little bulky + +/obj/item/gun/energy/retro/empty/Initialize(ml, material_key) + . = ..() + var/obj/item/cell/cell = get_cell() + if(cell) + cell.charge = 0 + update_icon() + +/obj/item/gun/energy/retro/captain + name = "antique laser gun" + desc = "A rare weapon, handcrafted by a now defunct specialty manufacturer on Luna for a small fortune. It's certainly aged well." self_recharge = 1 + slot_flags = SLOT_LOWER_BODY //too unusually shaped to fit in a holster /obj/item/gun/energy/lasercannon name = "laser cannon" diff --git a/code/modules/projectiles/guns/launcher/grenade_launcher.dm b/code/modules/projectiles/guns/launcher/grenade_launcher.dm index 1e1e7f0cda25..2fc0015f2667 100644 --- a/code/modules/projectiles/guns/launcher/grenade_launcher.dm +++ b/code/modules/projectiles/guns/launcher/grenade_launcher.dm @@ -108,9 +108,38 @@ return FALSE return TRUE -// For uplink purchase, comes loaded with a random assortment of grenades +/obj/item/gun/launcher/grenade/loaded + var/initial_load_type + /obj/item/gun/launcher/grenade/loaded/Initialize() . = ..() + if(initial_load_type) + chambered = new initial_load_type(src) + LAZYINITLIST(grenades) + for(var/i = 1 to max_grenades) + grenades += new initial_load_type(src) + +/obj/item/gun/launcher/grenade/loaded/anti_photon + initial_load_type = /obj/item/grenade/anti_photon + +/obj/item/gun/launcher/grenade/loaded/smoke + initial_load_type = /obj/item/grenade/smokebomb + +/obj/item/gun/launcher/grenade/loaded/teargas + initial_load_type = /obj/item/grenade/chem_grenade/teargas + +/obj/item/gun/launcher/grenade/loaded/flashbang + initial_load_type = /obj/item/grenade/flashbang + +/obj/item/gun/launcher/grenade/loaded/emp + initial_load_type = /obj/item/grenade/empgrenade + +/obj/item/gun/launcher/grenade/loaded/frag + initial_load_type = /obj/item/grenade/frag/shell + +// For uplink purchase, comes loaded with a random assortment of grenades +/obj/item/gun/launcher/grenade/random/Initialize() + . = ..() var/list/grenade_types = list( /obj/item/grenade/anti_photon = 2, diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 4feed2fd06c9..d36ec1783cc9 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -25,6 +25,10 @@ var/proj_trail_icon_state = "trail" /// Any extant trail effects. var/list/proj_trails + /// An effect to spawn when a non-hitscan projectile collides with a target. + var/impact_effect_type + /// A sound to play when striking a non-mob (hitsound is used for mobs) + var/hitsound_non_mob var/bumped = 0 //Prevents it from hitting more than one guy at once var/def_zone = "" //Aiming at @@ -73,6 +77,7 @@ var/fire_sound var/fire_sound_vol = 50 + var/fire_sound_vol_silenced = 10 var/miss_sounds var/ricochet_sounds var/list/impact_sounds //for different categories, IMPACT_MEAT etc @@ -131,6 +136,10 @@ //called when the projectile stops flying because it collided with something /obj/item/projectile/proc/on_impact(var/atom/A) + + impact_sounds(A) + impact_visuals(A) + if(damage && atom_damage_type == BURN) var/turf/T = get_turf(A) if(T) @@ -208,6 +217,7 @@ //Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying. /obj/item/projectile/proc/attack_mob(var/mob/living/target_mob, var/distance, var/special_miss_modifier=0) + SHOULD_CALL_PARENT(TRUE) if(!istype(target_mob)) return @@ -234,13 +244,18 @@ if(result == PROJECTILE_FORCE_MISS) if(!silenced) target_mob.visible_message("\The [src] misses [target_mob] narrowly!") + var/list/miss_sounds = get_miss_sounds() if(LAZYLEN(miss_sounds)) playsound(target_mob.loc, pick(miss_sounds), 60, 1) return FALSE //hit messages if(silenced) - to_chat(target_mob, "You've been hit in the [parse_zone(def_zone)] by \the [src]!") + to_chat(target_mob, SPAN_DANGER("You've been hit in the [parse_zone(def_zone)] by \the [src]!")) + if(hitsound) + var/impact_volume = get_impact_volume_by_damage() + if(impact_volume) + playsound(target_mob, hitsound, impact_volume, 1, -1) else target_mob.visible_message("\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter @@ -672,3 +687,61 @@ /obj/item/projectile/proc/update_effect(var/obj/effect/projectile/effect) return + +/obj/item/projectile/proc/get_projectile_damage(mob/living/target) + return damage + +// Makes a brief effect sprite appear when the projectile hits something solid. +/obj/item/projectile/proc/impact_visuals(atom/A, hit_x, hit_y) + // Hitscan things have their own impact sprite. + if(!impact_effect_type || hitscan) + return + if(isnull(hit_x) && isnull(hit_y)) + if(trajectory) + // Effect goes where the projectile 'stopped'. + hit_x = A.pixel_x + trajectory.return_px() + hit_y = A.pixel_y + trajectory.return_py() + else if(A == original) + // Otherwise it goes where the person who fired clicked. + hit_x = A.pixel_x + p_x - 16 + hit_y = A.pixel_y + p_y - 16 + else + // Otherwise it'll be random. + hit_x = A.pixel_x + rand(-8, 8) + hit_y = A.pixel_y + rand(-8, 8) + new impact_effect_type(get_turf(A), src, hit_x, hit_y) + +/obj/item/projectile/proc/get_impact_volume_by_damage() + if(damage || agony) + var/value_to_use = damage > agony ? damage : agony + // Multiply projectile damage by 1.2, then CLAMP the value between 30 and 100. + // This was 0.67 but in practice it made all projectiles that did 45 or less damage play at 30, + // which is hard to hear over the gunshots, and is rather rare for a projectile to do that much. + return clamp((value_to_use) * 1.2, 30, 100) + return 50 //if the projectile doesn't do damage or agony, play its hitsound at 50% volume. + +/obj/item/projectile/proc/impact_sounds(atom/A) + + var/play_volume = clamp(get_impact_volume_by_damage() + 20, 0, 100) + if(play_volume <= 0) + return + if(silenced) + play_volume = min(play_volume, 5) + + var/play_sound + if(ismob(A)) // Mob sounds are handled in attack_mob(). + play_sound = hitsound + else + play_sound = hitsound_non_mob + if(!play_sound) + return + playsound(A, play_sound, play_volume, 1, -1) + +/obj/item/projectile/proc/get_miss_sounds() + return + +/obj/item/projectile/proc/get_ricochet_sounds() + return + +/obj/item/projectile/proc/get_impact_sounds() + return diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 8df181e9195e..1c8b74eeb566 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -3,7 +3,6 @@ icon_state = "laser" temperature = T0C + 300 fire_sound='sound/weapons/Laser.ogg' - impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_LASER_MEAT, BULLET_IMPACT_METAL = SOUNDS_LASER_METAL) pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GLASS | PASS_FLAG_GRILLE damage = 40 atom_damage_type = BURN @@ -14,11 +13,26 @@ invisibility = INVISIBILITY_ABSTRACT //beam projectiles are invisible as they are rendered by the effect engine penetration_modifier = 0.3 distance_falloff = 2.5 + hitsound = 'sound/weapons/sear.ogg' + hitsound_non_mob = 'sound/weapons/searwall.ogg' muzzle_type = /obj/effect/projectile/muzzle/laser tracer_type = /obj/effect/projectile/tracer/laser impact_type = /obj/effect/projectile/impact/laser +/obj/item/projectile/beam/get_impact_sounds() + var/static/list/impact_sounds = list( + (BULLET_IMPACT_MEAT) = SOUNDS_LASER_MEAT, + (BULLET_IMPACT_METAL) = SOUNDS_LASER_METAL + ) + return impact_sounds + +/obj/item/projectile/beam/blue + damage = 30 + muzzle_type = /obj/effect/projectile/muzzle/laser/blue + tracer_type = /obj/effect/projectile/tracer/laser/blue + impact_type = /obj/effect/projectile/impact/laser/blue + /obj/item/projectile/beam/megabot damage = 45 distance_falloff = 0.5 diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index b80940577088..e94f5926a344 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -9,14 +9,31 @@ embed = 1 space_knockback = 1 penetration_modifier = 1.0 + impact_effect_type = /obj/effect/temp_visual/impact_effect + muzzle_type = /obj/effect/projectile/muzzle/bullet + hitsound_non_mob = "ricochet" + var/mob_passthrough_check = 0 var/caliber - muzzle_type = /obj/effect/projectile/muzzle/bullet - miss_sounds = list('sound/weapons/guns/miss1.ogg','sound/weapons/guns/miss2.ogg','sound/weapons/guns/miss3.ogg','sound/weapons/guns/miss4.ogg') - ricochet_sounds = list('sound/weapons/guns/ricochet1.ogg', 'sound/weapons/guns/ricochet2.ogg', - 'sound/weapons/guns/ricochet3.ogg', 'sound/weapons/guns/ricochet4.ogg') - impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_BULLET_MEAT, BULLET_IMPACT_METAL = SOUNDS_BULLET_METAL) +/obj/item/projectile/bullet/get_miss_sounds() + var/static/list/miss_sounds = list( + 'sound/weapons/guns/miss1.ogg', + 'sound/weapons/guns/miss2.ogg', + 'sound/weapons/guns/miss3.ogg', + 'sound/weapons/guns/miss4.ogg' + ) + +/obj/item/projectile/bullet/get_ricochet_sounds() + return global.ricochet_sound + +/obj/item/projectile/bullet/get_impact_sounds() + + var/static/list/impact_sounds = list( + (BULLET_IMPACT_MEAT) = SOUNDS_BULLET_MEAT, + (BULLET_IMPACT_METAL) = SOUNDS_BULLET_METAL + ) + return impact_sounds /obj/item/projectile/bullet/get_autopsy_descriptors() . = ..() @@ -34,7 +51,6 @@ else mob_passthrough_check = 0 . = ..() - if(. == 1 && isliving(target_mob)) var/mob/living/squish = target_mob if(!squish.isSynthetic()) diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index eb0cc1835e48..e08ffc8d1f01 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -6,6 +6,9 @@ atom_damage_type = BURN damage_flags = 0 distance_falloff = 2.5 + impact_effect_type = /obj/effect/temp_visual/impact_effect + hitsound_non_mob = 'sound/weapons/searwall.ogg' + hitsound = 'sound/weapons/zapbang.ogg' //releases a burst of light on impact or after travelling a distance /obj/item/projectile/energy/flash @@ -16,6 +19,7 @@ agony = 20 life_span = 15 //if the shell hasn't hit anything after travelling this far it just explodes. muzzle_type = /obj/effect/projectile/muzzle/bullet + hitsound_non_mob = null var/flash_range = 1 var/brightness = 7 var/light_flash_color = COLOR_WHITE @@ -106,6 +110,7 @@ damage = 30 atom_damage_type = CLONE irradiate = 40 + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/dart name = "dart" @@ -142,6 +147,7 @@ damage = 20 atom_damage_type = TOX irradiate = 20 + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/plasmastun name = "plasma pulse" diff --git a/code/modules/projectiles/projectile/fireball.dm b/code/modules/projectiles/projectile/fireball.dm new file mode 100644 index 000000000000..6221b8bf1348 --- /dev/null +++ b/code/modules/projectiles/projectile/fireball.dm @@ -0,0 +1,45 @@ +/obj/item/projectile/fireball + name = "fireball" + icon_state = "fireball" + fire_sound = 'sound/effects/bamf.ogg' + damage = 20 + atom_damage_type = BURN + damage_flags = DAM_DISPERSED // burn all over + var/fire_lifetime = 2 SECONDS + var/fire_temperature = (288 CELSIUS) / 0.9 + 1 // hot enough to ignite wood! divided by 0.9 and plus one to ensure we can light firepits + +/obj/effect/fake_fire/variable + name = "fire" + anchored = TRUE + mouse_opacity = MOUSE_OPACITY_UNCLICKABLE + firelevel = 1 + pressure = ONE_ATMOSPHERE + +/obj/effect/fake_fire/variable/Initialize(ml, new_temperature, new_lifetime) + lifetime = new_lifetime + last_temperature = new_temperature + return ..() + +// we deal our damage via fire_act, not via direct burn damage. our burn damage is specifically for mobs +/obj/item/projectile/fireball/get_structure_damage() + return 0 + +/obj/item/projectile/fireball/on_impact(var/atom/A) + . = ..() + var/obj/effect/fake_fire/fire = new /obj/effect/fake_fire/variable(get_turf(A), fire_temperature, fire_lifetime) + fire.Process() // process at least once! + qdel_self() + +/obj/item/projectile/fireball/after_move() + . = ..() + if(!loc) + return + for(var/mob/living/victim in loc) + if(!victim.simulated) + continue + victim.FireBurn(1, fire_temperature, ONE_ATMOSPHERE) + loc.fire_act(1, fire_temperature, ONE_ATMOSPHERE) + for(var/atom/burned in loc) + if(!burned.simulated || burned == src) + continue + burned.fire_act(1, fire_temperature, ONE_ATMOSPHERE) diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/force.dm index a3c2e748d0b9..e0b8ea2745f9 100644 --- a/code/modules/projectiles/projectile/force.dm +++ b/code/modules/projectiles/projectile/force.dm @@ -5,6 +5,8 @@ damage = 20 atom_damage_type = BURN damage_flags = 0 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_non_mob = 'sound/weapons/searwall.ogg' /obj/item/projectile/forcebolt/strong name = "force bolt" diff --git a/code/modules/projectiles/projectile/pellets.dm b/code/modules/projectiles/projectile/pellets.dm index c4a244c802ac..5c19804a9c2f 100644 --- a/code/modules/projectiles/projectile/pellets.dm +++ b/code/modules/projectiles/projectile/pellets.dm @@ -17,7 +17,9 @@ return max(pellets - pellet_loss, 1) /obj/item/projectile/bullet/pellet/attack_mob(var/mob/target_mob, var/distance, var/miss_modifier) - if (pellets < 0) return 1 + SHOULD_CALL_PARENT(FALSE) + if (pellets < 0) + return 1 var/total_pellets = get_pellets(distance) var/spread = max(base_spread - (spread_step*distance), 0) @@ -39,7 +41,9 @@ if (..()) hits++ def_zone = old_zone //restore the original zone the projectile was aimed at - pellets -= hits //each hit reduces the number of pellets left + if(hits > 0) + pellets -= hits //each hit reduces the number of pellets left + if (hits >= total_pellets || pellets <= 0) return 1 return 0 diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index d648f6d70c3f..d7ee296a8f14 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -6,6 +6,9 @@ atom_damage_type = BURN damage_flags = 0 nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/ion + hitsound_non_mob = 'sound/weapons/searwall.ogg' + hitsound = 'sound/weapons/ionrifle.ogg' var/heavy_effect_range = 1 var/light_effect_range = 2 @@ -51,6 +54,7 @@ atom_damage_type = BURN damage_flags = 0 nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser var/firing_temperature = 300 /obj/item/projectile/temp/on_hit(var/atom/target, var/blocked = 0)//These two could likely check temp protection on the mob @@ -88,6 +92,7 @@ damage = 0 atom_damage_type = TOX nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/floramut/on_hit(var/atom/target, var/blocked = 0) if(!isliving(target)) @@ -131,6 +136,7 @@ damage = 0 atom_damage_type = TOX nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) if(!isliving(target)) diff --git a/code/modules/projectiles/projectile/trace.dm b/code/modules/projectiles/projectile/trace.dm index a7e1f58c34fa..6abad14dbe3d 100644 --- a/code/modules/projectiles/projectile/trace.dm +++ b/code/modules/projectiles/projectile/trace.dm @@ -32,4 +32,5 @@ return ..() /obj/item/projectile/test/attack_mob() - return \ No newline at end of file + SHOULD_CALL_PARENT(FALSE) + return diff --git a/code/modules/random_map/automata/automata.dm b/code/modules/random_map/automata/automata.dm index 544b625a331e..3e45c90f4fc3 100644 --- a/code/modules/random_map/automata/automata.dm +++ b/code/modules/random_map/automata/automata.dm @@ -11,58 +11,68 @@ var/cell_threshold = 5 // Cell becomes alive with this many live neighbors. // Automata-specific procs and processing. -/datum/random_map/automata/generate_map() - for(var/iter = 1 to iterations) - var/list/next_map[limit_x*limit_y] - var/count - var/is_not_border_left - var/is_not_border_right - var/ilim_u - var/ilim_d - var/bottom_lim = ((limit_y - 1) * limit_x) - - if (!islist(map)) - set_map_size() - - for (var/i in 1 to (limit_x * limit_y)) - count = 0 - - is_not_border_left = i != 1 && ((i - 1) % limit_x) - is_not_border_right = i % limit_x +// we make a map slightly larger than we need in order to avoid needing to check edges +/datum/random_map/automata/New(var/tx, var/ty, var/tz, var/tlx, var/tly, var/do_not_apply, var/do_not_announce, var/used_area) + ..(tx, ty, tz, tlx+2, tly+2, do_not_apply, do_not_announce, used_area) - if (CELL_ALIVE(map[i])) // Center row. - ++count - if (is_not_border_left && CELL_ALIVE(map[i - 1])) - ++count - if (is_not_border_right && CELL_ALIVE(map[i + 1])) - ++count +/datum/random_map/automata/seed_map() + // we skip the edges here because they're just for indexing purposes + for(var/x in 2 to limit_x - 1) + for(var/y in 2 to limit_y - 1) + var/current_cell = TRANSLATE_COORD(x,y) + if(prob(initial_wall_cell)) + map[current_cell] = WALL_CHAR + else + map[current_cell] = initial_cell_char - if (i > limit_x) // top row - ilim_u = i - limit_x - if (CELL_ALIVE(map[ilim_u])) - ++count - if (is_not_border_left && CELL_ALIVE(map[ilim_u - 1])) - ++count - if (is_not_border_right && CELL_ALIVE(map[ilim_u + 1])) - ++count - - if (i <= bottom_lim) // bottom row - ilim_d = i + limit_x - if (CELL_ALIVE(map[ilim_d])) - ++count - if (is_not_border_left && CELL_ALIVE(map[ilim_d - 1])) - ++count - if (is_not_border_right && CELL_ALIVE(map[ilim_d + 1])) - ++count - - if(count >= cell_threshold) - REVIVE_CELL(i, next_map) - else // Nope. Can't be alive. Kill it. - KILL_CELL(i, next_map) +/datum/random_map/automata/apply_to_map() + if(!origin_x) origin_x = 1 + if(!origin_y) origin_y = 1 + if(!origin_z) origin_z = 1 + // adjust for automata map bounds weirdness + // this means that x=2 will be origin_x, which is good + // and that we only apply 2 to n-1, which is also good + origin_x -= 1 + origin_y -= 1 + for(var/x in 2 to limit_x-1) + for(var/y in 2 to limit_y-1) CHECK_TICK + apply_to_turf(x,y) +/datum/random_map/automata/generate_map() + var/list/map = src.map + // Instead of allocating a new next_map every iteration, + // we just flip the next_map and map lists. + var/list/next_map = new /list(length(map)) + var/temp // used to swap the maps + // do a running count to save on repeated accesses + // this reduces us from 9 checks per tile to just 3 + var/bottom_count = 0 + var/middle_count = 0 + var/top_count = 0 + for(var/iter = 1 to iterations) + // we have a 1 tile buffer on both sides so go from 2 to lim-1 + for (var/x in 2 to limit_x - 1) + bottom_count = 0 + middle_count = CELL_ALIVE(map[TRANSLATE_COORD(x-1, 1)]) + CELL_ALIVE(map[TRANSLATE_COORD(x, 1)]) + CELL_ALIVE(map[TRANSLATE_COORD(x+1, 1)]) + top_count = CELL_ALIVE(map[TRANSLATE_COORD(x-1, 2)]) + CELL_ALIVE(map[TRANSLATE_COORD(x, 2)]) + CELL_ALIVE(map[TRANSLATE_COORD(x+1, 2)]) + for (var/y in 2 to limit_y - 1) + var/i = TRANSLATE_COORD(x, y) + // shift everything down a row + bottom_count = middle_count + middle_count = top_count + top_count = CELL_ALIVE(map[i + limit_x - 1]) + CELL_ALIVE(map[i + limit_x]) + CELL_ALIVE(map[i + limit_x + 1]) + if((bottom_count + middle_count + top_count) >= cell_threshold) + REVIVE_CELL(i, next_map) + else // Nope. Can't be alive. Kill it. + KILL_CELL(i, next_map) + CHECK_TICK + // end iteration + temp = map // save this to use as our next slate map = next_map + next_map = temp // restore our next_map slate + src.map = map /datum/random_map/automata/get_additional_spawns(value, turf/T) return diff --git a/code/modules/random_map/dungeon/predefined.dm b/code/modules/random_map/dungeon/predefined.dm index c6b536cc2e54..91b22c5c42eb 100644 --- a/code/modules/random_map/dungeon/predefined.dm +++ b/code/modules/random_map/dungeon/predefined.dm @@ -5,7 +5,7 @@ room_theme_rare = list(/datum/room_theme/metal = 1, /datum/room_theme = 3, /datum/room_theme/metal/secure = 1) monsters_common = list(/mob/living/simple_animal/hostile/carp = 50, /mob/living/simple_animal/hostile/carp/pike = 1) - monsters_uncommon = list(/mob/living/simple_animal/hostile/hivebot = 10, /mob/living/simple_animal/hostile/hivebot/strong = 1) + monsters_uncommon = list(/mob/living/simple_animal/hostile/hivebot = 10, /mob/living/simple_animal/hostile/hivebot/melee/armored = 1) /datum/random_map/winding_dungeon/premade/New(var/tx, var/ty, var/tz, var/tlx, var/tly, var/do_not_apply, var/do_not_announce, var/used_area) loot_common = subtypesof(/obj/item/energy_blade/sword) + subtypesof(/obj/item/baton) + subtypesof(/obj/item/food) + subtypesof(/obj/item/chems/condiment) + subtypesof(/obj/item/chems/drinks) diff --git a/code/modules/random_map/noise/noise.dm b/code/modules/random_map/noise/noise.dm index 15f6277f693a..91667bc86faf 100644 --- a/code/modules/random_map/noise/noise.dm +++ b/code/modules/random_map/noise/noise.dm @@ -152,6 +152,7 @@ if(has_neighbor_with_path(x, y, get_appropriate_path(map[mapcell]), TRUE)) continue map[mapcell] = map[pick(get_neighbors(x, y, TRUE))] + CHECK_TICK #define CHECK_NEIGHBOR_FOR_PATH(X, Y) \ TRANSLATE_AND_VERIFY_COORD(X,Y);\ diff --git a/code/modules/random_map/random_map.dm b/code/modules/random_map/random_map.dm index ad1d3c26c0ec..10c390dfcd47 100644 --- a/code/modules/random_map/random_map.dm +++ b/code/modules/random_map/random_map.dm @@ -105,8 +105,8 @@ var/global/list/map_count = list() user = world var/dat = list("+------+
") - for(var/x = 1, x <= limit_x, x++) - for(var/y = 1, y <= limit_y, y++) + for(var/x in 1 to limit_x) + for(var/y in 1 to limit_y) var/current_cell = TRANSLATE_COORD(x,y) if(current_cell) dat += get_map_char(map[current_cell]) @@ -115,12 +115,11 @@ var/global/list/map_count = list() to_chat(user, JOINTEXT(dat)) /datum/random_map/proc/set_map_size() - map = list() - map.len = limit_x * limit_y + map = new /list(limit_x * limit_y) /datum/random_map/proc/seed_map() - for(var/x = 1, x <= limit_x, x++) - for(var/y = 1, y <= limit_y, y++) + for(var/x in 1 to limit_x) + for(var/y in 1 to limit_y) var/current_cell = TRANSLATE_COORD(x,y) if(prob(initial_wall_cell)) map[current_cell] = WALL_CHAR @@ -128,8 +127,8 @@ var/global/list/map_count = list() map[current_cell] = initial_cell_char /datum/random_map/proc/clear_map() - for(var/x = 1, x <= limit_x, x++) - for(var/y = 1, y <= limit_y, y++) + for(var/x in 1 to limit_x) + for(var/y in 1 to limit_y) map[TRANSLATE_COORD(x,y)] = 0 /datum/random_map/proc/generate() @@ -160,9 +159,8 @@ var/global/list/map_count = list() if(!origin_x) origin_x = 1 if(!origin_y) origin_y = 1 if(!origin_z) origin_z = 1 - - for(var/x = 1, x <= limit_x, x++) - for(var/y = 1, y <= limit_y, y++) + for(var/x in 1 to limit_x) + for(var/y in 1 to limit_y) CHECK_TICK apply_to_turf(x,y) @@ -200,8 +198,8 @@ var/global/list/map_count = list() return tx-- // Update origin so that x/y index ty-- // doesn't push it off-kilter by one. - for(var/x = 1, x <= limit_x, x++) - for(var/y = 1, y <= limit_y, y++) + for(var/x in 1 to limit_x) + for(var/y in 1 to limit_y) var/current_cell = TRANSLATE_COORD(x,y) if(!current_cell) continue diff --git a/code/modules/reagents/Chemistry-Grinder.dm b/code/modules/reagents/Chemistry-Grinder.dm index 1c4f5c85190b..d71b5f24b7e6 100644 --- a/code/modules/reagents/Chemistry-Grinder.dm +++ b/code/modules/reagents/Chemistry-Grinder.dm @@ -17,7 +17,7 @@ var/list/bag_whitelist = list( /obj/item/pill_bottle, - /obj/item/plants + /obj/item/plant_satchel ) var/blacklisted_types = list() var/item_size_limit = ITEM_SIZE_HUGE @@ -265,7 +265,7 @@ obj_flags = null grind_sound = 'sound/machines/juicer.ogg' blacklisted_types = list(/obj/item/stack/material) - bag_whitelist = list(/obj/item/plants) + bag_whitelist = list(/obj/item/plant_satchel) item_size_limit = ITEM_SIZE_SMALL skill_to_check = SKILL_COOKING diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 629091fd1096..961da084398e 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -10,6 +10,16 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new /atom/proc/remove_any_reagents(amount = 1, defer_update = FALSE, removed_phases = (MAT_PHASE_LIQUID | MAT_PHASE_SOLID), skip_reagents = null) return reagents?.remove_any(amount, defer_update, removed_phases, skip_reagents) +/// Adds reagents, but contaminated. A fraction of `amount` is replaced with `contaminant_type` according to `contaminant_proportion`. +/// Handles null contaminant_type and zero contaminant_proportion, but it's probably faster to check before you call this. +/atom/proc/add_to_reagents_contaminated(reagent_type, amount, data, contaminant_type = null, contaminant_proportion = 0, safety = FALSE, defer_update = FALSE, phase = null) + var/contaminant_to_add = 0 + if(contaminant_type) + contaminant_to_add = CHEMS_QUANTIZE(amount * contaminant_proportion) + add_to_reagents(reagent_type, amount - contaminant_to_add, data, safety = safety, defer_update = !!contaminant_to_add, phase = MAT_PHASE_LIQUID) + if(contaminant_to_add) + add_to_reagents(contaminant_type, contaminant_to_add, phase = MAT_PHASE_LIQUID) + /atom/proc/get_reagent_space() if(!REAGENT_MAXIMUM_VOLUME(reagents)) return 0 @@ -63,13 +73,18 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new VAR_PRIVATE/list/solid_volumes // This should be taken as powders/flakes, rather than large solid pieces of material. VAR_PRIVATE/list/reagent_data VAR_PRIVATE/atom/my_atom - VAR_PRIVATE/cached_color - VAR_PRIVATE/primary_reagent - VAR_PRIVATE/primary_solid - VAR_PRIVATE/primary_liquid - VAR_PRIVATE/total_volume = 0 - VAR_PRIVATE/total_liquid_volume // Used to determine when to create fluids in the world and the like. VAR_PRIVATE/maximum_volume = 120 + VAR_PRIVATE/tmp/cached_color + VAR_PRIVATE/tmp/primary_reagent + VAR_PRIVATE/tmp/primary_solid + VAR_PRIVATE/tmp/primary_liquid + VAR_PRIVATE/tmp/total_volume = 0 + VAR_PRIVATE/tmp/total_liquid_volume // Used to determine when to create fluids in the world and the like. + +// Reagent serde is handled per atom. +/datum/reagents/ShouldSerialize(_age) + SHOULD_CALL_PARENT(FALSE) + return FALSE /datum/reagents/New(var/maximum_volume = 120, var/atom/my_atom) src.maximum_volume = maximum_volume @@ -976,30 +991,18 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new . = FONT_COLORED(get_color(), .) /* Atom reagent creation - use it all the time */ -/atom/proc/create_reagents(var/max_vol) - if(istype(reagents)) - log_debug("Attempted to create a new reagents holder when already referencing one: [log_info_line(src)]") - REAGENT_SET_MAX_VOL(reagents, max(REAGENT_MAXIMUM_VOLUME(reagents), max_vol)) - else if(!reagents) - reagents = new/datum/reagents(max_vol, src) - else - return - return reagents - -/atom/proc/create_or_update_reagents(_vol, override_volume) - if(isnull(reagents)) - return create_reagents(_vol) - if(istype(reagents)) - if(override_volume) - REAGENT_SET_MAX_VOL(reagents, _vol) // should we remove excess reagents here? - else - REAGENT_SET_MAX_VOL(reagents, max(REAGENT_MAXIMUM_VOLUME(reagents), _vol)) +/atom/proc/create_or_update_reagents(vol, override_volume) + if(islist(reagents)) + return // We are pending serde, this will be handled in initialize_reagents(). + else if(istype(reagents)) + var/use_max_vol = override_volume ? vol : max(vol, REAGENT_MAXIMUM_VOLUME(reagents)) + REAGENT_SET_MAX_VOL(reagents, use_max_vol) reagents.update_total() - return reagents + else if(isnull(reagents)) + reagents = new /datum/reagents(vol, src) + return reagents /// Infinite reagent sink: nothing is ever actually added to it, useful for complex, filtered deletion of reagents without holder churn. -/datum/reagents/sink - /datum/reagents/sink/add_reagent(var/decl/material/reagent, amount, data, safety, defer_update, phase) amount = CHEMS_QUANTIZE(min(amount, REAGENTS_FREE_SPACE(src))) if(amount <= 0) @@ -1008,3 +1011,10 @@ var/global/datum/reagents/sink/infinite_reagent_sink = new if(!istype(reagent)) return FALSE return TRUE + +/datum/reagents/proc/get_explosive_power() + for(var/decl/material/mat in reagent_volumes) + if(isnull(mat.explosive_power_divisor)) + continue + . += (reagent_volumes[mat] / mat.explosive_power_divisor) + . = round(., 1) diff --git a/code/modules/reagents/chems/chems_blood.dm b/code/modules/reagents/chems/chems_blood.dm index b4a6f089ba82..299944612356 100644 --- a/code/modules/reagents/chems/chems_blood.dm +++ b/code/modules/reagents/chems/chems_blood.dm @@ -12,7 +12,7 @@ glass_desc = "Are you sure this is tomato juice?" coated_adjective = "bloody" value = 2.5 - opacity = TRUE + opacity = 1.0 min_fluid_opacity = FLUID_MAX_ALPHA max_fluid_opacity = 240 compost_value = 1 // yum @@ -89,3 +89,4 @@ value = 0 exoplanet_rarity_gas = MAT_RARITY_UNCOMMON compost_value = 1 // yum + opacity = 1.0 diff --git a/code/modules/reagents/chems/chems_cleaner.dm b/code/modules/reagents/chems/chems_cleaner.dm index d14189ec1813..007ce32ffa67 100644 --- a/code/modules/reagents/chems/chems_cleaner.dm +++ b/code/modules/reagents/chems/chems_cleaner.dm @@ -38,3 +38,4 @@ ignition_point = 353 boiling_point = 373 accelerant_value = 0.3 + opacity = 1.0 // liquid is opaque by default, but soap is solid (or maybe it should be like, mostly opaque? tbd) diff --git a/code/modules/reagents/chems/chems_compounds.dm b/code/modules/reagents/chems/chems_compounds.dm index cb305d78e23c..f080406738f4 100644 --- a/code/modules/reagents/chems/chems_compounds.dm +++ b/code/modules/reagents/chems/chems_compounds.dm @@ -263,12 +263,7 @@ if(!M.has_genetic_information()) return if(prob(removed * 0.1)) // Approx. one mutation per 10 injected/20 ingested/30 touching units - M.set_unique_enzymes(num2text(random_id(/mob, 1000000, 9999999))) - if(prob(98)) - M.add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/disability))) - else - M.add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/superpower))) - M.apply_damage(10 * removed, IRRADIATE, armor_pen = 100) + M.apply_random_mutation(10 * removed) /decl/material/liquid/lactate name = "lactate" diff --git a/code/modules/reagents/chems/chems_medicines.dm b/code/modules/reagents/chems/chems_medicines.dm index 6578646b9e28..b771084ef239 100644 --- a/code/modules/reagents/chems/chems_medicines.dm +++ b/code/modules/reagents/chems/chems_medicines.dm @@ -30,10 +30,11 @@ value = 1.5 exoplanet_rarity_gas = MAT_RARITY_EXOTIC uid = "chem_antirads" + var/antirad_power = 30 /decl/material/liquid/antirads/affect_blood(var/mob/living/M, var/removed, var/datum/reagents/holder) . = ..() - M.radiation = max(M.radiation - 30 * removed, 0) + M.radiation = max(M.radiation - antirad_power * removed, 0) /decl/material/liquid/brute_meds name = "styptic powder" diff --git a/code/modules/reagents/chems/chems_nutriment.dm b/code/modules/reagents/chems/chems_nutriment.dm index efe6040294cd..e1b478c93fab 100644 --- a/code/modules/reagents/chems/chems_nutriment.dm +++ b/code/modules/reagents/chems/chems_nutriment.dm @@ -14,6 +14,7 @@ nutriment_factor = 10 affect_blood_on_ingest = 0 affect_blood_on_inhale = 0 + opacity = 1.0 // liquids are half transparent by default; meat and etc should not be transparent // Technically a room-temperature solid, but saves // repathing it to /solid all over the codebase. @@ -71,6 +72,7 @@ uid = "chem_nutriment_honey" melting_point = 273 boiling_point = 373 + opacity = 0.5 /decl/material/liquid/nutriment/flour name = "flour" @@ -218,6 +220,7 @@ uid = "chem_nutriment_soysauce" melting_point = 273 boiling_point = 373 + opacity = 0.5 /decl/material/liquid/nutriment/ketchup name = "ketchup" @@ -256,6 +259,7 @@ melting_point = 273 boiling_point = 373 allergen_flags = ALLERGEN_FRUIT | ALLERGEN_VEGETABLE // Is a tomato a fruit or a vegetable? + opacity = 0.9 /decl/material/liquid/nutriment/garlicsauce name = "garlic sauce" @@ -269,6 +273,7 @@ melting_point = 273 boiling_point = 373 allergen_flags = ALLERGEN_VEGETABLE + opacity = 0.9 /decl/material/liquid/nutriment/rice name = "rice" @@ -294,6 +299,7 @@ melting_point = 273 boiling_point = 373 allergen_flags = ALLERGEN_FRUIT + opacity = 0.7 /decl/material/liquid/nutriment/sprinkles name = "sprinkles" @@ -329,6 +335,7 @@ uid = "chem_nutriment_vinegar" melting_point = 273 boiling_point = 373 + opacity = 0.5 /decl/material/liquid/nutriment/mayo name = "mayonnaise" @@ -342,7 +349,7 @@ allergen_flags = ALLERGEN_EGG /decl/material/liquid/nutriment/yeast - name = "Yeast" + name = "yeast" lore_text = "A collection of live fungal cultures, cultivated across history for use in fermentation and baking." taste_description = "mustiness" nutriment_factor = 1 diff --git a/code/modules/reagents/chems/chems_pigments.dm b/code/modules/reagents/chems/chems_pigments.dm index 5d23dc4e5230..95685fbc3db0 100644 --- a/code/modules/reagents/chems/chems_pigments.dm +++ b/code/modules/reagents/chems/chems_pigments.dm @@ -1,11 +1,12 @@ /decl/material/liquid/pigment name = "pigment" - lore_text = "Intensely coloured powder." + lore_text = "Intensely coloured powder." // then why is it a liquid? taste_description = "the back of class" color = "#888888" overdose = 5 hidden_from_codex = TRUE exoplanet_rarity_gas = MAT_RARITY_NOWHERE + opacity = 1.0 uid = "chem_pigment" /decl/material/liquid/pigment/red diff --git a/code/modules/reagents/cocktails.dm b/code/modules/reagents/cocktails.dm index fd688adbcd92..6a04df74b4e3 100644 --- a/code/modules/reagents/cocktails.dm +++ b/code/modules/reagents/cocktails.dm @@ -226,7 +226,7 @@ ) /decl/cocktail/toxins_special - name = "H2 Special" + name = "Toxins Special" description = "Raise a glass to the bomb technicians of yesteryear, wherever their ashes now reside." ratios = list( /decl/material/liquid/alcohol/rum = 1, diff --git a/code/modules/reagents/dispenser/cartridge.dm b/code/modules/reagents/dispenser/cartridge.dm index 35300fc4310a..daee57f2381d 100644 --- a/code/modules/reagents/dispenser/cartridge.dm +++ b/code/modules/reagents/dispenser/cartridge.dm @@ -11,10 +11,14 @@ possible_transfer_amounts = @"[50,100]" var/_reagent_label -/obj/item/chems/chem_disp_cartridge/Initialize() +/obj/item/chems/chem_disp_cartridge/Serialize() + . = ..() + SERIALIZE_IF_MODIFIED(_reagent_label, /obj/item/chems/chem_disp_cartridge) + +/obj/item/chems/chem_disp_cartridge/Initialize(ml, material_key) . = ..() var/decl/material/primary_reagent = istype(reagents) && reagents.get_primary_reagent_decl() - if(primary_reagent && !_reagent_label) + if(primary_reagent && isnull(_reagent_label)) _reagent_label = primary_reagent.name if(_reagent_label) setLabel(_reagent_label) diff --git a/code/modules/reagents/dispenser/dispenser_presets.dm b/code/modules/reagents/dispenser/dispenser_presets.dm index e6d1634b8447..afc9ccd606c4 100644 --- a/code/modules/reagents/dispenser/dispenser_presets.dm +++ b/code/modules/reagents/dispenser/dispenser_presets.dm @@ -28,7 +28,7 @@ buildable = FALSE -/obj/machinery/chemical_dispenser/ert +/obj/machinery/chemical_dispenser/medicine name = "medicine dispenser" spawn_cartridges = list( /obj/item/chems/chem_disp_cartridge/adrenaline, @@ -47,10 +47,8 @@ /obj/item/chems/chem_disp_cartridge/antibiotics, /obj/item/chems/chem_disp_cartridge/sedatives ) - buildable = FALSE - /obj/machinery/chemical_dispenser/bar_soft name = "soft drink dispenser" desc = "A soft drink machine." //Doesn't just serve soda --BlueNexus diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 89c9ecc84b34..847d7996b244 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -54,7 +54,7 @@ /obj/item/chems/update_name() . = ..() // handles material, etc var/newname = name - if(presentation_flags & PRESENTATION_FLAG_NAME) + if(istype(reagents) && (presentation_flags & PRESENTATION_FLAG_NAME)) var/decl/material/primary = reagents?.get_primary_reagent_decl() if(primary) newname += " of [primary.get_presentation_name(src)]" diff --git a/code/modules/reagents/reagent_containers/drinkingglass/glass_boxes.dm b/code/modules/reagents/reagent_containers/drinkingglass/glass_boxes.dm index d4d7e5a425be..dd072542c416 100644 --- a/code/modules/reagents/reagent_containers/drinkingglass/glass_boxes.dm +++ b/code/modules/reagents/reagent_containers/drinkingglass/glass_boxes.dm @@ -65,6 +65,12 @@ /obj/item/box/glasses/mug/WillContain() return list(/obj/item/chems/drinks/glass2/mug = max(1, storage?.storage_slots)) +/obj/item/box/glasses/coffeecup/WillContain() + return list(/obj/item/chems/drinks/glass2/coffeecup = max(1, storage?.storage_slots)) + +/obj/item/box/glasses/teacup/WillContain() + return list(/obj/item/chems/drinks/glass2/coffeecup/teacup = max(1, storage?.storage_slots)) + /obj/item/box/glasses/wine/WillContain() return list(/obj/item/chems/drinks/glass2/wine = max(1, storage?.storage_slots)) diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 572561fc288b..07d45320e232 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -187,6 +187,10 @@ update_name() update_icon() +/obj/item/chems/hypospray/autoinjector/used/Initialize() + . = ..() + atom_flags &= ~ATOM_FLAG_OPEN_CONTAINER + /obj/item/chems/hypospray/autoinjector/populate_reagents() SHOULD_CALL_PARENT(TRUE) . = ..() diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index bca089ca6fdd..fd4593aee2bd 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -383,6 +383,14 @@ add_to_reagents(/decl/material/liquid/amphetamines, 2 * vol_third) return ..() +/obj/item/chems/syringe/brute_meds + desc = "Contains drugs for treating brute trauma." + mode = SYRINGE_INJECT + +/obj/item/chems/syringe/brute_meds/populate_reagents() + add_to_reagents(/decl/material/liquid/brute_meds, REAGENT_MAXIMUM_VOLUME(reagents)) + return ..() + // TG ports /obj/item/chems/syringe/advanced diff --git a/code/modules/recycling/disposalpipe.dm b/code/modules/recycling/disposalpipe.dm index 0ed921518169..afc2c082b152 100644 --- a/code/modules/recycling/disposalpipe.dm +++ b/code/modules/recycling/disposalpipe.dm @@ -592,9 +592,21 @@ dpdir = sortdir | posdir | negdir -/obj/structure/disposalpipe/sortjunction/Initialize() +/obj/structure/disposalpipe/sortjunction/proc/validate_sort_type() + . = istext(sort_type) && sort_type != "" + if(!.) + if(name == initial(name)) + sort_type = "Unknown" + else + sort_type = name || "Unknown" + log_debug("Mapped untagged junction had empty sort_type, setting to '[sort_type]'.") + +/obj/structure/disposalpipe/sortjunction/Initialize(ml) . = ..() - if(sort_type) global.tagger_locations |= sort_type + if(sort_type) + global.tagger_locations |= sort_type + if(ml && !validate_sort_type()) + log_warning("Mapped sorting junction of type [type] initializing at [x],[y],[z] with invalid sort_type '[sort_type || "EMPTY"]'!") updatedir() updatename() @@ -662,6 +674,9 @@ desc = "An underfloor disposal pipe which filters all wrapped and tagged items." flipped_state = /obj/structure/disposalpipe/sortjunction/wildcard/flipped +/obj/structure/disposalpipe/sortjunction/wildcard/validate_sort_type() + return TRUE // Special case + /obj/structure/disposalpipe/sortjunction/wildcard/divert_check(var/checkTag) return checkTag != "" @@ -671,6 +686,12 @@ desc = "An underfloor disposal pipe which filters all untagged items." flipped_state = /obj/structure/disposalpipe/sortjunction/untagged/flipped +/obj/structure/disposalpipe/sortjunction/untagged/validate_sort_type() + . = (sort_type == "") + if(!.) + log_debug("Mapped untagged junction had non-empty sort_type, setting to empty string.") + sort_type = "" + /obj/structure/disposalpipe/sortjunction/untagged/divert_check(var/checkTag) return checkTag == "" diff --git a/code/modules/salvage/salvage.dm b/code/modules/salvage/salvage.dm new file mode 100644 index 000000000000..ecf3196f8834 --- /dev/null +++ b/code/modules/salvage/salvage.dm @@ -0,0 +1,131 @@ +/obj/item/salvage + desc = "The remains of an unfortunate device." + transform_animate_time = 0 + icon = 'icons/obj/modules/module_id.dmi' + icon_state = ICON_STATE_WORLD + abstract_type = /obj/item/salvage + var/work_skill = SKILL_DEVICES + var/obj/item/salvaged_type + var/list/repairs_required = list() + var/do_rotation = TRUE + +/obj/item/salvage/Initialize(var/ml, var/path) + . = ..(ml) + if(!ispath(salvaged_type, /obj/item)) + return INITIALIZE_HINT_QDEL + // TODO: grab partial initial matter from the salvage type. + icon_rotation = rand(-45, 45) + name = "[pick("busted", "broken", "shattered", "scrapped")] [salvaged_type::name]" + w_class = salvaged_type::w_class + + var/list/all_repair_options = get_repair_options() + var/list/selected_options = list() + for(var/opt_type in all_repair_options) + var/decl/salvage_repair_option/opt = RESOLVE_TO_DECL(opt_type) + if(istype(opt) && prob(opt.selection_prob)) + selected_options += opt + if(!length(selected_options)) + selected_options += RESOLVE_TO_DECL(pick(all_repair_options)) + for(var/decl/salvage_repair_option/opt in selected_options) + repairs_required += opt.create_salvage_requirement() + + update_icon() + +/obj/item/salvage/proc/get_repair_options() + return subtypesof(/decl/salvage_repair_option/material_sheet) + +/obj/item/salvage/attackby(obj/item/used_item, mob/user) + + // Find an appropriate repair (finished or not) + var/datum/salvage_repair_requirement/opt + for(var/datum/salvage_repair_requirement/rep in repairs_required) + if(!istype(used_item, rep.repair_type)) + continue + if(istype(used_item, /obj/item/stack/material) && !istype(used_item.material, rep.repair_material)) + continue + if(!opt || (opt.repair_amount == 0 && rep.repair_amount > 0)) + opt = rep + + // Apply the repair. + if(opt) + if(opt.repair_amount <= 0) + to_chat(user, SPAN_WARNING("\The [src] does not need further repair with \the [used_item].")) + else if(user.do_skilled((5 + rand(10)) SECONDS, work_skill, src) && !QDELETED(opt) && opt.repair_amount > 0) + if(istype(used_item, /obj/item/stack)) + var/obj/item/stack/stack = used_item + var/use_amt = min(opt.repair_amount, stack.get_amount()) + stack.use(use_amt) + opt.repair_amount -= use_amt + else if(user.try_unequip(used_item)) + qdel(used_item) + opt.repair_amount-- + // TODO: transfer material from the donor item to this item. + check_repair_completion(user) + return TRUE + + . = ..() + +/obj/item/salvage/get_examine_hints(mob/user, distance, infix, suffix) + . = ..() + . += SPAN_NOTICE("It requires the following items to be fully repaired:") + for(var/datum/salvage_repair_requirement/rep in repairs_required) + if(rep.repair_amount <= 0) + continue + var/is_mat_stack = ispath(rep.repair_type, /obj/item/stack/material) && rep.repair_material + var/obj/item/repair_type = rep.repair_type + var/repair_thing = is_mat_stack ? atom_info_repository.get_name_for(repair_type, rep.repair_material) : atom_info_repository.get_name_for(repair_type) + if(ispath(rep.repair_type, /obj/item/stack)) + var/obj/item/stack/stack = rep.repair_type + repair_thing = rep.repair_amount == 1 ? stack::singular_name : stack::plural_name + if(is_mat_stack) + var/decl/material/repair_mat = GET_DECL(rep.repair_material) + repair_thing = "[repair_mat.solid_name] [repair_thing]" + else if(rep.repair_amount > 1) + repair_thing = text_make_plural(repair_thing) + . += SPAN_NOTICE("- [rep.repair_amount] [repair_thing]") + +/obj/item/salvage/proc/check_repair_completion(mob/user) + + for(var/datum/salvage_repair_requirement/rep in repairs_required) + if(rep.repair_amount > 0) + if(user) + to_chat(user, SPAN_NOTICE("You mend some of the damage to \the [src], but further repair is required.")) + return + + var/obj/item/created = new salvaged_type(get_turf(src)) + if(isitem(created)) + if(created.name_prefix) + created.name_prefix = "[created.name_prefix] [pick("salvaged", "restored", "old", "worn")]" // enormous salvaged pipe wrench + else + created.name_prefix = pick("salvaged", "restored", "old", "worn") // salvaged laser rifle + created.update_name() + + var/atom/created_loc = loc + qdel(src) + + if(user) + to_chat(user, SPAN_NOTICE("You finish repairing \the [created]!")) + + if(user && created_loc == user) + user.put_in_hands(created) + else + created.forceMove(created_loc) + +/obj/item/salvage/on_update_icon() + SHOULD_CALL_PARENT(FALSE) + if(!salvaged_type) + return + var/old_name = name + var/old_desc = desc + var/old_pixel_x = pixel_x + var/old_pixel_y = pixel_y + var/old_plane = plane + var/old_layer = layer + appearance = atom_info_repository.get_appearance_of(salvaged_type) + name = old_name + desc = old_desc + pixel_x = old_pixel_x + pixel_y = old_pixel_y + plane = old_plane + layer = old_layer + update_transform() diff --git a/code/modules/salvage/salvage_ballistic.dm b/code/modules/salvage/salvage_ballistic.dm new file mode 100644 index 000000000000..904f6d0276c9 --- /dev/null +++ b/code/modules/salvage/salvage_ballistic.dm @@ -0,0 +1,24 @@ +/obj/item/salvage/ballistic + name = "broken ballistic weapon" + icon = /obj/item/gun/projectile/automatic/assault_rifle::icon + icon_state = /obj/item/gun/projectile/automatic/assault_rifle::icon_state + abstract_type = /obj/item/salvage/ballistic + +/obj/item/salvage/ballistic/get_repair_options() + return ..() + /decl/salvage_repair_option/component + +/obj/item/salvage/ballistic/assault + salvaged_type = /obj/item/gun/projectile/automatic/assault_rifle + +/obj/item/salvage/ballistic/pistol + salvaged_type = /obj/item/gun/projectile/pistol + +/obj/item/salvage/ballistic/smg + salvaged_type = /obj/item/gun/projectile/automatic/smg + +/obj/item/salvage/ballistic/shotgun_pump + salvaged_type = /obj/item/gun/projectile/shotgun/pump + +/obj/item/salvage/ballistic/shotgun_doublebarrel + salvaged_type = /obj/item/gun/projectile/shotgun/doublebarrel + diff --git a/code/modules/salvage/salvage_energy.dm b/code/modules/salvage/salvage_energy.dm new file mode 100644 index 000000000000..935a3467abbe --- /dev/null +++ b/code/modules/salvage/salvage_energy.dm @@ -0,0 +1,17 @@ +/obj/item/salvage/energy + name = "broken energy weapon" + icon = /obj/item/gun/energy/laser::icon + icon_state = /obj/item/gun/energy/laser::icon_state + abstract_type = /obj/item/salvage/energy + +/obj/item/salvage/energy/get_repair_options() + return ..() + /decl/salvage_repair_option/energy + +/obj/item/salvage/energy/ionrifle + salvaged_type = /obj/item/gun/energy/ionrifle + +/obj/item/salvage/energy/laserrifle + salvaged_type = /obj/item/gun/energy/laser + +/obj/item/salvage/energy/laser_retro + salvaged_type = /obj/item/gun/energy/retro/captain diff --git a/code/modules/salvage/salvage_launcher.dm b/code/modules/salvage/salvage_launcher.dm new file mode 100644 index 000000000000..571f69b6a136 --- /dev/null +++ b/code/modules/salvage/salvage_launcher.dm @@ -0,0 +1,14 @@ +/obj/item/salvage/launcher + name = "broken grenade launcher" + icon = /obj/item/gun/launcher/grenade::icon + icon_state = /obj/item/gun/launcher/grenade::icon_state + abstract_type = /obj/item/salvage/launcher + +/obj/item/salvage/launcher/get_repair_options() + return ..() + /decl/salvage_repair_option/launcher + +/obj/item/salvage/launcher/grenade + salvaged_type = /obj/item/gun/launcher/grenade + +/obj/item/salvage/launcher/dartgun + salvaged_type = /obj/item/gun/projectile/dartgun diff --git a/code/modules/salvage/salvage_magnetic.dm b/code/modules/salvage/salvage_magnetic.dm new file mode 100644 index 000000000000..78b8bcf47baa --- /dev/null +++ b/code/modules/salvage/salvage_magnetic.dm @@ -0,0 +1,12 @@ +/obj/item/salvage/magnetic + name = "broken magnetic weapon" + icon = /obj/item/gun/magnetic/railgun/flechette::icon + icon_state = /obj/item/gun/magnetic/railgun/flechette::icon_state + abstract_type = /obj/item/salvage/magnetic + +/obj/item/salvage/magnetic/get_repair_options() + return ..() + /decl/salvage_repair_option/magnetic + +/obj/item/salvage/magnetic/flechette + salvaged_type = /obj/item/gun/magnetic/railgun/flechette + diff --git a/code/modules/salvage/salvage_repair_option.dm b/code/modules/salvage/salvage_repair_option.dm new file mode 100644 index 000000000000..f63d88afc611 --- /dev/null +++ b/code/modules/salvage/salvage_repair_option.dm @@ -0,0 +1,90 @@ +/decl/salvage_repair_option + abstract_type = /decl/salvage_repair_option + var/selection_prob = 0 + var/list/selection_types + var/list/selection_materials + var/selection_min_amount = 1 + var/selection_max_amount = 3 + +/decl/salvage_repair_option/validate() + . = ..() + if(!islist(selection_types) || !length(selection_types)) + . += "null, empty or malformed selection_types list" + else + for(var/selection_type in selection_types) + if(!ispath(selection_type)) + . += "non-path selection type: '[selection_type]'" + else if(ispath(selection_type, /obj/item/stack/material) && (!islist(selection_materials) || !length(selection_materials))) + . += "material stack '[selection_type]' in selection_types, but selection_materials is empty/malformed" + for(var/selection_material in selection_materials) + if(!ispath(selection_material, /decl/material)) + . += "non-material path '[selection_material]' in selection_materials" + +/decl/salvage_repair_option/proc/create_salvage_requirement() + var/use_type = pick(selection_types) + var/use_mat = ispath(use_type, /obj/item/stack/material) ? pick(selection_materials) : null + return new /datum/salvage_repair_requirement(use_type, use_mat, rand(selection_min_amount, selection_max_amount)) + +/decl/salvage_repair_option/component + selection_prob = 30 + selection_types = list( + /obj/item/stock_parts/manipulator + ) + +/decl/salvage_repair_option/material_sheet + selection_prob = 40 + abstract_type = /decl/salvage_repair_option/material_sheet + +/decl/salvage_repair_option/material_sheet/plastic + selection_types = list( + /obj/item/stack/material/panel + ) + selection_materials = list( + /decl/material/solid/organic/plastic + ) + +/decl/salvage_repair_option/material_sheet/glass + selection_types = list( + /obj/item/stack/material/pane + ) + selection_materials = list( + /decl/material/solid/glass + ) + +/decl/salvage_repair_option/material_sheet/plasteel + selection_types = list( + /obj/item/stack/material/sheet/reinforced + ) + selection_materials = list( + /decl/material/solid/metal/plasteel + ) + +/decl/salvage_repair_option/launcher + selection_prob = 50 + selection_materials = list( + /decl/material/solid/metal/steel + ) + selection_types = list( + /obj/item/stack/tape_roll, + /obj/item/stack/material/rods, + /obj/item/handcuffs/cable + ) + selection_max_amount = 1 + +/decl/salvage_repair_option/energy + selection_prob = 25 + selection_types = list( + /obj/item/stack/cable_coil, + /obj/item/stock_parts/scanning_module, + /obj/item/stock_parts/capacitor + ) + selection_max_amount = 1 + +/decl/salvage_repair_option/magnetic + selection_prob = 70 + selection_types = list( + /obj/item/stock_parts/smes_coil, + /obj/item/assembly/prox_sensor, + /obj/item/stock_parts/circuitboard/apc + ) + selection_max_amount = 1 diff --git a/code/modules/salvage/salvage_repair_requirement.dm b/code/modules/salvage/salvage_repair_requirement.dm new file mode 100644 index 000000000000..fdf524bb61e4 --- /dev/null +++ b/code/modules/salvage/salvage_repair_requirement.dm @@ -0,0 +1,9 @@ +/datum/salvage_repair_requirement + var/repair_type + var/repair_material + var/repair_amount + +/datum/salvage_repair_requirement/New(_type, _mat, _amt) + repair_amount = _amt + repair_material = _mat + repair_type = _type diff --git a/code/modules/salvage/structure.dm b/code/modules/salvage/structure.dm new file mode 100644 index 000000000000..1d71f206157a --- /dev/null +++ b/code/modules/salvage/structure.dm @@ -0,0 +1,23 @@ +/obj/structure/salvage + icon = 'icons/obj/structures/salvage.dmi' + abstract_type = /obj/structure/salvage + tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT + material = /decl/material/solid/metal/steel + var/frame_type = /obj/machinery/constructable_frame/machine_frame + var/work_skill = SKILL_CONSTRUCTION + +/obj/structure/salvage/proc/get_salvageable_components() + return + +/obj/structure/salvage/Initialize(ml, _mat, _reinf_mat) + . = ..() + name_prefix = pick("broken", "ruined", "destroyed", "slagged", "damaged") + +/obj/structure/salvage/create_dismantled_products(turf/T) + . = ..() + if(frame_type) + new frame_type(T) + var/list/salvageable_components = get_salvageable_components() + for(var/comp in salvageable_components) + if(!salvageable_components[comp] || prob(salvageable_components[comp])) + new comp(T) diff --git a/code/modules/salvage/structure_computer.dm b/code/modules/salvage/structure_computer.dm new file mode 100644 index 000000000000..12b9f700eee3 --- /dev/null +++ b/code/modules/salvage/structure_computer.dm @@ -0,0 +1,116 @@ +/obj/structure/salvage/computer + name = "computer" + desc = "A defunct computer. There might still be useful components inside." + icon_state = "computer0" + +/obj/structure/salvage/computer/Initialize() + . = ..() + icon_state = "computer[rand(0,7)]" + +/obj/structure/salvage/computer/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 90, + /obj/item/stack/material/pane/mapped/glass/five = 90, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/network_card/advanced = 20, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/capacitor/adv = 30, + ) + return salvageable_parts + +/obj/structure/salvage/server + name = "server" + desc = "A damaged, broken server. There might still be useful components inside." + icon_state = "server0" + +/obj/structure/salvage/server/Initialize(ml, _mat, _reinf_mat) + . = ..() + +/obj/structure/salvage/server/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 90, + /obj/item/stack/material/pane/mapped/glass/five = 90, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/subspace/amplifier = 40, + /obj/item/stock_parts/subspace/amplifier = 40, + /obj/item/stock_parts/subspace/analyzer = 40, + /obj/item/stock_parts/subspace/analyzer = 40, + /obj/item/stock_parts/subspace/ansible = 40, + /obj/item/stock_parts/subspace/ansible = 40, + /obj/item/stock_parts/subspace/transmitter = 40, + /obj/item/stock_parts/subspace/transmitter = 40, + /obj/item/stock_parts/subspace/crystal = 30, + /obj/item/stock_parts/subspace/crystal = 30, + /obj/item/stock_parts/computer/network_card/advanced = 20 + ) + return salvageable_parts + +/obj/structure/salvage/computer_old + name = "computer" + desc = "A defunct computer. There might still be useful components inside." + icon_state = "os-computer" + +/obj/structure/salvage/computer_old/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 90, + /obj/item/stack/material/pane/mapped/glass/five = 90, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/computer/processor_unit/photonic = 40, + /obj/item/stock_parts/computer/processor_unit/photonic = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/network_card/advanced = 40 + ) + return salvageable_parts + +/obj/structure/salvage/data + name = "data storage" + desc = "An old, battered, broken data storage rack. There might still be useful components inside." + icon_state = "data0" + +/obj/structure/salvage/data/Initialize() + . = ..() + icon_state = "data[rand(0,1)]" + +/obj/structure/salvage/data/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 90, + /obj/item/stack/material/pane/mapped/glass/five = 90, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive/advanced = 30, + /obj/item/stock_parts/computer/hard_drive/advanced = 30, + /obj/item/stock_parts/computer/network_card/advanced = 20 + ) + return salvageable_parts diff --git a/code/modules/salvage/structure_console.dm b/code/modules/salvage/structure_console.dm new file mode 100644 index 000000000000..7f31e5be6ad6 --- /dev/null +++ b/code/modules/salvage/structure_console.dm @@ -0,0 +1,35 @@ +/obj/structure/salvage/console + name = "console" + desc = "A beat-up old console. Might still have some useful components inside." + icon_state = "os_console" + +/obj/structure/salvage/console/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stack/cable_coil/five = 90, + /obj/item/stock_parts/console_screen = 80, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/computer/processor_unit/small = 40, + /obj/item/stock_parts/computer/processor_unit/photonic = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/network_card/advanced = 40 + ) + return salvageable_parts + +/obj/structure/salvage/console/broken + icon_state = "os_console_broken" + +/obj/structure/salvage/console/broken/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stack/cable_coil/five = 90, + /obj/item/stock_parts/console_screen = 80, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/processor_unit/photonic = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/network_card/advanced = 40 + ) + return salvageable_parts diff --git a/code/modules/salvage/structure_machine.dm b/code/modules/salvage/structure_machine.dm new file mode 100644 index 000000000000..62fa97c6bbfc --- /dev/null +++ b/code/modules/salvage/structure_machine.dm @@ -0,0 +1,92 @@ + +/obj/structure/salvage/fabricator + name = "fabricator" + desc = "A busted, defunct fabricator. There might still be useful components or materials inside." + icon_state = "autolathe" + +/obj/structure/salvage/fabricator/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 80, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/capacitor = 40, + /obj/item/stock_parts/scanning_module = 40, + /obj/item/stock_parts/manipulator = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/capacitor/adv = 20, + /obj/item/stock_parts/micro_laser/high = 20, + /obj/item/stock_parts/micro_laser/high = 20, + /obj/item/stock_parts/matter_bin/adv = 20, + /obj/item/stock_parts/matter_bin/adv = 20, + /obj/item/stack/material/sheet/mapped/steel/twenty = 40, + /obj/item/stack/material/pane/mapped/glass/twenty = 40, + /obj/item/stack/material/panel/mapped/plastic/twenty = 40, + /obj/item/stack/material/sheet/reinforced/mapped/plasteel/ten = 40, + /obj/item/stack/material/ingot/mapped/silver/ten = 20, + /obj/item/stack/material/ingot/mapped/gold/ten = 20 + ) + return salvageable_parts + +/obj/structure/salvage/machine + name = "machine" + desc = "A badly-damaged machine of some kind. There might still be some usable components inside." + icon_state = "machine1" + +/obj/structure/salvage/machine/Initialize() + . = ..() + icon_state = "machine[rand(0,6)]" + +/obj/structure/salvage/machine/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 80, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/capacitor = 40, + /obj/item/stock_parts/capacitor = 40, + /obj/item/stock_parts/scanning_module = 40, + /obj/item/stock_parts/scanning_module = 40, + /obj/item/stock_parts/manipulator = 40, + /obj/item/stock_parts/manipulator = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/capacitor/adv = 20, + /obj/item/stock_parts/scanning_module/adv = 20, + /obj/item/stock_parts/manipulator/nano = 20, + /obj/item/stock_parts/micro_laser/high = 20, + /obj/item/stock_parts/matter_bin/adv = 20 + ) + return salvageable_parts + +/obj/structure/salvage/machine_old + name = "machine" + desc = "A badly-damaged machine of some kind. There might still be some usable components inside." + icon_state = "os-machine" + +/obj/structure/salvage/machine_old/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 80, + /obj/item/stock_parts/capacitor = 40, + /obj/item/stock_parts/capacitor = 40, + /obj/item/stock_parts/scanning_module = 40, + /obj/item/stock_parts/scanning_module = 40, + /obj/item/stock_parts/manipulator = 40, + /obj/item/stock_parts/manipulator = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/matter_bin = 40 + ) + return salvageable_parts diff --git a/code/modules/salvage/structure_misc.dm b/code/modules/salvage/structure_misc.dm new file mode 100644 index 000000000000..e1515b69c93b --- /dev/null +++ b/code/modules/salvage/structure_misc.dm @@ -0,0 +1,27 @@ +/obj/structure/salvage/implant_container + name = "old container" + icon_state = "implant_container0" + +/obj/structure/salvage/implant_container/Initialize() + . = ..() + icon_state = "implant_container[rand(0,1)]" + +/obj/structure/salvage/implant_container/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 80, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/implant/death_alarm = 15, + /obj/item/implant/explosive = 10, + /obj/item/implant/freedom = 5, + /obj/item/implant/tracking = 10, + /obj/item/implant/chem = 10, + /obj/item/implantcase = 30, + /obj/item/implanter = 30, + /obj/item/stack/material/sheet/mapped/steel/ten = 30, + /obj/item/stack/material/pane/mapped/glass/ten = 30, + /obj/item/stack/material/ingot/mapped/silver/ten = 30 + ) + return salvageable_parts diff --git a/code/modules/salvage/structure_terminal.dm b/code/modules/salvage/structure_terminal.dm new file mode 100644 index 000000000000..6e2ef10be5c5 --- /dev/null +++ b/code/modules/salvage/structure_terminal.dm @@ -0,0 +1,29 @@ +/obj/structure/salvage/personal + name = "personal terminal" + desc = "An unusable personal terminal. There might still be useful components inside." + icon_state = "personal0" + +/obj/structure/salvage/personal/Initialize(ml, _mat, _reinf_mat) + . = ..() + icon_state = "personal[rand(0,12)]" + +/obj/structure/salvage/personal/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 90, + /obj/item/stack/cable_coil/five = 90, + /obj/item/stack/material/pane/mapped/glass/five = 70, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/computer/network_card = 60, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/network_card/advanced = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/processor_unit = 60, + /obj/item/stock_parts/computer/processor_unit/small = 50, + /obj/item/stock_parts/computer/processor_unit/photonic = 40, + /obj/item/stock_parts/computer/processor_unit/photonic/small = 30, + /obj/item/stock_parts/computer/hard_drive = 60, + /obj/item/stock_parts/computer/hard_drive/advanced = 40 + ) + return salvageable_parts diff --git a/code/modules/scanners/mining.dm b/code/modules/scanners/mining.dm index 2b9095692b27..9da785d77ced 100644 --- a/code/modules/scanners/mining.dm +++ b/code/modules/scanners/mining.dm @@ -15,13 +15,12 @@ origin_tech = @'{"magnets":1,"engineering":1}' use_delay = 50 printout_color = "#fff7f0" - var/survey_data = 0 - scan_sound = 'sound/effects/ping.ogg' + var/survey_data = 0 /obj/item/scanner/mining/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - . += "A tiny indicator on \the [src] shows it holds [survey_data] good explorer point\s." + . += "A tiny indicator on \the [src] shows it holds [survey_data] survey point\s." /obj/item/scanner/mining/is_valid_scan_target(turf/T) return istype(T) @@ -39,13 +38,13 @@ if(scan_results[2]) survey_data += scan_results[2] playsound(loc, 'sound/machines/ping.ogg', 40, 1) - to_chat(user, SPAN_NOTICE("New survey data stored - earned [scan_results[2]] GEP.")) + to_chat(user, SPAN_NOTICE("New survey data stored - earned [scan_results[2]] survey points.")) /obj/item/scanner/mining/proc/put_disk_in_hand(var/mob/M) if(!survey_data) to_chat(M, SPAN_WARNING("There is no survey data stored on \the [src].")) return FALSE - visible_message(SPAN_NOTICE("\The [src] spits out a disk containing [survey_data] GEP.")) + visible_message(SPAN_NOTICE("\The [src] spits out a disk containing [survey_data] survey points.")) var/obj/item/disk/survey/D = new(get_turf(src)) D.data = survey_data survey_data = 0 diff --git a/code/modules/scanners/xenobio.dm b/code/modules/scanners/xenobio.dm index 3fc507c50dec..b7afc80b7a63 100644 --- a/code/modules/scanners/xenobio.dm +++ b/code/modules/scanners/xenobio.dm @@ -20,9 +20,6 @@ /obj/item/scanner/xenobio/is_valid_scan_target(atom/O) if(is_type_in_list(O, valid_targets)) return TRUE - if(istype(O, /obj/structure/stasis_cage)) - var/obj/structure/stasis_cage/cagie = O - return !!cagie.contained return FALSE /obj/item/scanner/xenobio/scan(mob/O, mob/user) diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm index eca9534b53ce..cb91e3c505c1 100644 --- a/code/modules/security_levels/keycard_authentication.dm +++ b/code/modules/security_levels/keycard_authentication.dm @@ -65,6 +65,13 @@ interact(user) return TRUE +/obj/machinery/keycard_auth/proc/get_event_options(mob/user) + . = list() + for(var/decl/keycard_auth_event/event in global.decls_repository.get_decls_of_subtype_unassociated(/decl/keycard_auth_event)) + if(!event.is_available(src, user)) + continue + . += event.get_option(src, user) + /obj/machinery/keycard_auth/interact(mob/user) user.set_machine(src) @@ -74,27 +81,12 @@ dat += "


" if(screen == 1) - dat += "Select an event to trigger:" + var/list/options = get_event_options(user) + dat += "Select an event to trigger:" show_browser(user, dat, "window=keycard_auth;size=500x250") if(screen == 2) - dat += "Please swipe your card to authorize the following event: [event]" + var/decl/keycard_auth_event/real_event = global.decls_repository.get_decl_by_id(event) + dat += "Please swipe your card to authorize the following event: [real_event.get_link_text()]" dat += "

Back" show_browser(user, dat, "window=keycard_auth;size=500x250") return @@ -144,8 +136,10 @@ /obj/machinery/keycard_auth/proc/broadcast_check() if(confirmed) confirmed = 0 + var/decl/keycard_auth_event/real_event = global.decls_repository.get_decl_by_id(event) + var/cached_name = real_event.get_link_text() // because triggering the event can change the name trigger_event(event) - log_and_message_admins("triggered and [key_name(event_confirmed_by)] confirmed event [event]", event_triggered_by || usr) + log_and_message_admins("triggered and [key_name(event_confirmed_by)] confirmed event [cached_name]", event_triggered_by || usr) reset() /obj/machinery/keycard_auth/proc/receive_request(var/obj/machinery/keycard_auth/source, obj/item/card/id/ID) @@ -166,45 +160,11 @@ busy = 0 /obj/machinery/keycard_auth/proc/trigger_event() - switch(event) - if("Red alert") - var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) - security_state.stored_security_level = security_state.current_security_level - security_state.set_security_level(security_state.high_security_level) - SSstatistics.add_field("alert_keycard_auth_red",1) - if("Revert alert") - var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) - security_state.set_security_level(security_state.stored_security_level) - SSstatistics.add_field("alert_keycard_revert_red",1) - if("Grant Emergency Maintenance Access") - global.using_map.make_maint_all_access() - SSstatistics.add_field("alert_keycard_auth_maintGrant",1) - if("Revoke Emergency Maintenance Access") - global.using_map.revoke_maint_all_access() - SSstatistics.add_field("alert_keycard_auth_maintRevoke",1) - if("Emergency Response Team") - if(is_ert_blocked()) - visible_message(SPAN_WARNING("\The [src] blinks and displays a message: All emergency response teams are dispatched and can not be called at this time."), range=2) - return - - trigger_armed_response_team(1) - SSstatistics.add_field("alert_keycard_auth_ert",1) - if("Grant Nuclear Authorization Code") - var/obj/machinery/nuclearbomb/nuke = locate(/obj/machinery/nuclearbomb/station) in SSmachines.machinery - if(nuke) - visible_message(SPAN_WARNING("\The [src] blinks and displays a message: The nuclear authorization code is [nuke.r_code]"), range=2) - else - visible_message(SPAN_WARNING("\The [src] blinks and displays a message: No self-destruct terminal found."), range=2) - SSstatistics.add_field("alert_keycard_auth_nukecode",1) - -/obj/machinery/keycard_auth/proc/is_ert_blocked() - if(get_config_value(/decl/config/toggle/ert_admin_call_only)) - return TRUE - return SSticker.mode?.ert_disabled + var/decl/keycard_auth_event/the_event = decls_repository.get_decl_by_id(event) + if(the_event) + the_event.on_event(src) /obj/machinery/keycard_auth/update_directional_offset(force = FALSE) if(!force && (!length(directional_offset) || !is_wall_mounted())) //Check if the thing is actually mapped onto a table or something return - . = ..() - -var/global/maint_all_access = 0 \ No newline at end of file + . = ..() \ No newline at end of file diff --git a/code/modules/security_levels/keycard_authentication_events.dm b/code/modules/security_levels/keycard_authentication_events.dm new file mode 100644 index 000000000000..076501f1b32e --- /dev/null +++ b/code/modules/security_levels/keycard_authentication_events.dm @@ -0,0 +1,79 @@ +/decl/keycard_auth_event + abstract_type = /decl/keycard_auth_event + decl_flags = DECL_FLAG_MANDATORY_UID + var/name = "Abstract Keycard Authentication Event" + +/decl/keycard_auth_event/proc/get_link_text(obj/machinery/keycard_auth/auth, mob/user) + return name + +/decl/keycard_auth_event/proc/is_available(obj/machinery/keycard_auth/auth, mob/user) + return TRUE + +/decl/keycard_auth_event/proc/get_option(obj/machinery/keycard_auth/auth, mob/user) + SHOULD_NOT_OVERRIDE(TRUE) + var/fail_reason = get_failure_reason(user) + if(fail_reason) + return fail_reason + return "[get_link_text(auth, user)]" + +/decl/keycard_auth_event/proc/get_failure_reason(obj/machinery/keycard_auth/auth, mob/user) + return + +/decl/keycard_auth_event/proc/on_event(obj/machinery/keycard_auth/auth) + return + +/decl/keycard_auth_event/high_security + name = "Toggle High Security Level" + uid = "keycard_event_toggle_high_security" + +/decl/keycard_auth_event/high_security/get_failure_reason(obj/machinery/keycard_auth/auth, mob/user) + var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) + if(security_state.current_security_level == security_state.severe_security_level) + return "Cannot modify the alert level at this time: [security_state.severe_security_level.name] engaged." + +/decl/keycard_auth_event/high_security/get_link_text(obj/machinery/keycard_auth/auth, mob/user) + var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) + if(security_state.current_security_level == security_state.high_security_level) // toggle! + return "Disengage [security_state.high_security_level.name]" + else + return "Engage [security_state.high_security_level.name]" + +/decl/keycard_auth_event/high_security/on_event(obj/machinery/keycard_auth/auth, mob/user) + var/decl/security_state/security_state = GET_DECL(global.using_map.security_state) + if(security_state.current_security_level == security_state.high_security_level) + security_state.set_security_level(security_state.stored_security_level) + SSstatistics.add_field("alert_keycard_revert_red",1) + else + security_state.stored_security_level = security_state.current_security_level + security_state.set_security_level(security_state.high_security_level) + SSstatistics.add_field("alert_keycard_auth_red",1) + +/decl/keycard_auth_event/maintenance_access + name = "Toggle Emergency Maintenance Access" + uid = "keycard_event_maintenance_access" + +/decl/keycard_auth_event/maintenance_access/get_link_text(obj/machinery/keycard_auth/auth, mob/user) + if(global.using_map.maint_all_access) + return "Revoke Emergency Maintenance Access" + else + return "Grant Emergency Maintenance Access" + +/decl/keycard_auth_event/maintenance_access/on_event(obj/machinery/keycard_auth/auth) + if(global.using_map.maint_all_access) + global.using_map.revoke_maint_all_access() + SSstatistics.add_field("alert_keycard_auth_maintRevoke",1) + else + global.using_map.make_maint_all_access() + SSstatistics.add_field("alert_keycard_auth_maintGrant",1) + +/decl/keycard_auth_event/nuke_code + name = "Grant Nuclear Authorization Code" + uid = "keycard_event_nuke_code" + +/decl/keycard_auth_event/nuke_code/on_event(obj/machinery/keycard_auth/auth) + var/obj/machinery/nuclearbomb/nuke = locate(/obj/machinery/nuclearbomb/station) in SSmachines.machinery + if(nuke) + auth.visible_message(SPAN_WARNING("\The [src] blinks and displays a message: The nuclear authorization code is [nuke.r_code]"), range=2) + else + auth.visible_message(SPAN_WARNING("\The [src] blinks and displays a message: No self-destruct terminal found."), range=2) + SSstatistics.add_field("alert_keycard_auth_nukecode",1) \ No newline at end of file diff --git a/code/modules/shuttles/landmarks.dm b/code/modules/shuttles/landmarks.dm index 661bab8cf891..4029741fcbda 100644 --- a/code/modules/shuttles/landmarks.dm +++ b/code/modules/shuttles/landmarks.dm @@ -56,7 +56,7 @@ var/global/list/shuttle_landmarks = list() if(!istype(docking_controller)) log_error("Could not find docking controller for shuttle waypoint '[name]', docking tag was '[docking_tag]'.") - var/obj/effect/overmap/visitable/location = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/location = global.overmap_sectors[z] if(location && location.docking_codes) docking_controller.docking_codes = location.docking_codes @@ -67,9 +67,9 @@ var/global/list/shuttle_landmarks = list() ADJUST_TAG_VAR(docking_controller, map_hash) /obj/effect/shuttle_landmark/forceMove() - var/obj/effect/overmap/visitable/map_origin = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/map_origin = global.overmap_sectors[z] . = ..() - var/obj/effect/overmap/visitable/map_destination = global.overmap_sectors[num2text(z)] + var/obj/effect/overmap/visitable/map_destination = global.overmap_sectors[z] if(map_origin != map_destination) if(map_origin) map_origin.remove_landmark(src, shuttle_restricted) diff --git a/code/modules/shuttles/shuttle_autodock.dm b/code/modules/shuttles/shuttle_autodock.dm index f2003eb0129d..5d56b4285d46 100644 --- a/code/modules/shuttles/shuttle_autodock.dm +++ b/code/modules/shuttles/shuttle_autodock.dm @@ -33,7 +33,7 @@ if(active_docking_controller) set_docking_codes(active_docking_controller.docking_codes) else if(current_location?.overmap_id) - var/obj/effect/overmap/visitable/location = global.overmap_sectors[num2text(current_location.z)] + var/obj/effect/overmap/visitable/location = global.overmap_sectors[current_location.z] if(location && location.docking_codes) set_docking_codes(location.docking_codes) dock() diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index b9e342c4225d..394c6abd75b3 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -118,7 +118,9 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 var/breath_type = /decl/material/gas/oxygen // Non-oxygen gas breathed, if any. /// Material types considered noticeably poisonous when inhaled (ie. updates the toxins indicator on the HUD). /// This is an associative list for speed. - var/poison_types = list(/decl/material/gas/chlorine = TRUE) + var/poison_types = list( + /decl/material/gas/chlorine = TRUE + ) var/exhale_type = /decl/material/gas/carbon_dioxide // Exhaled gas type. var/blood_reagent = /decl/material/liquid/blood @@ -401,12 +403,14 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 if(taste_sensitivity < 0) . += "taste_sensitivity ([taste_sensitivity]) was negative" -/decl/species/proc/equip_survival_gear(var/mob/living/human/H, var/box_type = /obj/item/box/survival) - var/obj/item/backpack/backpack = H.get_equipped_item(slot_back_str) +/decl/species/proc/equip_survival_gear(mob/living/wearer, box_type = /obj/item/box/survival) + if(!box_type) + return + var/obj/item/backpack/backpack = wearer.get_equipped_item(slot_back_str) if(istype(backpack)) - H.equip_to_slot_or_del(new box_type(backpack), slot_in_backpack_str) + wearer.equip_to_slot_or_del(new box_type(backpack), slot_in_backpack_str) else - H.put_in_hands_or_del(new box_type(H)) + wearer.put_in_hands_or_del(new box_type(wearer)) /decl/species/proc/get_manual_dexterity(var/mob/living/human/H) . = manual_dexterity diff --git a/code/modules/species/species_hud.dm b/code/modules/species/species_hud.dm index 6c67caea5d24..17ab4381d161 100644 --- a/code/modules/species/species_hud.dm +++ b/code/modules/species/species_hud.dm @@ -41,6 +41,8 @@ equip_slots |= slot_handcuffed_str if(slot_back_str in equip_slots) equip_slots |= slot_in_backpack_str + if(slot_wear_id_str in equip_slots) + equip_slots |= slot_in_wallet_str /datum/hud_data/monkey inventory_slots = list( diff --git a/code/modules/sprite_accessories/_accessory_category.dm b/code/modules/sprite_accessories/_accessory_category.dm index d2cdcebc5f51..58e9b96498fe 100644 --- a/code/modules/sprite_accessories/_accessory_category.dm +++ b/code/modules/sprite_accessories/_accessory_category.dm @@ -10,11 +10,9 @@ /// A default always-available type used as a fallback. var/default_accessory /// Set to FALSE for categories where multiple selection is allowed (markings) - var/single_selection = TRUE + var/single_selection = TRUE /// Set to TRUE to apply these markings as defaults when bodytype is set. - var/always_apply_defaults = FALSE - /// Whether the accessories in this category are cleared when prefs are applied. - var/clear_in_pref_apply = FALSE + var/always_apply_defaults = FALSE /decl/sprite_accessory_category/validate() . = ..() diff --git a/code/modules/sprite_accessories/markings/_accessory_markings.dm b/code/modules/sprite_accessories/markings/_accessory_markings.dm index 3d02bc64b080..25eafb7f5f36 100644 --- a/code/modules/sprite_accessories/markings/_accessory_markings.dm +++ b/code/modules/sprite_accessories/markings/_accessory_markings.dm @@ -4,7 +4,6 @@ base_accessory_type = /decl/sprite_accessory/marking uid = "acc_cat_markings" always_apply_defaults = TRUE - clear_in_pref_apply = TRUE /decl/sprite_accessory/marking icon = 'icons/mob/human_races/species/default_markings.dmi' diff --git a/code/modules/sprite_accessories/tails/_accessory_tail.dm b/code/modules/sprite_accessories/tails/_accessory_tail.dm index 311e416366e6..031e7ac40793 100644 --- a/code/modules/sprite_accessories/tails/_accessory_tail.dm +++ b/code/modules/sprite_accessories/tails/_accessory_tail.dm @@ -59,17 +59,3 @@ /decl/sprite_accessory/tail/none/hide_tail/accessory_is_available(mob/owner, decl/species/species, decl/bodytype/bodytype, list/traits) . = ..() && (BP_TAIL in bodytype.has_limbs) - -/* -// Leaving these in for future reference. -/decl/sprite_accessory/tail/debug - name = "Debug Tail" - uid = "acc_tail_debug" - is_whitelisted = "DEBUG" - -/decl/sprite_accessory/tail/debug_inner - name = "Debug Two-Tone Tail" - uid = "acc_tail_debug2" - is_whitelisted = "DEBUG" - accessory_metadata_types = list(SAM_COLOR, SAM_COLOR_INNER) -*/ diff --git a/code/modules/submaps/_submap.dm b/code/modules/submaps/_submap.dm index c2fde5b895d8..93bad99848de 100644 --- a/code/modules/submaps/_submap.dm +++ b/code/modules/submaps/_submap.dm @@ -45,7 +45,7 @@ qdel(src) return - var/obj/effect/overmap/visitable/cell = global.overmap_sectors[num2text(associated_z)] + var/obj/effect/overmap/visitable/cell = global.overmap_sectors[associated_z] if(istype(cell)) sync_cell(cell) diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 1ed731ee4546..4d25ab85a850 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -328,6 +328,9 @@ else return ..() +/obj/item/organ/internal/proc/get_attachment_failure_reason(obj/item/organ/external/affected, robotic = FALSE) + return FALSE + /decl/surgery_step/internal/attach_organ/pre_surgery_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/list/attachable_organs @@ -342,7 +345,7 @@ if(!LAZYLEN(attachable_organs)) return FALSE - var/obj/item/organ/organ_to_replace = show_radial_menu(user, tool, attachable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) + var/obj/item/organ/internal/organ_to_replace = show_radial_menu(user, tool, attachable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) if(!organ_to_replace) return FALSE @@ -350,11 +353,10 @@ to_chat(user, SPAN_WARNING("You can't find anywhere to attach \the [organ_to_replace] to!")) return FALSE - if(istype(organ_to_replace, /obj/item/organ/internal/augment)) - var/obj/item/organ/internal/augment/A = organ_to_replace - if(!(A.augment_flags & AUGMENTATION_ORGANIC)) - to_chat(user, SPAN_WARNING("\The [A] cannot function within a non-robotic limb.")) - return FALSE + var/attach_failure_reason = organ_to_replace.get_attachment_failure_reason(affected, robotic = FALSE) // if this returns FALSE, it can attach + if(attach_failure_reason) + to_chat(user, attach_failure_reason) + return FALSE var/decl/species/species = target.get_species() if(!species) diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 07dd1eb2d65d..ce4bd511bd65 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -463,12 +463,12 @@ var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) radial_button.name = "Reattach \the [I]" LAZYSET(removable_organs, I.organ_tag, radial_button) - var/organ_to_replace = show_radial_menu(user, tool, removable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) + var/obj/item/organ/internal/organ_to_replace = show_radial_menu(user, tool, removable_organs, radius = 42, require_near = TRUE, use_labels = RADIAL_LABELS_OFFSET, check_locs = list(tool)) if(!organ_to_replace) return FALSE - var/obj/item/organ/internal/augment/A = organ_to_replace - if(istype(A) && !(A.augment_flags & AUGMENTATION_MECHANIC)) - to_chat(user, SPAN_WARNING("\The [A] cannot function within a robotic limb.")) + var/attach_failure_reason = organ_to_replace.get_attachment_failure_reason(affected, robotic = TRUE) // if this returns FALSE, it can attach + if(attach_failure_reason) + to_chat(user, attach_failure_reason) return FALSE return organ_to_replace diff --git a/code/modules/tools/tool_serde.dm b/code/modules/tools/tool_serde.dm new file mode 100644 index 000000000000..f94e251715b2 --- /dev/null +++ b/code/modules/tools/tool_serde.dm @@ -0,0 +1,9 @@ +/obj/item/tool/Serialize() + . = ..() + SERIALIZE_DECL_IF_MODIFIED(handle_material, /obj/item/tool) + SERIALIZE_DECL_IF_MODIFIED(binding_material, /obj/item/tool) + +/obj/item/tool/Deserialize(list/instance_map) + . = ..() + DESERIALIZE_DECL_TO_TYPE(handle_material) + DESERIALIZE_DECL_TO_TYPE(binding_material) diff --git a/code/modules/vehicles/bike.dm b/code/modules/vehicles/bike.dm index 7522827dc86f..49e6274920d1 100644 --- a/code/modules/vehicles/bike.dm +++ b/code/modules/vehicles/bike.dm @@ -32,7 +32,7 @@ update_icon() /obj/vehicle/bike/user_buckle_mob(mob/living/M, mob/user) - return load(M) + return load_onto_vehicle(M) /obj/vehicle/bike/verb/toggle() set name = "Toggle Engine" @@ -89,11 +89,12 @@ qdel(trail) trail = null -/obj/vehicle/bike/load(var/atom/movable/loading) +/obj/vehicle/bike/load_onto_vehicle(var/atom/movable/loading) + if(!isliving(loading)) + return FALSE var/mob/living/M = loading - if(!istype(M)) return 0 if(M.buckled || M.anchored || M.restrained() || !Adjacent(M) || !M.Adjacent(src)) - return 0 + return FALSE return ..(M) /obj/vehicle/bike/emp_act(var/severity) @@ -124,14 +125,14 @@ /obj/vehicle/bike/receive_mouse_drop(atom/dropping, mob/user, params) . = ..() if(!. && istype(dropping, /atom/movable)) - if(!load(dropping)) + if(!load_onto_vehicle(dropping)) to_chat(user, SPAN_WARNING("You were unable to load \the [dropping] onto \the [src].")) return TRUE /obj/vehicle/bike/attack_hand(var/mob/user) if(user != load) return ..() - unload(load) + unload_from_vehicle(load) to_chat(user, "You unbuckle yourself from \the [src].") return TRUE @@ -139,7 +140,7 @@ if(user != load || !on) return if(user.incapacitated()) - unload(user) + unload_from_vehicle(user) visible_message("\The [user] falls off \the [src]!") return return Move(get_step(src, direction)) diff --git a/code/modules/vehicles/cargo_train.dm b/code/modules/vehicles/cargo_train.dm index f828d2f4e1c5..4fcb9ee46ce8 100644 --- a/code/modules/vehicles/cargo_train.dm +++ b/code/modules/vehicles/cargo_train.dm @@ -1,18 +1,21 @@ -/obj/vehicle/train/cargo/engine +/obj/vehicle/train/engine name = "cargo train tug" - desc = "A rideable electric car designed for pulling cargo trolleys." + desc = "A ridable electric car designed for pulling cargo trolleys." icon = 'icons/obj/vehicles.dmi' icon_state = "cargo_engine" on = 0 powered = 1 locked = 0 + load_item_visible = 1 load_offset_x = 0 buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 7) charge_use = 1 KILOWATTS active_engines = 1 + var/car_limit = 3 //how many cars an engine can pull before performance degrades - var/obj/item/key/cargo_train/key + var/obj/item/key/key + var/key_type = /obj/item/key/cargo_train /obj/item/key/cargo_train desc = "A small key on a yellow fob reading \"Choo Choo!\"." @@ -24,34 +27,24 @@ icon_state = "train_keys" w_class = ITEM_SIZE_TINY -/obj/vehicle/train/cargo/trolley - name = "cargo train trolley" - icon = 'icons/obj/vehicles.dmi' - icon_state = "cargo_trailer" - anchored = FALSE - passenger_allowed = 0 - locked = 0 - buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 8) - - load_item_visible = 1 - load_offset_x = 0 - load_offset_y = 4 - - //------------------------------------------- // Standard procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/Initialize() +/obj/vehicle/train/engine/Initialize() . = ..() cell = new /obj/item/cell/high(src) - key = new(src) - var/image/I = new(icon = icon, icon_state = "cargo_engine_overlay") + key = new key_type(src) + update_icon() + turn_off() //so engine verbs are correctly set + +/obj/vehicle/train/engine/on_update_icon() + . = ..() + var/image/I = image(icon, "cargo_engine_overlay") I.plane = plane I.layer = layer - overlays += I - turn_off() //so engine verbs are correctly set + set_overlays(I) -/obj/vehicle/train/cargo/engine/Move(var/turf/destination) +/obj/vehicle/train/engine/Move(var/turf/destination) if(on && cell.charge < (charge_use * CELLRATE)) turn_off() update_stats() @@ -67,46 +60,23 @@ return ..() -/obj/vehicle/train/cargo/trolley/attackby(obj/item/used_item, mob/user) - if(open && IS_WIRECUTTER(used_item)) - passenger_allowed = !passenger_allowed - user.visible_message("[user] [passenger_allowed ? "cuts" : "mends"] a cable in [src].","You [passenger_allowed ? "cut" : "mend"] the load limiter cable.") - return TRUE - return ..() - -/obj/vehicle/train/cargo/engine/attackby(obj/item/used_item, mob/user) - if(istype(used_item, /obj/item/key/cargo_train)) +/obj/vehicle/train/engine/attackby(obj/item/used_item, mob/user) + if(istype(used_item, key_type)) if(!key && user.try_unequip(used_item, src)) key = used_item - verbs += /obj/vehicle/train/cargo/engine/verb/remove_key + verbs |= /obj/vehicle/train/engine/verb/remove_key return TRUE return ..() -//cargo trains are open topped, so there is a chance the projectile will hit the mob ridding the train instead -/obj/vehicle/train/cargo/bullet_act(var/obj/item/projectile/Proj) - if(buckled_mob && prob(70)) - buckled_mob.bullet_act(Proj) - return - ..() - -/obj/vehicle/train/cargo/on_update_icon() - if(open) - icon_state = initial(icon_state) + "_open" - else - icon_state = initial(icon_state) - -/obj/vehicle/train/cargo/trolley/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) - return - -/obj/vehicle/train/cargo/engine/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) +/obj/vehicle/train/engine/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) ..() update_stats() -/obj/vehicle/train/cargo/engine/remove_cell(var/mob/living/human/H) +/obj/vehicle/train/engine/remove_cell(var/mob/living/human/H) ..() update_stats() -/obj/vehicle/train/cargo/engine/Bump(atom/Obstacle) +/obj/vehicle/train/engine/Bump(atom/Obstacle) var/obj/machinery/door/D = Obstacle var/mob/living/human/H = load if(istype(D) && istype(H)) @@ -114,52 +84,47 @@ ..() -/obj/vehicle/train/cargo/trolley/Bump(atom/Obstacle) - if(!lead) - return //so people can't knock others over by pushing a trolley around - ..() - //------------------------------------------- // Train procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/turn_on() +/obj/vehicle/train/engine/turn_on() if(!key) return + if(!cell) + return + ..() + update_stats() + if(on) + verbs |= /obj/vehicle/train/engine/verb/stop_engine + verbs -= /obj/vehicle/train/engine/verb/start_engine else - ..() - update_stats() - - verbs -= /obj/vehicle/train/cargo/engine/verb/stop_engine - verbs -= /obj/vehicle/train/cargo/engine/verb/start_engine - - if(on) - verbs += /obj/vehicle/train/cargo/engine/verb/stop_engine - else - verbs += /obj/vehicle/train/cargo/engine/verb/start_engine + verbs |= /obj/vehicle/train/engine/verb/start_engine + verbs -= /obj/vehicle/train/engine/verb/stop_engine -/obj/vehicle/train/cargo/engine/turn_off() +/obj/vehicle/train/engine/turn_off() ..() + if(!on) + verbs |= /obj/vehicle/train/engine/verb/start_engine + verbs -= /obj/vehicle/train/engine/verb/stop_engine + else + verbs |= /obj/vehicle/train/engine/verb/stop_engine + verbs -= /obj/vehicle/train/engine/verb/start_engine - verbs -= /obj/vehicle/train/cargo/engine/verb/stop_engine - verbs -= /obj/vehicle/train/cargo/engine/verb/start_engine - if(!on) - verbs += /obj/vehicle/train/cargo/engine/verb/start_engine +/obj/vehicle/train/engine/on_update_icon() + if(open) + icon_state = initial(icon_state) + "_open" else - verbs += /obj/vehicle/train/cargo/engine/verb/stop_engine + icon_state = initial(icon_state) -/obj/vehicle/train/cargo/crossed_mob(var/mob/living/victim) +/obj/vehicle/train/engine/crossed_mob(var/mob/living/victim) victim.apply_effects(5, 5) for(var/i = 1 to rand(1,5)) var/obj/item/organ/external/E = pick(victim.get_external_organs()) if(E) victim.apply_damage(rand(5,10), BRUTE, E.organ_tag) -/obj/vehicle/train/cargo/trolley/crossed_mob(var/mob/living/victim) - ..() - attack_log += text("\[[time_stamp()]\] ran over [victim.name] ([victim.ckey])") - -/obj/vehicle/train/cargo/engine/crossed_mob(var/mob/living/victim) +/obj/vehicle/train/engine/crossed_mob(var/mob/living/victim) ..() if(is_train_head() && ishuman(load)) var/mob/living/human/D = load @@ -170,161 +135,96 @@ else attack_log += text("\[[time_stamp()]\] ran over [victim.name] ([victim.ckey])") - //------------------------------------------- // Interaction procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/relaymove(mob/user, direction) +/obj/vehicle/train/engine/relaymove(mob/user, direction) if(user != load || user.incapacitated()) - return 0 - + return FALSE if(is_train_head()) if(direction == global.reverse_dir[dir] && tow) - return 0 + return FALSE if(Move(get_step(src, direction))) - return 1 - return 0 - else - return ..() + return TRUE + return FALSE + return ..() -/obj/vehicle/train/cargo/engine/get_examine_strings(mob/user, distance, infix, suffix) +/obj/vehicle/train/engine/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(distance <= 1) . += "The power light is [on ? "on" : "off"].\nThere are[key ? "" : " no"] keys in the ignition." . += "The charge meter reads [cell? round(cell.percent(), 0.01) : 0]%" -/obj/vehicle/train/cargo/engine/verb/start_engine() +/obj/vehicle/train/engine/verb/start_engine() set name = "Start engine" - set category = "Object" + set category = "Vehicle" set src in view(0) if(!ishuman(usr)) return if(on) - to_chat(usr, "The engine is already running.") + to_chat(usr, SPAN_WARNING("The engine is already running.")) return turn_on() if (on) - to_chat(usr, "You start [src]'s engine.") + to_chat(usr, SPAN_NOTICE("You start \the [src]'s engine.")) else - if(cell.charge < charge_use) - to_chat(usr, "[src] is out of power.") + if(!cell) + to_chat(usr, SPAN_NOTICE("\The [src] doesn't appear to have a power cell!")) + else if(cell.charge < charge_use) + to_chat(usr, SPAN_NOTICE("\The [src] is out of power.")) else - to_chat(usr, "[src]'s engine won't start.") + to_chat(usr, SPAN_NOTICE("\The [src]'s engine won't start.")) -/obj/vehicle/train/cargo/engine/verb/stop_engine() +/obj/vehicle/train/engine/verb/stop_engine() set name = "Stop engine" - set category = "Object" + set category = "Vehicle" set src in view(0) if(!ishuman(usr)) return if(!on) - to_chat(usr, "The engine is already stopped.") + to_chat(usr, SPAN_WARNING("The engine is already stopped.")) return turn_off() if (!on) - to_chat(usr, "You stop [src]'s engine.") + to_chat(usr, SPAN_NOTICE("You stop [src]'s engine.")) -/obj/vehicle/train/cargo/engine/verb/remove_key() +/obj/vehicle/train/engine/verb/remove_key() set name = "Remove key" - set category = "Object" + set category = "Vehicle" set src in view(0) - if(!ishuman(usr)) - return - - if(!key || (load && load != usr)) + if(!isliving(usr) || !key || (load && load != usr)) return if(on) turn_off() + var/mob/living/user = usr + key.dropInto(get_turf(user)) usr.put_in_hands(key) key = null + verbs -= /obj/vehicle/train/engine/verb/remove_key - verbs -= /obj/vehicle/train/cargo/engine/verb/remove_key - -//------------------------------------------- -// Loading/unloading procs -//------------------------------------------- -/obj/vehicle/train/cargo/trolley/load(var/atom/movable/loading) - if(ismob(loading) && !passenger_allowed) - return 0 - if(!istype(loading,/obj/machinery) && !istype(loading,/obj/structure/closet) && !istype(loading,/obj/structure/largecrate) && !istype(loading,/obj/structure/reagent_dispensers) && !istype(loading,/obj/structure/ore_box) && !ishuman(loading)) - return 0 - - //if there are any items you don't want to be able to interact with, add them to this check - // ~no more shielded, emitter armed death trains - if(istype(loading, /obj/machinery)) - load_object(loading) - else - ..() - - if(load) - return 1 - -/obj/vehicle/train/cargo/engine/load(var/atom/movable/loading) - if(!ishuman(loading)) - return 0 - - return ..() - -//Load the object "inside" the trolley and add an overlay of it. -//This prevents the object from being interacted with until it has -// been unloaded. A dummy object is loaded instead so the loading -// code knows to handle it correctly. -/obj/vehicle/train/cargo/trolley/proc/load_object(var/atom/movable/loading) - if(!isturf(loading.loc)) //To prevent loading things from someone's inventory, which wouldn't get handled properly. - return 0 - if(load || loading.anchored) - return 0 - - var/datum/vehicle_dummy_load/dummy_load = new() - load = dummy_load - - if(!load) - return - dummy_load.actual_load = loading - loading.forceMove(src) - - if(load_item_visible) - loading.pixel_x += load_offset_x - loading.pixel_y += load_offset_y - loading.plane = plane - loading.layer = VEHICLE_LOAD_LAYER - - overlays += loading - - //we can set these back now since we have already cloned the icon into the overlay - loading.pixel_x = initial(loading.pixel_x) - loading.pixel_y = initial(loading.pixel_y) - loading.reset_plane_and_layer() - -/obj/vehicle/train/cargo/trolley/unload(var/mob/user, var/direction) - if(istype(load, /datum/vehicle_dummy_load)) - var/datum/vehicle_dummy_load/dummy_load = load - load = dummy_load.actual_load - dummy_load.actual_load = null - qdel(dummy_load) - overlays.Cut() - ..() +/obj/vehicle/train/engine/load_onto_vehicle(var/atom/movable/loading, var/mob/user) + return istype(loading, /mob/living/human) && ..() //------------------------------------------- // Latching/unlatching procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/latch(obj/vehicle/train/T, mob/user) +/obj/vehicle/train/engine/latch(obj/vehicle/train/T, mob/user) if(!istype(T) || !Adjacent(T)) return 0 //if we are attaching a trolley to an engine we don't care what direction // it is in and it should probably be attached with the engine in the lead - if(istype(T, /obj/vehicle/train/cargo/trolley)) + if(istype(T, /obj/vehicle/train/trolley)) T.attach_to(src, user) else var/T_dir = get_dir(src, T) //figure out where T is wrt src @@ -345,24 +245,40 @@ // more engines increases this limit by car_limit per // engine. //------------------------------------------------------- -/obj/vehicle/train/cargo/engine/update_car(var/train_length, var/active_engines) - src.train_length = train_length - src.active_engines = active_engines +/obj/vehicle/train/engine/update_vehicle_move_delay(atom/prev_loc) + ..() + if(is_train_head() && on) + move_delay = max(move_delay, (-car_limit * active_engines) + train_length - active_engines) //limits base overweight so you can't overspeed trains + move_delay *= (1 / max(1, active_engines)) * 2 //overweight penalty (scaled by the number of engines) - //Update move delay - if(!is_train_head() || !on) - move_delay = initial(move_delay) //so that engines that have been turned off don't lag behind - else - move_delay = max(0, (-car_limit * active_engines) + train_length - active_engines) // limits base overweight so you cant overspeed trains - move_delay *= (1 / max(1, active_engines)) * 2 // overweight penalty (scaled by the number of engines) - move_delay += get_config_value(/decl/config/num/movement_run) // base reference speed - move_delay *= 1.1 // makes cargo trains 10% slower than running when not overweight +/obj/vehicle/train/engine/get_alt_interactions(mob/user) + . = ..() + LAZYADD(., /decl/interaction_handler/train/toggle_ignition) + if(key) + LAZYADD(., /decl/interaction_handler/train/remove_key) -/obj/vehicle/train/cargo/trolley/update_car(var/train_length, var/active_engines) - src.train_length = train_length - src.active_engines = active_engines +/decl/interaction_handler/train + abstract_type = /decl/interaction_handler/train + expected_target_type = /obj/vehicle/train/engine - if(!lead && !tow) - anchored = FALSE +/decl/interaction_handler/train/toggle_ignition + name = "Toggle Ignition" + +/decl/interaction_handler/train/toggle_ignition/invoked(atom/target, mob/user, obj/item/prop) + var/obj/vehicle/train/engine/train = target + if(train.on) + train.stop_engine() else - anchored = TRUE + train.start_engine() + +/decl/interaction_handler/train/remove_key + name = "Remove Key" + +/decl/interaction_handler/train/remove_key/is_possible(atom/target, mob/user, obj/item/prop) + if((. = ..())) + var/obj/vehicle/train/engine/train = target + return train.key + +/decl/interaction_handler/train/remove_key/invoked(atom/target, mob/user, obj/item/prop) + var/obj/vehicle/train/engine/train = target + train.remove_key() diff --git a/code/modules/vehicles/cargo_trolley.dm b/code/modules/vehicles/cargo_trolley.dm new file mode 100644 index 000000000000..f41875d4cd96 --- /dev/null +++ b/code/modules/vehicles/cargo_trolley.dm @@ -0,0 +1,103 @@ +/obj/vehicle/train/trolley + name = "cargo train trolley" + desc = "A large, flat platform made for putting things on." + icon = 'icons/obj/vehicles.dmi' + icon_state = "cargo_trailer" + anchored = FALSE + passenger_allowed = 0 + locked = 0 + buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 8) + + load_item_visible = 1 + load_offset_x = 0 + load_offset_y = 4 + + var/static/list/can_load_types = list( + /obj/machinery, + /obj/structure/closet, + /obj/structure/largecrate, + /obj/structure/reagent_dispensers, + /obj/structure/ore_box, + /mob/living/human + ) + +/obj/vehicle/train/trolley/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) + return + +//------------------------------------------- +// Loading/unloading procs +//------------------------------------------- +/obj/vehicle/train/trolley/load_onto_vehicle(var/atom/movable/loading, var/mob/user) + if(ismob(loading) && !passenger_allowed) + return 0 + if(!is_type_in_list(loading, can_load_types)) + return 0 + //if there are any items you don't want to be able to interact with, add them to this check + // ~no more shielded, emitter armed death trains + if(istype(loading, /obj/machinery)) + load_object(loading) + else + ..(loading, user) + return !!load + +//Load the object "inside" the trolley and add an overlay of it. +//This prevents the object from being interacted with until it has +// been unloaded. A dummy object is loaded instead so the loading +// code knows to handle it correctly. +/obj/vehicle/train/trolley/proc/load_object(var/atom/movable/loading) + //To prevent loading things from someone's inventory, which wouldn't get handled properly. + if(!isturf(loading.loc) || load || loading.anchored) + return 0 + var/datum/vehicle_dummy_load/dummy_load = new + dummy_load.actual_load = loading + load = dummy_load + loading.forceMove(src) + update_icon() + +/obj/vehicle/train/trolley/unload_from_vehicle(var/mob/user, var/direction) + if(istype(load, /datum/vehicle_dummy_load)) + var/datum/vehicle_dummy_load/dummy_load = load + load = dummy_load.actual_load + dummy_load.actual_load = null + qdel(dummy_load) + update_icon() + ..() + +/obj/vehicle/train/trolley/on_update_icon() + cut_overlays() + var/datum/vehicle_dummy_load/dummy_load = load + if(istype(dummy_load) && dummy_load.actual_load && load_item_visible) + var/atom/movable/loading = dummy_load.actual_load + loading.pixel_x += load_offset_x + loading.pixel_y += load_offset_y + loading.plane = plane + loading.layer = VEHICLE_LOAD_LAYER + add_overlay(loading) + compile_overlays() // We want to reset the pixel values on our load after this. + //we can set these back now since we have already cloned the icon into the overlay + loading.pixel_x = initial(loading.pixel_x) + loading.pixel_y = initial(loading.pixel_y) + loading.layer = initial(loading.layer) + +/obj/vehicle/train/trolley/update_car(var/train_length, var/active_engines) + ..() + anchored = lead || tow + +/obj/vehicle/train/trolley/Bump(atom/Obstacle) + if(!lead) + return //so people can't knock others over by pushing a trolley around + ..() + +/obj/vehicle/train/trolley/attackby(obj/item/used_item, mob/user) + if(open && IS_WIRECUTTER(used_item)) + passenger_allowed = !passenger_allowed + user.visible_message( + SPAN_NOTICE("\The [user] [passenger_allowed ? "cuts" : "mends"] a cable in [src]."), + SPAN_NOTICE("You [passenger_allowed ? "cut" : "mend"] the load limiter cable.") + ) + return TRUE + return ..() + +/obj/vehicle/train/trolley/crossed_mob(var/mob/living/victim) + ..() + attack_log += text("\[[time_stamp()]\] ran over [victim.name] ([victim.ckey])") diff --git a/code/modules/vehicles/quad_bike.dm b/code/modules/vehicles/quad_bike.dm new file mode 100644 index 000000000000..3d01876bc161 --- /dev/null +++ b/code/modules/vehicles/quad_bike.dm @@ -0,0 +1,198 @@ +/obj/vehicle/train/engine/quadbike //It's a train engine, so it can tow trailers. + name = "electric all terrain vehicle" + desc = "A ridable electric ATV designed for all terrain. Except space." + icon = 'icons/obj/vehicles_64x64.dmi' + icon_state = "quad" + on = 0 + powered = 1 + locked = 0 + load_item_visible = 1 + load_offset_x = 0 + buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 5) + pixel_x = -16 + base_speed = 0.45 + car_limit = 1 //It gets a trailer. That's about it. + active_engines = 1 + key_type = /obj/item/key/quadbike + paint_color = "#ffffff" + layer = OBJ_LAYER + vehicle_transit_type = VEHICLE_QUADBIKE + + var/frame_state = "quad" //Custom-item proofing! + var/paint_base = 'icons/obj/vehicles_64x64.dmi' + var/custom_frame = FALSE + var/datum/composite_sound/vehicle_engine/soundloop + +/obj/vehicle/train/engine/quadbike/Initialize() + cell = new /obj/item/cell/high(src) + key = new key_type(src) + soundloop = new(list(src), FALSE) + . = ..() + turn_off() + update_icon() + +/obj/vehicle/train/engine/quadbike/built/Initialize() + key = new key_type(src) + . = ..() + turn_off() + +/obj/vehicle/train/engine/quadbike/random/Initialize() + paint_color = rgb(rand(1,255),rand(1,255),rand(1,255)) + . = ..() + +/obj/vehicle/train/engine/quadbike/Destroy() + QDEL_NULL(soundloop) + return ..() + +/obj/item/key/quadbike + name = "key" + desc = "A keyring with a small steel key, and a blue fob reading \"ZOOM!\"." + icon = 'icons/obj/vehicles.dmi' + icon_state = "quad_keys" + w_class = ITEM_SIZE_TINY + +/obj/vehicle/train/engine/quadbike/forceMove(turf/destination) + var/atom/old_loc = loc + if((. = ..())) + update_vehicle_move_delay(old_loc) + handle_vehicle_icon() + +/obj/vehicle/train/engine/quadbike/Move(turf/destination) + var/atom/old_loc = loc + if((. = ..())) + update_vehicle_move_delay(old_loc) + handle_vehicle_icon() + +/obj/vehicle/train/engine/quadbike/update_vehicle_move_delay(atom/prev_loc) + ..() + update_car(train_length, active_engines) + +/obj/vehicle/train/engine/quadbike/proc/handle_vehicle_icon() + switch(dir) //Due to being a Big Boy sprite, it has to have special pixel shifting to look 'normal' when being driven. + if(1) + pixel_y = -6 + if(2) + pixel_y = -6 + if(4) + pixel_y = 0 + if(8) + pixel_y = 0 + +/obj/vehicle/train/engine/quadbike/attackby(obj/item/used_item, mob/user) + if(istype(used_item, /obj/item/multitool) && open) + var/new_paint = input("Please select a paint color.", "Trailer Color", paint_color) as color|null + if(new_paint && !QDELETED(src) && !QDELETED(used_item) && !QDELETED(user) && !user.incapacitated() && user.get_active_held_item() == used_item) + paint_color = new_paint + update_icon() + return TRUE + return ..() + +/obj/vehicle/train/engine/quadbike/on_update_icon() + ..() + cut_overlays() + + if(custom_frame) + var/image/Bodypaint = new(icon = 'icons/obj/custom_items_vehicle.dmi', icon_state = "[frame_state]_a") + Bodypaint.layer = layer + Bodypaint.color = paint_color + add_overlay(Bodypaint) + + var/image/Overmob = new(icon = 'icons/obj/custom_items_vehicle.dmi', icon_state = "[frame_state]_overlay") //over mobs + var/image/Overmob_color = new(icon = 'icons/obj/custom_items_vehicle.dmi', icon_state = "[frame_state]_overlay_a") //over the over mobs, gives the color. + Overmob.layer = layer + 0.2 + Overmob_color.layer = layer + 0.2 + Overmob_color.color = paint_color + add_overlay(Overmob) + add_overlay(Overmob_color) + return + + var/image/Bodypaint = new(icon = paint_base, icon_state = "[frame_state]_a", layer = src.layer) + Bodypaint.color = paint_color + add_overlay(Bodypaint) + + var/image/Overmob = new(icon = paint_base, icon_state = "[frame_state]_overlay", layer = src.layer + 0.2) //over mobs + var/image/Overmob_color = new(icon = paint_base, icon_state = "[frame_state]_overlay_a", layer = src.layer + 0.2) //over the over mobs, gives the color. + Overmob.layer = ABOVE_HUMAN_LAYER + Overmob_color.layer = ABOVE_HUMAN_LAYER + Overmob_color.color = paint_color + + add_overlay(Overmob) + add_overlay(Overmob_color) + +/obj/vehicle/train/engine/quadbike/Bump(atom/Obstacle) + if(!istype(Obstacle, /atom/movable)) + return + var/atom/movable/A = Obstacle + + if(!A.anchored) + var/turf/T = get_step(A, dir) + if(isturf(T)) + A.Move(T) //bump things away when hit + + if(istype(A, /mob/living)) + var/mob/living/M = A + visible_message(SPAN_DANGER("\The [src] knocks over \the [M]!")) + M.apply_effects(2, 2) // Knock people down for a short moment + M.apply_damages(8 / move_delay) // Smaller amount of damage than a tug, since this will always be possible because Quads don't have safeties. + var/list/throw_dirs = all_throw_dirs.Copy() + if(!emagged) // By the power of Bumpers TM, it won't throw them ahead of the quad's path unless it's emagged or the person turns. + take_damage(round(M.mob_size / 2)) + throw_dirs -= dir + throw_dirs -= get_dir(M, src) //Don't throw it AT the quad either. + else + take_damage(round(M.mob_size / 4)) // Less damage if they actually put the point in to emag it. + var/turf/T2 = get_step(A, pick(throw_dirs)) + M.throw_at(T2, 1, 1, src) + if(isliving(load)) + var/mob/living/D = load + to_chat(D, SPAN_DANGER("You hit \the [M]!")) + admin_attack_log(D, M, "Ran over with [src.name]") + +/obj/vehicle/train/engine/quadbike/crossed_mob(mob/living/victim) + . = ..() + var/list/throw_dirs = all_throw_dirs.Copy() + if(!emagged) + throw_dirs -= dir + if(tow) + throw_dirs -= get_dir(victim, tow) //Don't throw it at the trailer either. + var/turf/T = get_step(victim, pick(throw_dirs)) + victim.throw_at(T, 1, 1, src) + +/obj/vehicle/train/engine/quadbike/turn_on() + ..() + if(on) + visible_message(SPAN_NOTICE("\The [src] rumbles to life."), "You hear something rumble deeply.") + soundloop.start() + +/obj/vehicle/train/engine/quadbike/turn_off() + if(on) + visible_message(SPAN_NOTICE("\The [src] putters before turning off."), "You hear something putter slowly.") + soundloop.stop() + ..() + +/obj/vehicle/train/engine/quadbike/snowmobile + name = "snowmobile" + desc = "An electric snowmobile for traversing snow and ice with ease! Other terrain, not so much." + icon = 'icons/obj/vehicles.dmi' + icon_state = "snowmobile" + load_item_visible = 1 + base_speed = 0.6 + car_limit = 0 + key_type = /obj/item/key/snowmobile + frame_state = "snowmobile" + paint_base = 'icons/obj/vehicles.dmi' + pixel_x = 0 + water_delay = 6 + +/obj/item/key/snowmobile + name = "key" + desc = "A keyring with an ice-blue fob reading \"CHILL\"." + icon = 'icons/obj/vehicles.dmi' + icon_state = "sno_keys" + +/obj/vehicle/train/engine/quadbike/snowmobile/random/Initialize() + paint_color = rgb(rand(1,255),rand(1,255),rand(1,255)) + . = ..() + +/obj/vehicle/train/engine/quadbike/snowmobile/handle_vehicle_icon() + return diff --git a/code/modules/vehicles/quad_trailer.dm b/code/modules/vehicles/quad_trailer.dm new file mode 100644 index 000000000000..a730ab54b656 --- /dev/null +++ b/code/modules/vehicles/quad_trailer.dm @@ -0,0 +1,108 @@ +/* + * Trailer bits and bobs. + */ +/obj/vehicle/train/trolley/trailer + name = "all terrain trailer" + icon = 'icons/obj/vehicles_64x64.dmi' + icon_state = "quadtrailer" + anchored = FALSE + passenger_allowed = 1 + buckle_lying = 1 + locked = 0 + load_item_visible = 1 + load_offset_x = 0 + load_offset_y = 13 + buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 16) + pixel_x = -16 + paint_color = "#ffffff" + var/mob_offset_y = 16 + +/obj/vehicle/train/trolley/trailer/random/Initialize() + paint_color = rgb(rand(1,255),rand(1,255),rand(1,255)) + . = ..() + +/obj/vehicle/train/trolley/trailer/proc/update_load() + if(load) + var/y_offset = load_offset_y + if(istype(load, /mob/living)) + y_offset = mob_offset_y + load.pixel_x = (initial(load.pixel_x) + 16 + load_offset_x + pixel_x) //Base location for the sprite, plus 16 to center it on the 'base' sprite of the trailer, plus the x shift of the trailer, then shift it by the same pixel_x as the trailer to track it. + load.pixel_y = (initial(load.pixel_y) + y_offset + pixel_y) //Same as the above. + return 1 + return 0 +/obj/vehicle/train/trolley/trailer/Initialize() + . = ..() + update_icon() + +/obj/vehicle/train/trolley/trailer/Move() + var/atom/old_loc = loc + if((. = ..())) + update_trolley_offset(old_loc) + +/obj/vehicle/train/trolley/trailer/forceMove() + var/atom/old_loc = loc + if((. = ..())) + update_trolley_offset(old_loc) + +/obj/vehicle/train/trolley/trailer/proc/update_trolley_offset(var/atom/old_loc) + if(lead) + switch(dir) //Due to being a Big Boy sprite, it has to have special pixel shifting to look 'normal'. + if(1) + default_pixel_y = -10 + default_pixel_x = -16 + if(2) + default_pixel_y = 0 + default_pixel_x = -16 + if(4) + default_pixel_y = 0 + default_pixel_x = -25 + if(8) + default_pixel_y = 0 + default_pixel_x = -5 + else + default_pixel_x = initial(default_pixel_x) + default_pixel_y = initial(default_pixel_y) + reset_offsets(0) + update_load() + +/obj/vehicle/train/trolley/trailer/Bump(atom/Obstacle) + if(!istype(Obstacle, /atom/movable)) + return + + var/atom/movable/A = Obstacle + if(!A.anchored) + var/turf/T = get_step(A, dir) + if(isturf(T)) + A.Move(T) //bump things away when hit + + if(istype(A, /mob/living)) + var/mob/living/M = A + visible_message(SPAN_DANGER("\The [src] knocks over \the [M]!")) + M.apply_effects(1, 1) + M.apply_damages(8 / move_delay) + if(load) + M.apply_damages(4/move_delay) + var/list/throw_dirs = all_throw_dirs.Copy() + if(!emagged) + throw_dirs -= dir + var/turf/T2 = get_step(A, pick(throw_dirs)) + M.throw_at(T2, 1, 1, src) + if(isliving(load)) + var/mob/living/D = load + to_chat(D, SPAN_DANGER("You hit \the [M]!")) + admin_attack_log(D, M, "Ran over with \the [src]") + +/obj/vehicle/train/trolley/trailer/on_update_icon() + ..() + var/image/Bodypaint = new(icon = icon, icon_state = "[initial(icon_state)]_a", layer = src.layer) + Bodypaint.color = paint_color + set_overlays(Bodypaint) + +/obj/vehicle/train/trolley/trailer/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/multitool) && open) + var/new_paint = input("Please select paint color.", "Paint Color", paint_color) as color|null + if(new_paint) + paint_color = new_paint + update_icon() + return + ..() diff --git a/code/modules/vehicles/train.dm b/code/modules/vehicles/train.dm index 0cb43501e1e8..59665c6b326f 100644 --- a/code/modules/vehicles/train.dm +++ b/code/modules/vehicles/train.dm @@ -5,17 +5,18 @@ max_health = 100 fire_dam_coeff = 0.7 brute_dam_coeff = 0.5 + layer = ABOVE_HUMAN_LAYER var/passenger_allowed = 1 - var/active_engines = 0 var/train_length = 0 - var/obj/vehicle/train/lead var/obj/vehicle/train/tow + var/static/list/all_throw_dirs = list(NORTH, SOUTH, EAST, WEST, NORTHWEST, NORTHEAST, SOUTHWEST, SOUTHEAST) + /obj/vehicle/train/user_buckle_mob(mob/living/M, mob/user) - return load(M) + return load_onto_vehicle(M) //------------------------------------------- // Standard procs @@ -95,7 +96,7 @@ return 1 return 0 - unload(user, direction) + unload_from_vehicle(user, direction) to_chat(user, "You climb down from [src].") return 1 @@ -110,7 +111,7 @@ /obj/vehicle/train/receive_mouse_drop(atom/dropping, mob/user, params) . = ..() if(!. && istype(dropping, /atom/movable)) - if(!load(dropping)) + if(!load_onto_vehicle(dropping)) to_chat(user, SPAN_WARNING("You were unable to load \the [dropping] onto \the [src].")) return TRUE @@ -121,9 +122,9 @@ if(user != load && (user in src)) user.forceMove(loc) else if(load) - unload(user) + unload_from_vehicle(user) else if(!load && !user.buckled) - load(user) + load_onto_vehicle(user) return TRUE /obj/vehicle/train/verb/unlatch_v() @@ -235,5 +236,7 @@ T.update_car(train_length, active_engines) T = T.lead -/obj/vehicle/train/proc/update_car(var/train_length, var/active_engines) - return +/obj/vehicle/train/proc/update_car(var/_train_length, var/_active_engines) + SHOULD_CALL_PARENT(TRUE) + train_length = _train_length + active_engines = _active_engines diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index 111eb94818e2..6086aa1fab26 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -6,7 +6,7 @@ /obj/vehicle name = "vehicle" icon = 'icons/obj/vehicles.dmi' - layer = ABOVE_HUMAN_LAYER + layer = OBJ_LAYER density = TRUE anchored = TRUE animate_movement=1 @@ -17,6 +17,11 @@ buckle_movable = 1 buckle_lying = 0 + var/const/VEHICLE_GENERIC = 1 + var/const/VEHICLE_QUADBIKE = 2 + var/const/VEHICLE_SNOWMOBILE = 3 + + var/vehicle_transit_type = VEHICLE_GENERIC var/attack_log = null var/on = 0 var/fire_dam_coeff = 1.0 @@ -26,7 +31,13 @@ var/stat = 0 var/emagged = 0 var/powered = 0 //set if vehicle is powered and should use fuel when moving - var/move_delay = 1 //set this to limit the speed of the vehicle + + /// How long a single move takes with this vehicle. + var/move_delay = 1 + /// The base delay of a move with this vehicle, assuming no terrain modifiers. If null, uses default running + var/base_speed + /// Speed when a location is flooded. + var/water_delay = 4 var/obj/item/cell/cell var/charge_use = 200 // W @@ -35,10 +46,31 @@ var/load_item_visible = 1 //set if the loaded item should be overlayed on the vehicle sprite var/load_offset_x = 0 //pixel_x offset for item overlay var/load_offset_y = 0 //pixel_y offset for item overlay - //------------------------------------------- // Standard procs //------------------------------------------- +/obj/vehicle/Initialize(mapload) + update_vehicle_move_delay(null) + base_speed ||= get_config_value(/decl/config/num/movement_run) + . = ..() + +/obj/vehicle/proc/update_vehicle_move_delay(atom/prev_loc) + + var/turf/floor/prev_turf = prev_loc + var/turf/floor/this_turf = loc + if(istype(prev_turf) && istype(this_turf) && this_turf.get_topmost_flooring() == prev_turf.get_topmost_flooring() && this_turf.check_fluid_depth(FLUID_SHALLOW) == prev_turf.check_fluid_depth(FLUID_SHALLOW)) + return // Same speed if terrain type doesn't change + + var/terrain_mod + if(loc?.check_fluid_depth(FLUID_SHALLOW)) + terrain_mod = water_delay + else if(istype(this_turf)) + terrain_mod = this_turf.get_vehicle_transit_delay(src) + + if(isnull(terrain_mod)) + move_delay = base_speed + else + move_delay = base_speed * terrain_mod /obj/vehicle/Move() if(world.time > l_move_time + move_delay) @@ -162,7 +194,7 @@ /obj/vehicle/unbuckle_mob(mob/user) . = ..(user) if(load == .) - unload(.) + unload_from_vehicle(.) //------------------------------------------- // Vehicle procs @@ -207,7 +239,7 @@ var/mob/living/M = load M.apply_effects(5, 5) - unload() + unload_from_vehicle() new /obj/effect/gibspawner/robot(my_turf) new /obj/effect/decal/cleanable/blood/oil(src.loc) @@ -261,7 +293,7 @@ // the vehicle load() definition before // calling this parent proc. //------------------------------------------- -/obj/vehicle/proc/load(var/atom/movable/loading) +/obj/vehicle/proc/load_onto_vehicle(var/atom/movable/loading) //This loads objects onto the vehicle so they can still be interacted with. //Define allowed items for loading in specific vehicle definitions. if(!isturf(loading.loc)) //To prevent loading things from someone's inventory, which wouldn't get handled properly. @@ -293,7 +325,7 @@ return 1 -/obj/vehicle/proc/unload(var/mob/user, var/direction) +/obj/vehicle/proc/unload_from_vehicle(var/mob/user, var/direction) if(!load) return diff --git a/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm b/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm index c56e354022c8..c2d373859706 100644 --- a/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm +++ b/code/modules/xenoarcheaology/artifacts/standalone/gigadrill.dm @@ -12,6 +12,10 @@ var/active = 0 var/drill_time = 10 var/turf/drilling_turf + matter = list( + /decl/material/solid/metal/plasteel/ocp = MATTER_AMOUNT_PRIMARY, + /decl/material/solid/gemstone/diamond = MATTER_AMOUNT_REINFORCEMENT, + ) /obj/machinery/giga_drill/physical_attack_hand(mob/user) if(active) diff --git a/code/modules/xenoarcheaology/finds/find_types/guns.dm b/code/modules/xenoarcheaology/finds/find_types/guns.dm index 3111921fc19e..1a0da34cc8b5 100644 --- a/code/modules/xenoarcheaology/finds/find_types/guns.dm +++ b/code/modules/xenoarcheaology/finds/find_types/guns.dm @@ -47,7 +47,7 @@ /obj/item/gun/energy/laser/practice, /obj/item/gun/energy/laser, /obj/item/gun/energy/xray, - /obj/item/gun/energy/captain + /obj/item/gun/energy/retro/captain ) var/egun_icons = list( 'icons/obj/guns/xenoarch/egun_1.dmi', diff --git a/code/unit_tests/equipment_tests.dm b/code/unit_tests/equipment_tests.dm index 3a1390139822..4c1ebac20fdf 100644 --- a/code/unit_tests/equipment_tests.dm +++ b/code/unit_tests/equipment_tests.dm @@ -120,7 +120,7 @@ "[slot_gloves_str]" = /obj/item/clothing/gloves/rainbow, "[slot_l_ear_str]" = /obj/item/clothing/head/hairflower, "[slot_r_ear_str]" = /obj/item/clothing/head/hairflower, - "[slot_belt_str]" = /obj/item/ore, // note: this should be an item without ITEM_FLAG_IS_BELT + "[slot_belt_str]" = /obj/item/ore_satchel, // note: this should be an item without ITEM_FLAG_IS_BELT "[slot_wear_suit_str]" = /obj/item/clothing/suit/chickensuit ) diff --git a/code/unit_tests/icon_tests.dm b/code/unit_tests/icon_tests.dm index 279daba8a344..89814527ca65 100644 --- a/code/unit_tests/icon_tests.dm +++ b/code/unit_tests/icon_tests.dm @@ -85,42 +85,59 @@ return 1 /datum/unit_test/icon_test/signs_shall_have_existing_icon_states - name = "ICON STATE: Signs shall have existing icon states" - var/list/skip_types = list( - // Posters use a decl to set their icon and handle their own validation. - /obj/structure/sign/poster - ) + name = "ICON STATE: Sign Subtypes Shall Have Existing Icon States" /datum/unit_test/icon_test/signs_shall_have_existing_icon_states/start_test() var/list/failures = list() - for(var/sign_type in typesof(/obj/structure/sign)) - var/obj/structure/sign/sign = sign_type - if(TYPE_IS_ABSTRACT(sign)) - continue + var/static/list/skip_icon_state_checks = list( + // Posters use a decl to set their icon and handle their own validation. + /obj/structure/sign/poster + ) + + var/list/icon_states_to_find = list() + for(var/obj/structure/sign/sign as anything in typesof(/obj/structure/sign)) var/skip = FALSE - for(var/skip_type in skip_types) - if(ispath(sign_type, skip_type)) + for(var/skip_type in skip_icon_state_checks) + if(ispath(sign, skip_type)) skip = TRUE break if(skip) continue - var/check_state = initial(sign.icon_state) - if(!check_state) - failures += "[sign] - null icon_state" - continue - var/check_icon = initial(sign.icon) - if(!check_icon) - failures += "[sign] - null icon_state" + var/sign_state = sign::icon_state + var/sign_icon = sign::icon + + if(!(sign_icon in icon_states_to_find)) + icon_states_to_find[sign_icon] = icon_states(sign_icon) || list() + icon_states_to_find[sign_icon] -= sign_state + + if(TYPE_IS_ABSTRACT(sign)) continue - if(!check_state_in_icon(check_state, check_icon)) - failures += "[sign] - missing icon_state '[check_state]' in icon '[check_icon]" - if(failures.len) - fail("Signs with missing icon states:\n\t-[jointext(failures, "\n\t-")]") + + if(!sign_icon) + failures += "[sign] - missing icon" + else if(!istext(sign_state)) + failures += "[sign] - missing or invalid icon_state" + else if(!check_state_in_icon(sign_state, sign_icon)) + failures += "[sign] - missing icon_state '[sign_state]' from icon '[sign_icon]'" + + var/static/list/skip_extraneous_state_checks = list( + // Barsign icon_state is set by user, skip testing it here. + 'icons/obj/barsigns.dmi' + ) + + for(var/sign_icon in icon_states_to_find) + var/list/remaining = icon_states_to_find[sign_icon] + if(!(sign_icon in skip_extraneous_state_checks) && length(remaining)) + failures += "[sign_icon] - unused icon_states: [jointext(remaining, ", ")]" + + if(length(failures)) + fail("[length(failures)] issue\s with sign icons or icon states:\n[jointext(failures, "\n")]") else - pass("All signs have valid icon states.") + pass("All signs have valid icon states and no extraneous icon states.") + return 1 /datum/unit_test/icon_test/random_spawners_shall_have_existing_icon_states diff --git a/code/unit_tests/map_tests.dm b/code/unit_tests/map_tests.dm index 20ba36dac7da..434712cb0f9f 100644 --- a/code/unit_tests/map_tests.dm +++ b/code/unit_tests/map_tests.dm @@ -23,6 +23,9 @@ var/bad_msg = "--------------- [A.proper_name]([A.type])" var/exemptions = get_exemptions(A) + if(exemptions & global.using_map.SKIP_ALL_TESTS) + continue + if(!A.apc && !(exemptions & global.using_map.NO_APC)) log_bad("[bad_msg] lacks an APC.") area_good = 0 @@ -815,6 +818,15 @@ var/global/_unit_test_sort_junctions = list() package.test = src packages_awaiting_delivery[package] = start_tag +/datum/unit_test/networked_disposals_shall_deliver_tagged_packages/fail(message) + . = ..() + if(length(packages_awaiting_delivery)) + log_unit_test("[ascii_red]!!! FAILURE !!! [length(packages_awaiting_delivery)] package\s still processing.") + for(var/obj/structure/disposalholder/unit_test/package in packages_awaiting_delivery) + var/turf/package_turf = get_turf(package) + log_unit_test("[ascii_red] - [packages_awaiting_delivery[package]]: [package_turf?.x || "NULL"],[package_turf?.y || "NULL"],[package_turf?.z || "NULL"]") + packages_awaiting_delivery.Cut() + /obj/structure/disposalholder/unit_test is_spawnable_type = FALSE // NO var/datum/unit_test/networked_disposals_shall_deliver_tagged_packages/test diff --git a/code/unit_tests/mob_tests.dm b/code/unit_tests/mob_tests.dm index 2a0cc80fdbb1..9e334a8d1477 100644 --- a/code/unit_tests/mob_tests.dm +++ b/code/unit_tests/mob_tests.dm @@ -24,6 +24,8 @@ T = locate(/turf/space) var/datum/mob_snapshot/dummy_appearance = new for(var/decl/bodytype/bodytype in decls_repository.get_decls_of_subtype_unassociated(/decl/bodytype)) + if(bodytype.skip_organ_validation) + continue var/decl/species/species = bodytype.get_user_species_for_validation() if(!species) continue @@ -208,6 +210,7 @@ var/msg = "Damage taken: [ending_damage] out of [damage_amount] || expected: [expected_msg] \[Overall Health:[ending_health] (Initial: [initial_health]\]" if(failure) + msg += " || species: [H.get_species()?.type || "NULL"] || bodytype: [H.get_bodytype()?.type || "NULL"]" fail(msg) else pass(msg) @@ -305,6 +308,8 @@ var/failed = FALSE var/datum/mob_snapshot/dummy_appearance = new for(var/decl/bodytype/bodytype in decls_repository.get_decls_of_subtype_unassociated(/decl/bodytype)) + if(bodytype.skip_organ_validation) + continue var/decl/species/species = bodytype.get_user_species_for_validation() if(!species) continue diff --git a/code/unit_tests/organ_tests.dm b/code/unit_tests/organ_tests.dm index 72d20645b58b..69f4266c6b49 100644 --- a/code/unit_tests/organ_tests.dm +++ b/code/unit_tests/organ_tests.dm @@ -118,6 +118,8 @@ var/failcount = 0 var/datum/mob_snapshot/dummy_appearance = new for(var/decl/bodytype/bodytype in decls_repository.get_decls_of_subtype_unassociated(/decl/bodytype)) + if(bodytype.skip_organ_validation) + continue var/decl/species/species = bodytype.get_user_species_for_validation() if(!species) continue @@ -255,6 +257,8 @@ var/failcount = 0 var/datum/mob_snapshot/dummy_appearance = new for(var/decl/bodytype/bodytype in decls_repository.get_decls_of_subtype_unassociated(/decl/bodytype)) + if(bodytype.skip_organ_validation) + continue var/decl/species/species = bodytype.get_user_species_for_validation() if(!species) continue diff --git a/code/unit_tests/shuttle_tests.dm b/code/unit_tests/shuttle_tests.dm index 0d0d7118d49b..12467d8b13cd 100644 --- a/code/unit_tests/shuttle_tests.dm +++ b/code/unit_tests/shuttle_tests.dm @@ -4,8 +4,8 @@ /datum/unit_test/generic_shuttle_landmarks_shall_not_appear_in_restricted_list/start_test() var/fail = FALSE - for(var/sz in global.overmap_sectors) - var/obj/effect/overmap/visitable/sector = global.overmap_sectors[sz] + for(var/sz, sec in global.overmap_sectors) + var/obj/effect/overmap/visitable/sector = sec if(!istype(sector)) continue var/list/failures = list() diff --git a/code/unit_tests/unique_tests.dm b/code/unit_tests/unique_tests.dm index cef4aba20aab..c0c100bcd7c2 100644 --- a/code/unit_tests/unique_tests.dm +++ b/code/unit_tests/unique_tests.dm @@ -181,9 +181,9 @@ continue group_by(decls_by_uid, decl_instance.uid, decl_type) - var/number_of_issues = number_of_issues(decls_by_uid, "Language UIDs") + var/number_of_issues = number_of_issues(decls_by_uid, "/decl UIDs") if(number_of_issues) - fail("[number_of_issues] issue\s with decl UIDs found.") + fail("[number_of_issues] issue\s with /decl UIDs found.") else pass("All decl UIDs are unique.") return TRUE diff --git a/html/changelog.html b/html/changelog.html index 6e4661c68847..9e0ead821a49 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,16 +52,34 @@ -->

-

30 December 2025

+

10 January 2026

Penelope Haze updated:

-

02 November 2025

+

06 January 2026

MistakeNot4892 updated:

+ +

02 January 2026

+

Sutures updated:

+ + +

31 December 2025

+

Penelope Haze updated:

+ + +

30 December 2025

+

Penelope Haze updated:

+
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 235786373a59..8e915de101ab 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15052,3 +15052,18 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Penelope Haze: - tweak: Iron can now optionally be used instead of aluminum when filling sealant tanks for sealant guns. +2025-12-31: + Penelope Haze: + - admin: Kharmaan nymph jobbans now use the "Mantid Nymph" role in the jobban menu, + rather than the Semi-Antagonist role. +2026-01-02: + Sutures: + - tweak: The equip hotkey can now place items into backpacks and wallets. +2026-01-06: + MistakeNot4892: + - tweak: Internal code for neo-avian markings has been reworked, check your sprite + accessories. +2026-01-10: + Penelope Haze: + - balance: Certain supply packs that give a random selection are now priced more + fairly. diff --git a/html/changelogs/AutoChangeLog-pr-5223.yml b/html/changelogs/AutoChangeLog-pr-5223.yml deleted file mode 100644 index 4a934dba46d3..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5223.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Penelope Haze -changes: - - {admin: 'Kharmaan nymph jobbans now use the "Mantid Nymph" role in the jobban - menu, rather than the Semi-Antagonist role.'} -delete-after: true diff --git a/icons/clothing/head/hood_parka.dmi b/icons/clothing/head/hood_parka.dmi new file mode 100644 index 000000000000..7b006dea4245 Binary files /dev/null and b/icons/clothing/head/hood_parka.dmi differ diff --git a/icons/clothing/mask/gas_mask.dmi b/icons/clothing/mask/gas_mask.dmi index 0a3fee24ee20..ef68f54325ee 100644 Binary files a/icons/clothing/mask/gas_mask.dmi and b/icons/clothing/mask/gas_mask.dmi differ diff --git a/icons/clothing/mask/gas_mask_alt.dmi b/icons/clothing/mask/gas_mask_alt.dmi index 4d6f7d872663..8a3891ef624c 100644 Binary files a/icons/clothing/mask/gas_mask_alt.dmi and b/icons/clothing/mask/gas_mask_alt.dmi differ diff --git a/icons/clothing/suits/wintercoat/parka.dmi b/icons/clothing/suits/wintercoat/parka.dmi new file mode 100644 index 000000000000..acc4af648701 Binary files /dev/null and b/icons/clothing/suits/wintercoat/parka.dmi differ diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 5502343f0d55..a43e06941625 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/effects/impact_effects.dmi b/icons/effects/impact_effects.dmi new file mode 100644 index 000000000000..a3239f69fe2f Binary files /dev/null and b/icons/effects/impact_effects.dmi differ diff --git a/icons/effects/map_effects.dmi b/icons/effects/map_effects.dmi new file mode 100644 index 000000000000..8b2dc22e3c48 Binary files /dev/null and b/icons/effects/map_effects.dmi differ diff --git a/icons/effects/staminabar.dmi b/icons/effects/staminabar.dmi new file mode 100644 index 000000000000..7e93f98b5ce1 Binary files /dev/null and b/icons/effects/staminabar.dmi differ diff --git a/icons/mob/onmob/items/lefthand.dmi b/icons/mob/onmob/items/lefthand.dmi index d7947b3d21b1..f94a41218dea 100644 Binary files a/icons/mob/onmob/items/lefthand.dmi and b/icons/mob/onmob/items/lefthand.dmi differ diff --git a/icons/mob/onmob/items/righthand.dmi b/icons/mob/onmob/items/righthand.dmi index 6561ca1e9d48..c830e2f9fbd6 100644 Binary files a/icons/mob/onmob/items/righthand.dmi and b/icons/mob/onmob/items/righthand.dmi differ diff --git a/icons/mob/simple_animal/hivebot.dmi b/icons/mob/simple_animal/hivebot.dmi deleted file mode 100644 index dff5a696fb4d..000000000000 Binary files a/icons/mob/simple_animal/hivebot.dmi and /dev/null differ diff --git a/icons/mob/simple_animal/hivebots/hivebot_green.dmi b/icons/mob/simple_animal/hivebots/hivebot_green.dmi new file mode 100644 index 000000000000..af859e82b121 Binary files /dev/null and b/icons/mob/simple_animal/hivebots/hivebot_green.dmi differ diff --git a/icons/mob/simple_animal/hivebots/hivebot_red.dmi b/icons/mob/simple_animal/hivebots/hivebot_red.dmi new file mode 100644 index 000000000000..32ab4636993f Binary files /dev/null and b/icons/mob/simple_animal/hivebots/hivebot_red.dmi differ diff --git a/icons/mob/simple_animal/hivebots/hivebot_white.dmi b/icons/mob/simple_animal/hivebots/hivebot_white.dmi new file mode 100644 index 000000000000..ead79af5a6b1 Binary files /dev/null and b/icons/mob/simple_animal/hivebots/hivebot_white.dmi differ diff --git a/icons/mob/simple_animal/hivebots/hivebot_yellow.dmi b/icons/mob/simple_animal/hivebots/hivebot_yellow.dmi new file mode 100644 index 000000000000..421e47424aec Binary files /dev/null and b/icons/mob/simple_animal/hivebots/hivebot_yellow.dmi differ diff --git a/icons/mob/simple_animal/megabot.dmi b/icons/mob/simple_animal/hivebots/megabot.dmi similarity index 96% rename from icons/mob/simple_animal/megabot.dmi rename to icons/mob/simple_animal/hivebots/megabot.dmi index 8f0a174d5c2a..107bd1921534 100644 Binary files a/icons/mob/simple_animal/megabot.dmi and b/icons/mob/simple_animal/hivebots/megabot.dmi differ diff --git a/icons/obj/barsigns.dmi b/icons/obj/barsigns.dmi index 2636de482993..aeb3e4393782 100644 Binary files a/icons/obj/barsigns.dmi and b/icons/obj/barsigns.dmi differ diff --git a/icons/obj/christmas.dmi b/icons/obj/christmas.dmi index a42d14ecff52..dedf006c922a 100644 Binary files a/icons/obj/christmas.dmi and b/icons/obj/christmas.dmi differ diff --git a/icons/obj/custom_items_vehicle.dmi b/icons/obj/custom_items_vehicle.dmi new file mode 100644 index 000000000000..a6afb68bab51 Binary files /dev/null and b/icons/obj/custom_items_vehicle.dmi differ diff --git a/icons/obj/debris_circuit.dmi b/icons/obj/debris_circuit.dmi new file mode 100644 index 000000000000..1cd54c4bcabf Binary files /dev/null and b/icons/obj/debris_circuit.dmi differ diff --git a/icons/obj/debris_device.dmi b/icons/obj/debris_device.dmi new file mode 100644 index 000000000000..4f1935caabf4 Binary files /dev/null and b/icons/obj/debris_device.dmi differ diff --git a/icons/obj/debris_metal.dmi b/icons/obj/debris_metal.dmi new file mode 100644 index 000000000000..6f94ce14c3c3 Binary files /dev/null and b/icons/obj/debris_metal.dmi differ diff --git a/icons/obj/guns/basic_energy.dmi b/icons/obj/guns/basic_energy.dmi index 8bb69581951b..737cde5906db 100644 Binary files a/icons/obj/guns/basic_energy.dmi and b/icons/obj/guns/basic_energy.dmi differ diff --git a/icons/obj/holosign.dmi b/icons/obj/holosign.dmi index cf1781d4235b..a4493a5ee3e6 100644 Binary files a/icons/obj/holosign.dmi and b/icons/obj/holosign.dmi differ diff --git a/icons/obj/items/bladed/knife_survival.dmi b/icons/obj/items/bladed/knife_survival.dmi new file mode 100644 index 000000000000..aee1b2fc710e Binary files /dev/null and b/icons/obj/items/bladed/knife_survival.dmi differ diff --git a/icons/obj/items/device/cataloguer.dmi b/icons/obj/items/device/cataloguer.dmi deleted file mode 100644 index 77851d7d8f33..000000000000 Binary files a/icons/obj/items/device/cataloguer.dmi and /dev/null differ diff --git a/icons/obj/mine.dmi b/icons/obj/mine.dmi new file mode 100644 index 000000000000..1c28f5775952 Binary files /dev/null and b/icons/obj/mine.dmi differ diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index 7225f19d780e..042118ab8a49 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ diff --git a/icons/obj/signs/decks.dmi b/icons/obj/signs/decks.dmi index c2f207f87c32..e624d02927c5 100644 Binary files a/icons/obj/signs/decks.dmi and b/icons/obj/signs/decks.dmi differ diff --git a/icons/obj/signs/departments.dmi b/icons/obj/signs/departments.dmi new file mode 100644 index 000000000000..0b3bb48a4ed6 Binary files /dev/null and b/icons/obj/signs/departments.dmi differ diff --git a/icons/obj/signs/directions.dmi b/icons/obj/signs/directions.dmi index 0ff60dbce788..fb224e2799f1 100644 Binary files a/icons/obj/signs/directions.dmi and b/icons/obj/signs/directions.dmi differ diff --git a/icons/obj/signs/flags.dmi b/icons/obj/signs/flags.dmi new file mode 100644 index 000000000000..e3af8b280d66 Binary files /dev/null and b/icons/obj/signs/flags.dmi differ diff --git a/icons/obj/signs/levels.dmi b/icons/obj/signs/levels.dmi new file mode 100644 index 000000000000..46531830bd84 Binary files /dev/null and b/icons/obj/signs/levels.dmi differ diff --git a/icons/obj/signs/location_signs.dmi b/icons/obj/signs/location_signs.dmi deleted file mode 100644 index 096a46f79cff..000000000000 Binary files a/icons/obj/signs/location_signs.dmi and /dev/null differ diff --git a/icons/obj/signs/medical.dmi b/icons/obj/signs/medical.dmi deleted file mode 100644 index 7b4c8d9f4eb5..000000000000 Binary files a/icons/obj/signs/medical.dmi and /dev/null differ diff --git a/icons/obj/signs/signs.dmi b/icons/obj/signs/signs.dmi new file mode 100644 index 000000000000..022fb48f4c75 Binary files /dev/null and b/icons/obj/signs/signs.dmi differ diff --git a/icons/obj/signs/slim_decks.dmi b/icons/obj/signs/slim_decks.dmi deleted file mode 100644 index 02957d4097a5..000000000000 Binary files a/icons/obj/signs/slim_decks.dmi and /dev/null differ diff --git a/icons/obj/signs/slim_location_signs.dmi b/icons/obj/signs/slim_location_signs.dmi deleted file mode 100644 index 4f8ada811c74..000000000000 Binary files a/icons/obj/signs/slim_location_signs.dmi and /dev/null differ diff --git a/icons/obj/signs/slim_warnings.dmi b/icons/obj/signs/slim_warnings.dmi deleted file mode 100644 index c33488ac81fb..000000000000 Binary files a/icons/obj/signs/slim_warnings.dmi and /dev/null differ diff --git a/icons/obj/signs/warnings.dmi b/icons/obj/signs/warnings.dmi index 1bb003b6d99c..c6952b814f07 100644 Binary files a/icons/obj/signs/warnings.dmi and b/icons/obj/signs/warnings.dmi differ diff --git a/icons/obj/structures/cliffs.dmi b/icons/obj/structures/cliffs.dmi new file mode 100644 index 000000000000..4713360313f7 Binary files /dev/null and b/icons/obj/structures/cliffs.dmi differ diff --git a/icons/obj/structures/salvage.dmi b/icons/obj/structures/salvage.dmi new file mode 100644 index 000000000000..ea2004324aa3 Binary files /dev/null and b/icons/obj/structures/salvage.dmi differ diff --git a/icons/obj/vehicles_64x64.dmi b/icons/obj/vehicles_64x64.dmi new file mode 100644 index 000000000000..50cc6457594a Binary files /dev/null and b/icons/obj/vehicles_64x64.dmi differ diff --git a/icons/turf/flooring/decals.dmi b/icons/turf/flooring/decals.dmi index 3ac9258ed803..d81b442969a2 100644 Binary files a/icons/turf/flooring/decals.dmi and b/icons/turf/flooring/decals.dmi differ diff --git a/maps/away/bearcat/bearcat-1.dmm b/maps/away/bearcat/bearcat-1.dmm index cf784259590e..534accc465e3 100644 --- a/maps/away/bearcat/bearcat-1.dmm +++ b/maps/away/bearcat/bearcat-1.dmm @@ -1935,7 +1935,7 @@ /area/ship/scrap/maintenance/storage) "ef" = ( /obj/structure/closet/crate/plastic, -/obj/item/ore, +/obj/item/ore_satchel, /obj/item/tool/pickaxe, /obj/item/stack/flag/yellow, /obj/item/box/glowsticks, diff --git a/maps/away/bearcat/bearcat-2.dmm b/maps/away/bearcat/bearcat-2.dmm index 14b782b9dba0..ceeee078bfa0 100644 --- a/maps/away/bearcat/bearcat-2.dmm +++ b/maps/away/bearcat/bearcat-2.dmm @@ -75,7 +75,7 @@ id_tag = "scraplock"; name = "External Lockdown" }, -/obj/item/gun/energy/captain, +/obj/item/gun/energy/retro/captain, /turf/floor/tiled/dark/usedup, /area/ship/scrap/command/bridge) "am" = ( @@ -1961,7 +1961,7 @@ /turf/floor/usedup, /area/ship/scrap/crew/hallway/starboard) "ed" = ( -/obj/structure/sign/department/redcross, +/obj/structure/sign/department/cross, /turf/wall, /area/ship/scrap/crew/medbay) "ee" = ( diff --git a/maps/away/casino/casino.dmm b/maps/away/casino/casino.dmm index dc523a800a1c..8d4b0c4ee5da 100644 --- a/maps/away/casino/casino.dmm +++ b/maps/away/casino/casino.dmm @@ -4575,7 +4575,7 @@ /turf/floor/plating, /area/casino/casino_bow) "ne" = ( -/obj/machinery/atmospherics/unary/heater{ +/obj/machinery/atmospherics/unary/temperature/heater{ dir = 8 }, /turf/floor/plating, diff --git a/maps/away/derelict/derelict-station.dmm b/maps/away/derelict/derelict-station.dmm index efcdd0536054..46a25943bbfc 100644 --- a/maps/away/derelict/derelict-station.dmm +++ b/maps/away/derelict/derelict-station.dmm @@ -2423,7 +2423,7 @@ /turf/wall, /area/constructionsite/medical) "iE" = ( -/obj/structure/sign/department/bluecross_2, +/obj/structure/sign/department/cross/blue2, /turf/wall, /area/constructionsite/hallway/aft) "iF" = ( diff --git a/maps/away/errant_pisces/errant_pisces.dm b/maps/away/errant_pisces/errant_pisces.dm index b5179c2bd942..0f53db80c754 100644 --- a/maps/away/errant_pisces/errant_pisces.dm +++ b/maps/away/errant_pisces/errant_pisces.dm @@ -180,9 +180,9 @@ /obj/abstract/landmark/corpse/carp_fisher name = "carp fisher" - corpse_outfits = list(/decl/outfit/corpse/carp_fisher) + corpse_outfits = list(/decl/outfit/corpse_carp_fisher) -/decl/outfit/corpse/carp_fisher +/decl/outfit/corpse_carp_fisher name = "Dead carp fisher" uniform = /obj/item/clothing/jumpsuit/green suit = /obj/item/clothing/suit/apron/overalls diff --git a/maps/away/errant_pisces/errant_pisces.dmm b/maps/away/errant_pisces/errant_pisces.dmm index 77c6de05aef4..e7ea44f352bc 100644 --- a/maps/away/errant_pisces/errant_pisces.dmm +++ b/maps/away/errant_pisces/errant_pisces.dmm @@ -260,7 +260,7 @@ /turf/floor/plating, /area/errant_pisces/bow_starboard) "aJ" = ( -/obj/machinery/atmospherics/unary/heater{ +/obj/machinery/atmospherics/unary/temperature/heater{ dir = 1 }, /turf/floor/plating, @@ -286,7 +286,7 @@ /turf/floor/plating, /area/errant_pisces/bow_port) "aN" = ( -/obj/machinery/atmospherics/unary/heater{ +/obj/machinery/atmospherics/unary/temperature/heater{ dir = 1 }, /turf/floor/plating, @@ -451,7 +451,7 @@ /obj/effect/floor_decal/industrial/warning{ dir = 4 }, -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/floor/plating, /area/errant_pisces/enginering) "bp" = ( @@ -569,7 +569,7 @@ /turf/floor/plating, /area/errant_pisces/enginering) "bK" = ( -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/floor/plating, /area/errant_pisces/enginering) "bL" = ( @@ -4273,7 +4273,7 @@ /area/errant_pisces/general_storage) "lE" = ( /obj/structure/rack, -/obj/item/ore, +/obj/item/ore_satchel, /obj/random/tool, /turf/floor/plating, /area/errant_pisces/general_storage) @@ -4380,11 +4380,11 @@ /turf/floor/tiled/white, /area/errant_pisces/science_wing) "lU" = ( -/obj/vehicle/train/cargo/engine, +/obj/vehicle/train/engine, /turf/floor/plating, /area/errant_pisces/general_storage) "lV" = ( -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/floor/plating, /area/errant_pisces/general_storage) "lW" = ( diff --git a/maps/away/unishi/unishi-2.dmm b/maps/away/unishi/unishi-2.dmm index d6e536822aa5..376eed3145da 100644 --- a/maps/away/unishi/unishi-2.dmm +++ b/maps/away/unishi/unishi-2.dmm @@ -1528,7 +1528,7 @@ /area/unishi/hydro) "er" = ( /obj/effect/vine, -/obj/machinery/atmospherics/unary/freezer{ +/obj/machinery/atmospherics/unary/temperature/freezer{ dir = 8; icon_state = "freezer" }, @@ -1664,7 +1664,7 @@ /area/unishi/hydro) "eJ" = ( /obj/effect/vine, -/obj/machinery/atmospherics/unary/heater{ +/obj/machinery/atmospherics/unary/temperature/heater{ dir = 8 }, /turf/floor/tiled, diff --git a/maps/away/unishi/unishi-3.dmm b/maps/away/unishi/unishi-3.dmm index f5ed1e294bf9..ccb4c5e12c0d 100644 --- a/maps/away/unishi/unishi-3.dmm +++ b/maps/away/unishi/unishi-3.dmm @@ -768,7 +768,7 @@ /turf/floor/tiled/white, /area/unishi/med) "cs" = ( -/obj/structure/sign/department/bluecross_1, +/obj/structure/sign/department/cross/blue, /turf/wall, /area/unishi/med) "ct" = ( diff --git a/maps/away/yacht/yacht.dmm b/maps/away/yacht/yacht.dmm index 43c5ed43367e..03c697787019 100644 --- a/maps/away/yacht/yacht.dmm +++ b/maps/away/yacht/yacht.dmm @@ -92,7 +92,7 @@ /obj/structure/safe, /obj/item/chems/pill/cyanide, /obj/item/rig/medical/equipped, -/obj/item/gun/energy/captain, +/obj/item/gun/energy/retro/captain, /obj/effect/spider/stickyweb, /obj/effect/decal/cleanable/dirt/visible, /turf/floor/laminate/walnut, @@ -1001,7 +1001,7 @@ /turf/floor/plating, /area/yacht/engine) "di" = ( -/obj/machinery/atmospherics/unary/heater, +/obj/machinery/atmospherics/unary/temperature/heater, /turf/floor/plating, /area/yacht/engine) "dj" = ( @@ -1009,7 +1009,7 @@ /turf/floor/plating, /area/yacht/engine) "dk" = ( -/obj/machinery/atmospherics/unary/heater, +/obj/machinery/atmospherics/unary/temperature/heater, /obj/effect/decal/cleanable/dirt/visible, /turf/floor/plating, /area/yacht/engine) @@ -1100,7 +1100,7 @@ /turf/floor/plating, /area/yacht/engine) "dw" = ( -/obj/machinery/atmospherics/unary/heater{ +/obj/machinery/atmospherics/unary/temperature/heater{ dir = 1 }, /obj/effect/decal/cleanable/dirt/visible, diff --git a/maps/example/example-1.dmm b/maps/example/example-1.dmm index 129c5b08e972..500a16664c7c 100644 --- a/maps/example/example-1.dmm +++ b/maps/example/example-1.dmm @@ -334,7 +334,7 @@ /turf/floor/tiled/dark/monotile, /area/example/first) "qR" = ( -/obj/machinery/power/debug_items/infinite_generator, +/obj/machinery/debug_items/infinite_generator, /obj/structure/cable/yellow, /turf/floor, /area/example/first) diff --git a/maps/example/example-2.dmm b/maps/example/example-2.dmm index f1bf69ddda70..51b091fc0d04 100644 --- a/maps/example/example-2.dmm +++ b/maps/example/example-2.dmm @@ -436,7 +436,7 @@ /turf/floor/tiled/steel_grid, /area/example/second) "Ci" = ( -/obj/machinery/power/debug_items/infinite_generator, +/obj/machinery/debug_items/infinite_generator, /obj/structure/cable/yellow, /turf/floor/tiled/white, /area/example/second) diff --git a/maps/example/example-3.dmm b/maps/example/example-3.dmm index 7dda2d9ff4ca..018cb124270b 100644 --- a/maps/example/example-3.dmm +++ b/maps/example/example-3.dmm @@ -198,7 +198,7 @@ /turf/floor/tiled/steel_grid, /area/example/third) "GA" = ( -/obj/machinery/power/debug_items/infinite_generator, +/obj/machinery/debug_items/infinite_generator, /obj/structure/cable/yellow{ icon_state = "0-2" }, diff --git a/maps/exodus/exodus-1.dmm b/maps/exodus/exodus-1.dmm index 2fa3d828f5ed..6454cc223445 100644 --- a/maps/exodus/exodus-1.dmm +++ b/maps/exodus/exodus-1.dmm @@ -1416,7 +1416,7 @@ /turf/floor/plating, /area/exodus/maintenance/sub/starboard) "et" = ( -/obj/structure/sign/directions/supply{ +/obj/structure/sign/directions/cargo/supply{ dir = 1 }, /turf/wall/r_wall/prepainted, @@ -4285,13 +4285,13 @@ /turf/floor/tiled/steel_grid, /area/exodus/engineering/atmos) "ln" = ( -/obj/machinery/atmospherics/unary/freezer{ +/obj/machinery/atmospherics/unary/temperature/freezer{ icon_state = "freezer" }, /turf/floor/tiled/steel_grid, /area/exodus/engineering/atmos) "lo" = ( -/obj/machinery/atmospherics/unary/heater{ +/obj/machinery/atmospherics/unary/temperature/heater{ icon_state = "heater" }, /turf/floor/tiled/steel_grid, diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index c9e92ad63477..a6fd60b3e000 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -11555,7 +11555,7 @@ dir = 4 }, /obj/effect/floor_decal/corner/blue/diagonal, -/obj/structure/sign/warning/pods{ +/obj/structure/sign/directions/pods{ dir = 4; pixel_x = 32 }, @@ -16379,7 +16379,7 @@ /obj/effect/floor_decal/corner/lime{ dir = 10 }, -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_y = -32 }, @@ -16392,7 +16392,7 @@ /obj/effect/floor_decal/corner/lime{ dir = 10 }, -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_y = -32 }, @@ -29918,7 +29918,7 @@ /obj/effect/floor_decal/corner/paleblue{ dir = 5 }, -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_y = 32 }, @@ -33737,7 +33737,7 @@ /turf/floor/bluegrid, /area/exodus/turret_protected/ai_upload) "btO" = ( -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_x = -32 }, @@ -34922,7 +34922,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/storage) "bwe" = ( -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /obj/effect/floor_decal/industrial/outline/yellow, /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/storage) @@ -34933,7 +34933,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/quartermaster/storage) "bwg" = ( -/obj/vehicle/train/cargo/engine, +/obj/vehicle/train/engine, /obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/cable/green{ icon_state = "1-2" @@ -35334,7 +35334,7 @@ /turf/floor/tiled/white, /area/exodus/medical/chemistry) "bwY" = ( -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_x = 32 }, @@ -38443,7 +38443,7 @@ /obj/machinery/light{ dir = 4 }, -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_x = 32 }, @@ -38633,7 +38633,7 @@ /turf/floor/plating, /area/exodus/medical/genetics/cloning) "bDF" = ( -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_x = -32 }, @@ -41296,7 +41296,7 @@ /turf/wall/prepainted, /area/exodus/research/storage) "bIK" = ( -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_x = 32 }, @@ -42587,7 +42587,7 @@ /turf/floor/tiled/white, /area/exodus/research) "bLl" = ( -/obj/machinery/atmospherics/unary/freezer{ +/obj/machinery/atmospherics/unary/temperature/freezer{ icon_state = "freezer" }, /obj/effect/floor_decal/corner/purple{ @@ -42665,7 +42665,7 @@ /turf/floor/tiled/steel_grid, /area/exodus/maintenance/atmos_control) "bLv" = ( -/obj/machinery/atmospherics/unary/heater{ +/obj/machinery/atmospherics/unary/temperature/heater{ icon_state = "heater" }, /obj/effect/floor_decal/corner/purple{ @@ -44497,7 +44497,7 @@ "bPd" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/maintenance, -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_y = 32 }, @@ -45157,7 +45157,7 @@ "bQy" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/maintenance, -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_y = 32 }, @@ -46893,7 +46893,7 @@ /turf/floor/reinforced, /area/exodus/research/misc_lab) "bTT" = ( -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_y = 32 }, @@ -47178,7 +47178,7 @@ pixel_x = -6; pixel_y = 25 }, -/obj/machinery/atmospherics/unary/freezer{ +/obj/machinery/atmospherics/unary/temperature/freezer{ dir = 8; icon_state = "freezer" }, @@ -48633,7 +48633,7 @@ /obj/structure/cable/green{ icon_state = "4-8" }, -/obj/structure/sign/department/greencross{ +/obj/structure/sign/department/cross/green{ pixel_y = -32; dir = 1 }, @@ -48909,7 +48909,7 @@ /turf/wall/prepainted, /area/exodus/maintenance/research_port) "bYg" = ( -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_x = -32 }, @@ -50231,7 +50231,7 @@ /obj/effect/floor_decal/corner/pink{ dir = 5 }, -/obj/structure/sign/department/greencross{ +/obj/structure/sign/department/cross/green{ pixel_y = 32 }, /turf/floor/tiled/white, @@ -59794,7 +59794,7 @@ /turf/floor/tiled/techfloor/grid, /area/exodus/engineering/storage) "cwX" = ( -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_y = 32 }, @@ -64128,7 +64128,7 @@ /obj/machinery/light{ dir = 4 }, -/obj/structure/sign/department/star_of_life{ +/obj/structure/sign/department/cross/star_of_life{ name = "Medbay"; pixel_x = 32 }, diff --git a/maps/exodus/exodus-admin.dmm b/maps/exodus/exodus-admin.dmm index cde6caa5150c..493d426ffd7b 100644 --- a/maps/exodus/exodus-admin.dmm +++ b/maps/exodus/exodus-admin.dmm @@ -1147,7 +1147,7 @@ /turf/unsimulated/floor/steel, /area/centcom/holding) "aLJ" = ( -/obj/structure/sign/department/redcross, +/obj/structure/sign/department/cross, /turf/unsimulated/wall, /area/centcom/holding) "aLK" = ( diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index 4308072cd477..a5bb07e061cf 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -3,6 +3,7 @@ #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/augments/_augments.dme" #include "../../mods/content/beekeeping/_beekeeping.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/blob/_blob.dme" @@ -14,11 +15,13 @@ #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/response_team/_response_team.dme" #include "../../mods/content/sealant_gun/_sealant_gun.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" + #include "../../mods/content/exploration/_exploration.dme" #include "../../mods/content/tabloids/_tabloids.dme" #include "../../mods/gamemodes/cult/_cult.dme" diff --git a/maps/exodus/exodus_setup.dm b/maps/exodus/exodus_setup.dm index 9db56fdf9a4d..19bb80bf3a2a 100644 --- a/maps/exodus/exodus_setup.dm +++ b/maps/exodus/exodus_setup.dm @@ -13,8 +13,8 @@ welcome_text += "Time since last port visit:
[rand(60,180)] days

" welcome_text += "Scan results show the following points of interest:
" - for(var/zlevel in global.overmap_sectors) - var/obj/effect/overmap/visitable/O = global.overmap_sectors[zlevel] + for(var/zlevel, sector in global.overmap_sectors) + var/obj/effect/overmap/visitable/O = sector if(O.name == exodus.name) continue if(istype(O, /obj/effect/overmap/visitable/ship/landable)) //Don't show shuttles diff --git a/maps/exodus/exodus_shuttles.dm b/maps/exodus/exodus_shuttles.dm index 9691d2c27ff0..c7abc4523d25 100644 --- a/maps/exodus/exodus_shuttles.dm +++ b/maps/exodus/exodus_shuttles.dm @@ -20,7 +20,7 @@ } \ /area/shuttle/escape_pod_##NUMBER { \ name = "Escape Pod " + #NUMBER; \ - area_flags = AREA_FLAG_RAD_SHIELDED | AREA_FLAG_ION_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT; \ + area_flags = AREA_FLAG_RAD_SHIELDED | AREA_FLAG_ION_SHIELDED | AREA_FLAG_NO_LEGACY_PERSISTENCE; \ } ESCAPE_POD(1) @@ -38,7 +38,7 @@ ESCAPE_POD(4) current_location = "nav_exodus_research_pod_dock" /area/ship/exodus_pod_research name = "Research Pod" - area_flags = AREA_FLAG_RAD_SHIELDED | AREA_FLAG_ION_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT + area_flags = AREA_FLAG_RAD_SHIELDED | AREA_FLAG_ION_SHIELDED | AREA_FLAG_NO_LEGACY_PERSISTENCE /obj/effect/overmap/visitable/ship/landable/pod/research name = "Exodus Research Pod" shuttle = "Research Pod" @@ -57,7 +57,7 @@ ESCAPE_POD(4) current_location = "nav_exodus_mining_pod_dock" /area/ship/exodus_pod_mining name = "Mining Pod" - area_flags = AREA_FLAG_RAD_SHIELDED | AREA_FLAG_ION_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT + area_flags = AREA_FLAG_RAD_SHIELDED | AREA_FLAG_ION_SHIELDED | AREA_FLAG_NO_LEGACY_PERSISTENCE /obj/effect/overmap/visitable/ship/landable/pod/mining name = "Exodus Mining Pod" shuttle = "Mining Pod" @@ -76,7 +76,7 @@ ESCAPE_POD(4) current_location = "nav_exodus_engineering_pod_dock" /area/ship/exodus_pod_engineering name = "Engineering Pod" - area_flags = AREA_FLAG_RAD_SHIELDED | AREA_FLAG_ION_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT + area_flags = AREA_FLAG_RAD_SHIELDED | AREA_FLAG_ION_SHIELDED | AREA_FLAG_NO_LEGACY_PERSISTENCE /obj/effect/overmap/visitable/ship/landable/pod/engineering name = "Exodus Engineering Pod" shuttle = "Engineering Pod" diff --git a/maps/karzerfeste/karzerfeste_testing.dm b/maps/karzerfeste/karzerfeste_testing.dm index 5adbc61c7065..1075bf425aab 100644 --- a/maps/karzerfeste/karzerfeste_testing.dm +++ b/maps/karzerfeste/karzerfeste_testing.dm @@ -1,5 +1,4 @@ -/datum/map/karzerfeste - apc_test_exempt_areas = list( - /area/space = NO_SCRUBBER|NO_VENT|NO_APC, - /area/karzerfeste = NO_SCRUBBER|NO_VENT|NO_APC - ) +/datum/map/karzerfeste/New() + LAZYSET(apc_test_exempt_areas, /area/karzerfeste, NO_SCRUBBER|NO_VENT|NO_APC) + LAZYDISTINCTADD(area_coherency_test_exempted_root_areas, /area/karzerfeste/caves/poi) + ..() diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index fc9f404b88e2..45af5c6f8e6b 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -426,7 +426,7 @@ /turf/floor/plating/airless, /area/space) "ce" = ( -/obj/item/ore, +/obj/item/ore_satchel, /turf/floor/plating/airless, /area/space) "cf" = ( @@ -3645,7 +3645,7 @@ /area/ministation/atmospherics) "pS" = ( /obj/effect/floor_decal/industrial/outline/yellow, -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/floor/tiled, /area/ministation/cargo) "pT" = ( @@ -3835,7 +3835,7 @@ /obj/machinery/light{ dir = 1 }, -/obj/machinery/atmospherics/unary/freezer, +/obj/machinery/atmospherics/unary/temperature/freezer, /turf/floor/tiled/techfloor, /area/ministation/atmospherics) "qL" = ( @@ -10707,7 +10707,7 @@ /area/ministation/engine) "NR" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/sign/directions/supply{ +/obj/structure/sign/directions/cargo/supply{ dir = 1; pixel_x = -32; pixel_y = 32 @@ -10925,7 +10925,7 @@ /obj/machinery/camera/network/mining{ dir = 1 }, -/obj/item/ore, +/obj/item/ore_satchel, /turf/floor/plating/airless, /area/space) "Oy" = ( @@ -11114,7 +11114,7 @@ /turf/floor/plating, /area/space) "OW" = ( -/obj/structure/sign/directions/supply{ +/obj/structure/sign/directions/cargo/supply{ dir = 1; pixel_x = -32; pixel_y = 32 @@ -13140,7 +13140,7 @@ /area/ministation/engine) "Ws" = ( /obj/effect/floor_decal/industrial/outline/yellow, -/obj/vehicle/train/cargo/engine, +/obj/vehicle/train/engine, /turf/floor/tiled, /area/ministation/cargo) "Wt" = ( diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index 1ff78b8cade6..fc3ebff786d9 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -877,7 +877,7 @@ /turf/floor/plating, /area/ministation/maint/l2centraln) "et" = ( -/obj/effect/floor_decal/snow, +/obj/effect/floor_decal/snow_floor, /turf/floor/tiled/freezer/kitchen, /area/ministation/cafe) "ew" = ( @@ -1108,7 +1108,7 @@ dir = 8 }, /obj/effect/decal/cleanable/blood, -/obj/structure/sign/department/redcross{ +/obj/structure/sign/department/cross{ pixel_y = 32 }, /obj/structure/disposalpipe/segment{ @@ -1118,7 +1118,7 @@ /area/ministation/hall/e2) "fD" = ( /obj/structure/meat_hook, -/obj/effect/floor_decal/snow, +/obj/effect/floor_decal/snow_floor, /turf/floor/tiled/freezer/kitchen, /area/ministation/cafe) "fF" = ( @@ -2583,7 +2583,7 @@ /area/ministation/security) "mw" = ( /obj/machinery/light/small, -/obj/effect/floor_decal/snow, +/obj/effect/floor_decal/snow_floor, /obj/structure/meat_hook, /turf/floor/tiled/freezer/kitchen, /area/ministation/cafe) @@ -5166,7 +5166,7 @@ /turf/floor/tiled, /area/ministation/hydro) "wt" = ( -/obj/effect/floor_decal/snow, +/obj/effect/floor_decal/snow_floor, /obj/machinery/gibber, /turf/floor/tiled/freezer/kitchen, /area/ministation/cafe) @@ -5707,7 +5707,7 @@ dir = 5 }, /obj/structure/table/glass, -/obj/item/plants, +/obj/item/plant_satchel, /obj/item/scanner/plant, /obj/item/wirecutters/clippers, /obj/item/book/skill/service/botany, diff --git a/maps/ministation/ministation-2.dmm b/maps/ministation/ministation-2.dmm index c503f8dbd579..36d8182f59f5 100644 --- a/maps/ministation/ministation-2.dmm +++ b/maps/ministation/ministation-2.dmm @@ -3759,7 +3759,7 @@ /turf/floor/plating, /area/ministation/bridge) "tT" = ( -/obj/machinery/atmospherics/unary/freezer{ +/obj/machinery/atmospherics/unary/temperature/freezer{ dir = 8; set_temperature = 263 }, @@ -5996,7 +5996,7 @@ /turf/floor/tiled/white, /area/ministation/science) "JY" = ( -/obj/machinery/atmospherics/unary/freezer, +/obj/machinery/atmospherics/unary/temperature/freezer, /obj/structure/sign/warning/nosmoking_burned{ pixel_y = 32 }, @@ -6117,7 +6117,7 @@ /turf/floor/tiled/white, /area/ministation/science) "LK" = ( -/obj/machinery/atmospherics/unary/freezer{ +/obj/machinery/atmospherics/unary/temperature/freezer{ set_temperature = 263 }, /obj/machinery/light{ diff --git a/maps/ministation/ministation.dm b/maps/ministation/ministation.dm index f4aafdce3206..a44a244232b9 100644 --- a/maps/ministation/ministation.dm +++ b/maps/ministation/ministation.dm @@ -20,6 +20,7 @@ Twice... #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/augments/_augments.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/blob/_blob.dme" #include "../../mods/content/corporate/_corporate.dme" @@ -35,6 +36,7 @@ Twice... #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" + #include "../../mods/content/exploration/_exploration.dme" #include "../../mods/gamemodes/cult/_cult.dme" #include "../../mods/gamemodes/heist/_heist.dme" diff --git a/maps/ministation/outfits/civilian.dm b/maps/ministation/outfits/civilian.dm index 0c4e64faab02..631204576f2e 100644 --- a/maps/ministation/outfits/civilian.dm +++ b/maps/ministation/outfits/civilian.dm @@ -4,7 +4,7 @@ uniform = /obj/item/clothing/jumpsuit/cargotech id_type = /obj/item/card/id/ministation/cargo pda_type = /obj/item/modular_computer/pda/cargo - backpack_contents = list(/obj/item/crowbar = 1, /obj/item/ore = 1) + backpack_contents = list(/obj/item/crowbar = 1, /obj/item/ore_satchel = 1) outfit_flags = OUTFIT_HAS_BACKPACK | OUTFIT_EXTENDED_SURVIVAL | OUTFIT_HAS_VITALS_SENSOR /decl/outfit/job/ministation/cargo/Initialize() diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index 28b2618475fe..d334f4879d6c 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -7,10 +7,10 @@ #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" + #include "../../mods/content/augments/_augments.dme" #include "../../mods/content/beekeeping/_beekeeping.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/biomods/_biomods.dme" - #include "../../mods/content/blacksmithy/_blacksmithy.dme" #include "../../mods/content/blob/_blob.dme" #include "../../mods/content/breath_holding/_breath_holding.dme" #include "../../mods/content/byond_membership/_byond_membership.dm" @@ -21,13 +21,13 @@ #include "../../mods/content/government/_government.dme" #include "../../mods/content/inertia/_inertia.dme" #include "../../mods/content/integrated_electronics/_integrated_electronics.dme" - #include "../../mods/content/item_sharpening/_item_sharpening.dme" #include "../../mods/content/matchmaking/_matchmaking.dme" #include "../../mods/content/modern_earth/_modern_earth.dme" #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/content/plant_dissection/_plant_dissection.dme" #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/response_team/_response_team.dme" #include "../../mods/content/sealant_gun/_sealant_gun.dme" #include "../../mods/content/shackles/_shackles.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" @@ -39,6 +39,7 @@ #include "../../mods/content/blacksmithy/_blacksmithy.dme" #include "../../mods/content/item_sharpening/_item_sharpening.dme" #include "../../mods/pyrelight/_pyrelight.dme" // include after _fantasy.dme so overrides work + #include "../../mods/content/exploration/_exploration.dme" #include "../../mods/gamemodes/cult/_cult.dme" #include "../../mods/gamemodes/heist/_heist.dme" diff --git a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm index 8b1dd94704b9..d149ef82ff8e 100644 --- a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm +++ b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm @@ -613,7 +613,7 @@ /obj/item/tool/axe/hatchet, /obj/item/plantspray/pests, /obj/item/plantspray/weeds, -/obj/item/plants, +/obj/item/plant_satchel, /obj/item/chems/glass/bottle/eznutrient, /obj/item/seeds/wheatseed, /obj/effect/decal/cleanable/dirt/visible, @@ -626,8 +626,8 @@ /obj/structure/closet/crate{ dir = 8 }, -/obj/item/ore, -/obj/item/ore, +/obj/item/ore_satchel, +/obj/item/ore_satchel, /obj/item/mop, /obj/item/flashlight/lantern, /obj/item/flashlight/lantern, diff --git a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm index 8f79725b6aac..92cdc72ce31d 100644 --- a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm +++ b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm @@ -1617,7 +1617,7 @@ /turf/floor/tiled/techfloor, /area/map_template/colony) "dJ" = ( -/obj/machinery/atmospherics/unary/freezer{ +/obj/machinery/atmospherics/unary/temperature/freezer{ icon_state = "freezer" }, /obj/effect/floor_decal/industrial/outline/yellow, @@ -3379,7 +3379,7 @@ /turf/floor/lino, /area/map_template/colony/messhall) "gL" = ( -/obj/machinery/atmospherics/unary/heater{ +/obj/machinery/atmospherics/unary/temperature/heater{ dir = 1 }, /obj/effect/floor_decal/industrial/outline/yellow, @@ -5387,7 +5387,7 @@ /turf/floor/tiled/techfloor, /area/map_template/colony) "kt" = ( -/obj/machinery/atmospherics/unary/freezer{ +/obj/machinery/atmospherics/unary/temperature/freezer{ dir = 8 }, /obj/effect/floor_decal/industrial/outline/yellow, @@ -6964,7 +6964,7 @@ /turf/floor/concrete, /area/template_noop) "nr" = ( -/obj/machinery/atmospherics/unary/heater{ +/obj/machinery/atmospherics/unary/temperature/heater{ dir = 8 }, /obj/effect/floor_decal/industrial/outline/yellow, @@ -7429,8 +7429,8 @@ /obj/item/tool/pickaxe, /obj/item/tool/shovel, /obj/item/tool/shovel, -/obj/item/ore, -/obj/item/ore, +/obj/item/ore_satchel, +/obj/item/ore_satchel, /obj/item/backpack/dufflebag/eng, /obj/item/backpack/dufflebag/eng, /obj/machinery/door/window/northright, diff --git a/maps/shaded_hills/levels/_levels.dm b/maps/shaded_hills/levels/_levels.dm index dc2d59ddcc52..411714f57c56 100644 --- a/maps/shaded_hills/levels/_levels.dm +++ b/maps/shaded_hills/levels/_levels.dm @@ -30,7 +30,7 @@ level_id = "shaded_hills_grassland" level_generators = list( /datum/random_map/automata/cave_system/shaded_hills, - /datum/random_map/noise/ore/poor, + /datum/random_map/noise/ore/poor/shaded_hills, /datum/random_map/noise/forage/shaded_hills/grassland ) connected_levels = list( @@ -163,7 +163,7 @@ subtemplate_area = /area/shaded_hills/caves/deep/poi level_generators = list( /datum/random_map/automata/cave_system/shaded_hills, - /datum/random_map/noise/ore/rich + /datum/random_map/noise/ore/rich/shaded_hills ) base_turf = /turf/floor/rock/basalt diff --git a/maps/shaded_hills/levels/random_map.dm b/maps/shaded_hills/levels/random_map.dm index d51ed84765b3..5c3098b3c2c7 100644 --- a/maps/shaded_hills/levels/random_map.dm +++ b/maps/shaded_hills/levels/random_map.dm @@ -11,6 +11,13 @@ smooth_single_tiles = TRUE target_turf_type = /turf/unsimulated/mask +/datum/random_map/noise/forage/shaded_hills + abstract_type = /datum/random_map/noise/forage/shaded_hills + +/datum/random_map/noise/ore/poor/shaded_hills + +/datum/random_map/noise/ore/rich/shaded_hills + /datum/random_map/noise/shaded_hills/swamp descriptor = "Shaded Hills swamp" @@ -33,10 +40,6 @@ return /turf/floor/grass/wild return /turf/floor/grass -// TODO -/datum/random_map/noise/forage/shaded_hills - abstract_type = /datum/random_map/noise/forage/shaded_hills - /datum/random_map/noise/forage/shaded_hills/grassland/New() forage["grass"] |= list( "yarrow", diff --git a/maps/shaded_hills/shaded_hills_testing.dm b/maps/shaded_hills/shaded_hills_testing.dm index 6a0e4d9d3593..e13f0fffa547 100644 --- a/maps/shaded_hills/shaded_hills_testing.dm +++ b/maps/shaded_hills/shaded_hills_testing.dm @@ -1,5 +1,4 @@ -/datum/map/shaded_hills - apc_test_exempt_areas = list( - /area/space = NO_SCRUBBER|NO_VENT|NO_APC, - /area/shaded_hills = NO_SCRUBBER|NO_VENT|NO_APC - ) +/datum/map/shaded_hills/New() + LAZYDISTINCTADD(area_coherency_test_exempted_root_areas, /area/shaded_hills/outside) + LAZYSET(apc_test_exempt_areas, /area/shaded_hills, (NO_SCRUBBER|NO_VENT|NO_APC)) + ..() diff --git a/maps/tradeship/jobs/command.dm b/maps/tradeship/jobs/command.dm index 3647a632760c..d4dd0bdff303 100644 --- a/maps/tradeship/jobs/command.dm +++ b/maps/tradeship/jobs/command.dm @@ -27,8 +27,8 @@ global.using_map.station_short = ship global.using_map.station_name = "Tradeship [ship]" - for(var/sz in global.overmap_sectors) - var/obj/effect/overmap/visitable/ship/tradeship/B = global.overmap_sectors[sz] + for(var/sz, sec in global.overmap_sectors) + var/obj/effect/overmap/visitable/ship/tradeship/B = sec if(istype(B)) B.SetName(global.using_map.station_name) command_announcement.Announce("Attention all hands on [global.using_map.station_name]! Thank you for your attention.", "Ship Re-Christened") diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm index c799611aca02..34a51a33bfa2 100644 --- a/maps/tradeship/tradeship-1.dmm +++ b/maps/tradeship/tradeship-1.dmm @@ -2135,7 +2135,7 @@ /area/ship/trade/science) "lg" = ( /obj/structure/closet/crate/plastic, -/obj/item/ore, +/obj/item/ore_satchel, /obj/item/tool/pickaxe, /obj/item/stack/flag/yellow, /obj/item/box/glowsticks, diff --git a/maps/tradeship/tradeship-2.dmm b/maps/tradeship/tradeship-2.dmm index 3e2abcf9d90a..f724fda47819 100644 --- a/maps/tradeship/tradeship-2.dmm +++ b/maps/tradeship/tradeship-2.dmm @@ -4520,7 +4520,7 @@ /turf/space, /area/ship/trade/shuttle/outgoing/general) "lZ" = ( -/obj/structure/sign/department/redcross{ +/obj/structure/sign/department/cross{ pixel_x = 32; dir = 8 }, diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index 542a6dcb22f4..28f3b208c5a1 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -16,6 +16,7 @@ #include "../../mods/content/plant_dissection/_plant_dissection.dme" + #include "../../mods/content/augments/_augments.dme" #include "../../mods/content/beekeeping/_beekeeping.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/blob/_blob.dme" @@ -34,6 +35,7 @@ #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" + #include "../../mods/content/exploration/_exploration.dme" #include "../../mods/gamemodes/cult/_cult.dme" #include "../../mods/gamemodes/heist/_heist.dme" diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index 04ce37b278c6..f552714156e7 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -207,6 +207,9 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also toggleable var/default_ui_style + /// Is maint currently all-access? + var/maint_all_access = FALSE + /datum/map/New() ..() default_ui_style ||= DEFAULT_UI_STYLE @@ -475,11 +478,11 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also toggleable return /datum/map/proc/make_maint_all_access(var/radstorm = 0) - maint_all_access = 1 + maint_all_access = TRUE priority_announcement.Announce("The maintenance access requirement has been revoked on all maintenance airlocks.", "Attention!") /datum/map/proc/revoke_maint_all_access(var/radstorm = 0) - maint_all_access = 0 + maint_all_access = FALSE priority_announcement.Announce("The maintenance access requirement has been readded on all maintenance airlocks.", "Attention!") /datum/map/proc/show_titlescreen(client/C) @@ -549,7 +552,7 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also toggleable var/obj/item/passport/pass = new passport_type(get_turf(H)) if(istype(pass)) pass.set_info(H) - if(!H.equip_to_slot(pass, slot_in_backpack_str)) + if(!H.equip_to_slot(pass, slot_in_wallet_str) && !H.equip_to_slot(pass, slot_in_backpack_str)) H.put_in_hands(pass) /datum/map/proc/populate_overmap_events() diff --git a/maps/~mapsystem/maps_antagonism.dm b/maps/~mapsystem/maps_antagonism.dm index db59a1035aee..320d177359c3 100644 --- a/maps/~mapsystem/maps_antagonism.dm +++ b/maps/~mapsystem/maps_antagonism.dm @@ -1,6 +1,6 @@ /datum/map var/list/potential_theft_targets = list( - "the captain's antique laser gun" = /obj/item/gun/energy/captain, + "the captain's antique laser gun" = /obj/item/gun/energy/retro/captain, "an RCD" = /obj/item/rcd, "a jetpack" = /obj/item/tank/jetpack, "a captain's jumpsuit" = /obj/item/clothing/jumpsuit/captain, diff --git a/maps/~mapsystem/maps_unit_testing.dm b/maps/~mapsystem/maps_unit_testing.dm index 025460d97241..e25b6cbebb86 100644 --- a/maps/~mapsystem/maps_unit_testing.dm +++ b/maps/~mapsystem/maps_unit_testing.dm @@ -1,7 +1,8 @@ /datum/map - var/const/NO_APC = 1 - var/const/NO_VENT = 2 - var/const/NO_SCRUBBER = 4 + var/const/NO_APC = BITFLAG(0) + var/const/NO_VENT = BITFLAG(1) + var/const/NO_SCRUBBER = BITFLAG(2) + var/const/SKIP_ALL_TESTS = BITFLAG(3) /// Defines the expected result of the atmospherics shuttle unit test for atmosphere. var/shuttle_atmos_expectation = UT_NORMAL diff --git a/mods/content/augments/_augments.dm b/mods/content/augments/_augments.dm new file mode 100644 index 000000000000..702d94917fae --- /dev/null +++ b/mods/content/augments/_augments.dm @@ -0,0 +1,13 @@ +//Augmentation organ tag defines +#define BP_AUGMENT_R_ARM "right arm augment" +#define BP_AUGMENT_L_ARM "left arm augment" +#define BP_AUGMENT_R_HAND "right hand augment" +#define BP_AUGMENT_L_HAND "left hand augment" +#define BP_AUGMENT_R_LEG "right leg augment" +#define BP_AUGMENT_L_LEG "left leg augment" +#define BP_AUGMENT_CHEST_ARMOUR "chest armor augment" +#define BP_AUGMENT_CHEST_ACTIVE "active chest augment" +#define BP_AUGMENT_HEAD "head augment" + +/decl/modpack/augments + name = "Augmentation Content" diff --git a/mods/content/augments/_augments.dme b/mods/content/augments/_augments.dme new file mode 100644 index 000000000000..ec0fba60a729 --- /dev/null +++ b/mods/content/augments/_augments.dme @@ -0,0 +1,24 @@ +#ifndef CONTENT_PACK_AUGMENTS +#define CONTENT_PACK_AUGMENTS +// BEGIN_INCLUDE +#include "_augments.dm" +#include "active.dm" +#include "augment_loadout.dm" +#include "augment_organ.dm" +#include "bodytype_augment.dm" +#include "designs_augments.dm" +#include "overrides.dm" +#include "simple.dm" +#include "active\armblades.dm" +#include "active\cyberbrain.dm" +#include "active\polytool.dm" +#include "active\tool\engineering.dm" +#include "active\tool\surgical.dm" +#include "passive\armor.dm" +#include "passive\boost.dm" +#include "passive\nanoaura.dm" +#include "passive\boost\muscle.dm" +#include "passive\boost\reflex.dm" +#include "passive\boost\shooting.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/code/modules/augment/active.dm b/mods/content/augments/active.dm similarity index 87% rename from code/modules/augment/active.dm rename to mods/content/augments/active.dm index e338d1e763c8..773215a8fa69 100644 --- a/code/modules/augment/active.dm +++ b/mods/content/augments/active.dm @@ -22,3 +22,6 @@ if(. && istype(action)) action.button_icon_state = icon_state action.button?.update_icon() + +/datum/action/item_action/organ/augment + button_icon = 'mods/content/augments/icons/augment.dmi' \ No newline at end of file diff --git a/code/modules/augment/active/armblades.dm b/mods/content/augments/active/armblades.dm similarity index 97% rename from code/modules/augment/active/armblades.dm rename to mods/content/augments/active/armblades.dm index 8e40dc7c43e3..38d043f9a32e 100644 --- a/code/modules/augment/active/armblades.dm +++ b/mods/content/augments/active/armblades.dm @@ -2,7 +2,7 @@ icon_state = "armblade" item_state = null name = "armblade" - icon = 'icons/obj/augment.dmi' + icon = 'mods/content/augments/icons/augment.dmi' desc = "A handy utility blade for the discerning augmentee. Warranty void if used for cutting." base_parry_chance = 30 sharp = TRUE diff --git a/code/modules/augment/active/cyberbrain.dm b/mods/content/augments/active/cyberbrain.dm similarity index 100% rename from code/modules/augment/active/cyberbrain.dm rename to mods/content/augments/active/cyberbrain.dm diff --git a/code/modules/augment/active/polytool.dm b/mods/content/augments/active/polytool.dm similarity index 100% rename from code/modules/augment/active/polytool.dm rename to mods/content/augments/active/polytool.dm diff --git a/code/modules/augment/active/tool/engineering.dm b/mods/content/augments/active/tool/engineering.dm similarity index 84% rename from code/modules/augment/active/tool/engineering.dm rename to mods/content/augments/active/tool/engineering.dm index 20fec8cfe1a6..2aff3eedd2a0 100644 --- a/code/modules/augment/active/tool/engineering.dm +++ b/mods/content/augments/active/tool/engineering.dm @@ -19,13 +19,13 @@ /obj/item/weldingtool/finger name = "digital welder" desc = "A precise, high quality welding tool." - icon = 'icons/obj/items/tool/welders/welder_finger.dmi' + icon = 'mods/content/augments/icons/welder_finger.dmi' /obj/item/wirecutters/finger name = "digital splicer" desc = "A small embedded cutter in your finger." icon_state = "wirecutter_finger" - icon = 'icons/obj/augment_tools.dmi' + icon = 'mods/content/augments/icons/augment_tools.dmi' /obj/item/wirecutters/finger/on_update_icon() SHOULD_CALL_PARENT(FALSE) @@ -35,7 +35,7 @@ name = "digital screwdriver" desc = "A nifty power tool at your literal fingertips." icon_state = "screwdriver_finger" - icon = 'icons/obj/augment_tools.dmi' + icon = 'mods/content/augments/icons/augment_tools.dmi' /obj/item/screwdriver/finger/on_update_icon() SHOULD_CALL_PARENT(FALSE) @@ -45,7 +45,7 @@ name = "digital prybar" desc = "A somewhat awkward to use prybar. It doubles as a bottle opener." icon_state = "prybar_finger" - icon = 'icons/obj/augment_tools.dmi' + icon = 'mods/content/augments/icons/augment_tools.dmi' /obj/item/crowbar/finger/on_update_icon() SHOULD_CALL_PARENT(FALSE) @@ -55,7 +55,7 @@ name = "digital wrench" desc = "A rotating wrench. Don't get your hair caught in it." icon_state = "wrench_finger" - icon = 'icons/obj/augment_tools.dmi' + icon = 'mods/content/augments/icons/augment_tools.dmi' /obj/item/wrench/finger/on_update_icon() SHOULD_CALL_PARENT(FALSE) @@ -65,4 +65,4 @@ name = "digital multitool" desc = "A multitool inside of a multitool. Doubletool?" icon_state = "multitool_finger" - icon = 'icons/obj/augment_tools.dmi' + icon = 'mods/content/augments/icons/augment_tools.dmi' diff --git a/code/modules/augment/active/tool/surgical.dm b/mods/content/augments/active/tool/surgical.dm similarity index 100% rename from code/modules/augment/active/tool/surgical.dm rename to mods/content/augments/active/tool/surgical.dm diff --git a/code/modules/client/preference_setup/loadout/lists/augmentations.dm b/mods/content/augments/augment_loadout.dm similarity index 79% rename from code/modules/client/preference_setup/loadout/lists/augmentations.dm rename to mods/content/augments/augment_loadout.dm index e2f387cd6ec2..3fd9d8efa984 100644 --- a/code/modules/client/preference_setup/loadout/lists/augmentations.dm +++ b/mods/content/augments/augment_loadout.dm @@ -27,9 +27,14 @@ if(!istype(organ_to_implant_into)) return ..() - if(augment_flags == AUGMENTATION_MECHANIC && !BP_IS_PROSTHETIC(organ_to_implant_into)) - to_chat(user, SPAN_DANGER("Your [organ_to_implant_into.name] is not prosthetic, and therefore \the [src] can not be installed!")) - return ..() + if(BP_IS_PROSTHETIC(organ_to_implant_into)) + if(!(augment_flags & AUGMENTATION_MECHANIC)) + to_chat(user, SPAN_DANGER("Your [organ_to_implant_into.name] is not organic, and therefore \the [src] can not be installed!")) + return ..() + else + if(!(augment_flags & AUGMENTATION_ORGANIC)) + to_chat(user, SPAN_DANGER("Your [organ_to_implant_into.name] is not prosthetic, and therefore \the [src] can not be installed!")) + return ..() user.add_organ(src, organ_to_implant_into) to_chat(user, SPAN_NOTICE("Your [organ_to_implant_into.name] has been replaced with \the [src].")) diff --git a/code/modules/augment/augment.dm b/mods/content/augments/augment_organ.dm similarity index 85% rename from code/modules/augment/augment.dm rename to mods/content/augments/augment_organ.dm index 63105dc6e02b..8f097a8874bd 100644 --- a/code/modules/augment/augment.dm +++ b/mods/content/augments/augment_organ.dm @@ -1,7 +1,7 @@ /obj/item/organ/internal/augment name = "embedded augment" desc = "An embedded augment." - icon = 'icons/obj/augment.dmi' + icon = 'mods/content/augments/icons/augment.dmi' w_class = ITEM_SIZE_TINY // Need to be tiny to fit inside limbs. //By default these fit on both flesh and robotic organs and are robotic organ_properties = ORGAN_PROP_PROSTHETIC @@ -12,6 +12,8 @@ var/descriptor = "" var/known = TRUE + var/const/AUGMENTATION_MECHANIC = BITFLAG(0) + var/const/AUGMENTATION_ORGANIC = BITFLAG(1) var/augment_flags = AUGMENTATION_MECHANIC | AUGMENTATION_ORGANIC var/list/allowed_organs = list(BP_AUGMENT_R_ARM, BP_AUGMENT_L_ARM) @@ -36,6 +38,14 @@ parent_organ = affected.organ_tag update_parent_organ() +/obj/item/organ/internal/augment/get_attachment_failure_reason(obj/item/organ/external/affected, robotic = FALSE) + if(robotic) + if(!(augment_flags & AUGMENTATION_ORGANIC)) + return SPAN_WARNING("\The [src] cannot function within a non-robotic limb.") + else if(!(augment_flags & AUGMENTATION_MECHANIC)) + return SPAN_WARNING("\The [src] cannot function within a robotic limb.") + return ..() + /obj/item/organ/internal/augment/proc/update_parent_organ() //This tries to match a parent organ to an augment slot //This is intended to match the possible positions to a parent organ diff --git a/mods/content/augments/bodytype_augment.dm b/mods/content/augments/bodytype_augment.dm new file mode 100644 index 000000000000..4fd09ed6a0ed --- /dev/null +++ b/mods/content/augments/bodytype_augment.dm @@ -0,0 +1,4 @@ +// Dummy/stub prosthetic type for augment implants. +/decl/bodytype/prosthetic/augment + name = "Augment" + uid = "bodytype_prosthetic_augment" diff --git a/code/modules/fabrication/designs/robotics/designs_augments.dm b/mods/content/augments/designs_augments.dm similarity index 100% rename from code/modules/fabrication/designs/robotics/designs_augments.dm rename to mods/content/augments/designs_augments.dm diff --git a/icons/obj/augment.dmi b/mods/content/augments/icons/augment.dmi similarity index 100% rename from icons/obj/augment.dmi rename to mods/content/augments/icons/augment.dmi diff --git a/icons/obj/augment_tools.dmi b/mods/content/augments/icons/augment_tools.dmi similarity index 100% rename from icons/obj/augment_tools.dmi rename to mods/content/augments/icons/augment_tools.dmi diff --git a/icons/obj/items/tool/welders/welder_finger.dmi b/mods/content/augments/icons/welder_finger.dmi similarity index 100% rename from icons/obj/items/tool/welders/welder_finger.dmi rename to mods/content/augments/icons/welder_finger.dmi diff --git a/mods/content/augments/overrides.dm b/mods/content/augments/overrides.dm new file mode 100644 index 000000000000..296d4c337067 --- /dev/null +++ b/mods/content/augments/overrides.dm @@ -0,0 +1,32 @@ + +/obj/structure/mineral_bath/should_dissolve_implant(obj/implanted_object) + if(istype(implanted_object, /obj/item/organ/internal/augment)) + return FALSE + return ..() + +// Cause arm and hand augments to trigger fault ailments. +/datum/ailment/fault/locking_thumbs/New(obj/item/organ/_organ) + var/static/did_injection = FALSE + if(!did_injection) + did_injection = TRUE + applies_to_organ |= list( + BP_AUGMENT_R_ARM, + BP_AUGMENT_L_ARM, + BP_AUGMENT_R_HAND, + BP_AUGMENT_L_HAND + ) + . = ..() + +/datum/ailment/fault/locking_thumbs/resolve_tag_to_slot(organ_tag) + switch(organ_tag) + if(BP_AUGMENT_L_ARM, BP_AUGMENT_L_HAND) + return BP_L_HAND + if(BP_AUGMENT_R_ARM, BP_AUGMENT_R_HAND) + return BP_R_HAND + return ..() + +// Add augments to scan results +/obj/item/organ/external/get_scan_results() + for(var/obj/item/organ/internal/augment/aug in internal_organs) + if(istype(aug) && aug.known) + . += "[capitalize(aug.name)] implanted" \ No newline at end of file diff --git a/mods/content/augments/passive/armor.dm b/mods/content/augments/passive/armor.dm new file mode 100644 index 000000000000..2dff1139cba4 --- /dev/null +++ b/mods/content/augments/passive/armor.dm @@ -0,0 +1,26 @@ +/obj/item/organ/internal/augment/armor + name = "subdermal armor" + allowed_organs = list(BP_AUGMENT_CHEST_ARMOUR) + icon_state = "armor-chest" + desc = "A flexible composite mesh designed to prevent tearing and puncturing of underlying tissue." + material = /decl/material/solid/metal/steel + origin_tech = @'{"materials":4,"engineering":2,"biotech":3}' + var/brute_mult = 0.8 + var/burn_mult = 1 + +/obj/item/organ/internal/augment/armor/reset_matter() + matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) + +// override to add armor augment damage mods +/obj/item/organ/external/get_brute_mod(var/damage_flags) + . = ..() + var/obj/item/organ/internal/augment/armor/armor_augment = owner?.get_organ(BP_AUGMENT_CHEST_ARMOUR, /obj/item/organ/internal/augment/armor) + if(armor_augment) + . *= armor_augment.brute_mult + +// override to add armor augment damage mods +/obj/item/organ/external/get_burn_mod(var/damage_flags) + . = ..() + var/obj/item/organ/internal/augment/armor/armor_augment = owner?.get_organ(BP_AUGMENT_CHEST_ARMOUR, /obj/item/organ/internal/augment/armor) + if(armor_augment) + . *= armor_augment.burn_mult \ No newline at end of file diff --git a/code/modules/augment/passive/boost.dm b/mods/content/augments/passive/boost.dm similarity index 100% rename from code/modules/augment/passive/boost.dm rename to mods/content/augments/passive/boost.dm diff --git a/code/modules/augment/passive/boost/muscle.dm b/mods/content/augments/passive/boost/muscle.dm similarity index 100% rename from code/modules/augment/passive/boost/muscle.dm rename to mods/content/augments/passive/boost/muscle.dm diff --git a/code/modules/augment/passive/boost/reflex.dm b/mods/content/augments/passive/boost/reflex.dm similarity index 100% rename from code/modules/augment/passive/boost/reflex.dm rename to mods/content/augments/passive/boost/reflex.dm diff --git a/code/modules/augment/passive/boost/shooting.dm b/mods/content/augments/passive/boost/shooting.dm similarity index 100% rename from code/modules/augment/passive/boost/shooting.dm rename to mods/content/augments/passive/boost/shooting.dm diff --git a/code/modules/augment/passive/nanoaura.dm b/mods/content/augments/passive/nanoaura.dm similarity index 58% rename from code/modules/augment/passive/nanoaura.dm rename to mods/content/augments/passive/nanoaura.dm index 751ed5541abc..e22f8331cd63 100644 --- a/code/modules/augment/passive/nanoaura.dm +++ b/mods/content/augments/passive/nanoaura.dm @@ -1,3 +1,4 @@ +//This handy augment protects you to a degree, keeping it online after critical damage however is bad /obj/item/organ/internal/augment/active/nanounit name = "nanite MCU" allowed_organs = list(BP_AUGMENT_CHEST_ACTIVE) @@ -55,3 +56,38 @@ if(owner && modifier_archetype) owner.remove_mob_modifier(modifier_archetype, source = src) . = ..() + +// The modifier applied by the above implant. +/decl/mob_modifier/nanoswarm + name = "Defensive Nanoswarm" + desc = "You are surrounded by nanomachines that harden in response to projectiles." + hud_icon_state = "nanomachines" + on_add_message_1p = SPAN_NOTICE("Your skin tingles as the nanites spread over your body.") + on_end_message_1p = SPAN_WARNING("The nanites dissolve!") + +/decl/mob_modifier/nanoswarm/on_modifier_datum_added(mob/living/_owner, decl/mob_modifier/modifier) + . = ..() + playsound(_owner.loc,'sound/weapons/flash.ogg',35,1) + +/decl/mob_modifier/nanoswarm/check_modifiers_block_attack(mob/living/_owner, list/modifiers, attack_type, atom/movable/attacker, additional_data) + if(attack_type != MM_ATTACK_TYPE_PROJECTILE) + return ..() + + var/obj/item/organ/internal/augment/active/nanounit/unit + for(var/datum/mob_modifier/modifier in modifiers) + var/obj/item/organ/internal/augment/active/nanounit/implant = modifier.source?.resolve() + if(istype(implant) && implant.active && implant.charges >= 0) // active with 0 charges means it's about to critically fail. + unit = implant + break + + if(!istype(unit)) + return ..() + + _owner.visible_message(SPAN_WARNING("The nanomachines harden as a response to physical trauma!")) + playsound(_owner, 'sound/effects/basscannon.ogg',35,1) + unit.charges-- + if(unit.charges <= 0) + to_chat(_owner, SPAN_DANGER("Warning: Critical damage threshold passed. Shut down unit to avoid further damage.")) + else + unit.catastrophic_failure() + return MM_ATTACK_RESULT_BLOCKED|MM_ATTACK_RESULT_DEFLECTED diff --git a/code/modules/augment/simple.dm b/mods/content/augments/simple.dm similarity index 100% rename from code/modules/augment/simple.dm rename to mods/content/augments/simple.dm diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria.dm b/mods/content/corporate/away_sites/lar_maria/lar_maria.dm index 269d8ed89b83..591e68506de3 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria.dm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria.dm @@ -72,23 +72,23 @@ /obj/abstract/landmark/corpse/lar_maria/test_subject name = "dead test subject" - corpse_outfits = list(/decl/outfit/corpse/test_subject) + corpse_outfits = list(/decl/outfit/corpse_test_subject) spawn_flags = CORPSE_SPAWNER_NO_RANDOMIZATION//no name, no hairs etc. -/decl/outfit/corpse/test_subject +/decl/outfit/corpse_test_subject name = "dead ZHP test subject" uniform = /obj/item/clothing/jumpsuit/orange shoes = /obj/item/clothing/shoes/color/orange /obj/abstract/landmark/corpse/lar_maria/zhp_guard name = "dead guard" - corpse_outfits = list(/decl/outfit/corpse/zhp_guard) + corpse_outfits = list(/decl/outfit/corpse_zhp_guard) skin_tones_per_species = list(/decl/species/human::uid = list(-15)) /obj/abstract/landmark/corpse/lar_maria/zhp_guard/dark skin_tones_per_species = list(/decl/species/human::uid = list(-115)) -/decl/outfit/corpse/zhp_guard +/decl/outfit/corpse_zhp_guard name = "Dead ZHP guard" uniform = /obj/item/clothing/jumpsuit/virologist suit = /obj/item/clothing/suit/armor/pcarrier/light @@ -141,9 +141,9 @@ /obj/abstract/landmark/corpse/lar_maria/virologist name = "dead virologist" - corpse_outfits = list(/decl/outfit/corpse/zhp_virologist) + corpse_outfits = list(/decl/outfit/corpse_zhp_virologist) -/decl/outfit/corpse/zhp_virologist +/decl/outfit/corpse_zhp_virologist name = "Dead male ZHP virologist" uniform = /obj/item/clothing/jumpsuit/virologist suit = /obj/item/clothing/suit/toggle/labcoat @@ -160,12 +160,12 @@ /obj/abstract/landmark/corpse/lar_maria/virologist_female name = "dead virologist" - corpse_outfits = list(/decl/outfit/corpse/zhp_virologist_female) + corpse_outfits = list(/decl/outfit/corpse_zhp_virologist_female) hair_styles_per_species = list(/decl/species/human::uid = list(/decl/sprite_accessory/hair/flair)) hair_colors_per_species = list(/decl/species/human::uid = list("#ae7b48")) genders_per_species = list(/decl/species/human::uid = list(FEMALE)) -/decl/outfit/corpse/zhp_virologist_female +/decl/outfit/corpse_zhp_virologist_female name = "Dead female ZHP virologist" uniform = /obj/item/clothing/jumpsuit/virologist suit = /obj/item/clothing/suit/toggle/labcoat diff --git a/mods/content/corporate/datum/antagonists/deathsquad.dm b/mods/content/corporate/datum/antagonists/deathsquad.dm index 6b393f8984a3..28311da174e8 100644 --- a/mods/content/corporate/datum/antagonists/deathsquad.dm +++ b/mods/content/corporate/datum/antagonists/deathsquad.dm @@ -28,7 +28,7 @@ /decl/outfit/commando name = "Special Role - Deathsquad Commando" - l_ear = /obj/item/radio/headset/ert + l_ear = /obj/item/radio/headset/specops uniform = /obj/item/clothing/jumpsuit/green l_pocket = /obj/item/plastique shoes = /obj/item/clothing/shoes/jackboots/swat diff --git a/mods/content/corporate/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm b/mods/content/corporate/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm index b89a49fad8d1..47c3fe5848c4 100644 --- a/mods/content/corporate/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm +++ b/mods/content/corporate/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm @@ -266,7 +266,7 @@ /area/map_template/oldpod) "aJ" = ( /obj/abstract/landmark/corpse/pirate, -/obj/item/gun/energy/captain, +/obj/item/gun/energy/retro/captain, /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, /turf/floor/tiled/monotile, diff --git a/mods/content/dungeon_loot/subtypes/exosuit.dm b/mods/content/dungeon_loot/subtypes/exosuit.dm index 29ab815a1196..e8a237e802c6 100644 --- a/mods/content/dungeon_loot/subtypes/exosuit.dm +++ b/mods/content/dungeon_loot/subtypes/exosuit.dm @@ -90,32 +90,6 @@ ) return rare_loot -/obj/structure/loot_pile/exosuit/explorer - name = "exploration exosuit wreckage" - desc = "The ruins of some unfortunate exploration exosuit. Perhaps something is salvageable." - -/obj/structure/loot_pile/exosuit/explorer/get_common_loot() - var/static/list/common_loot = list( - /obj/item/mech_component/manipulators/powerloader/exploration, - /obj/item/mech_component/chassis/pod/exploration, - /obj/item/mech_equipment/light - ) - return common_loot - -/obj/structure/loot_pile/exosuit/explorer/get_uncommon_loot() - var/static/list/uncommon_loot = list( - /obj/item/mech_component/propulsion/tracks/exploration, - /obj/item/mech_equipment/clamp - ) - return uncommon_loot - -/obj/structure/loot_pile/exosuit/explorer/get_rare_loot() - var/static/list/rare_loot = list( - /obj/item/mech_component/sensors/light/painted, - /obj/item/mech_equipment/mounted_system/taser/plasma - ) - return rare_loot - /obj/structure/loot_pile/exosuit/heavy name = "heavy exosuit wreckage" desc = "The ruins of some unfortunate heavy exosuit. Perhaps something is salvageable." diff --git a/mods/content/exploration/_exploration.dm b/mods/content/exploration/_exploration.dm new file mode 100644 index 000000000000..f96b87fbdd07 --- /dev/null +++ b/mods/content/exploration/_exploration.dm @@ -0,0 +1,2 @@ +/decl/modpack/exploration + name = "Exploration Content" diff --git a/mods/content/exploration/_exploration.dme b/mods/content/exploration/_exploration.dme new file mode 100644 index 000000000000..5c69cbfa8f0f --- /dev/null +++ b/mods/content/exploration/_exploration.dme @@ -0,0 +1,22 @@ +#ifndef CONTENT_PACK_EXPLORATION +#define CONTENT_PACK_EXPLORATION +// BEGIN_INCLUDE +#include "_exploration.dm" +#include "access.dm" +#include "projectiles.dm" +#include "screen_cataloguer.dm" +#include "specimen_codex.dm" +#include "specimen_mob.dm" +#include "equipment\cataloguer.dm" +#include "equipment\clothing.dm" +#include "equipment\device.dm" +#include "equipment\guns.dm" +#include "equipment\loadout.dm" +#include "equipment\melee.dm" +#include "equipment\radios.dm" +#include "equipment\specimen_tag.dm" +#include "equipment\specimen_tagger.dm" +#include "structures\closets.dm" +#include "structures\stasis_cage.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/mods/content/exploration/access.dm b/mods/content/exploration/access.dm new file mode 100644 index 000000000000..48e7c0abd2dd --- /dev/null +++ b/mods/content/exploration/access.dm @@ -0,0 +1,11 @@ +var/global/const/access_xenofauna = "ACCESS_XENOFAUNA" +/datum/access/xenofauna + id = access_xenofauna + desc = "Xenfauna Lab" + region = ACCESS_REGION_RESEARCH + +var/global/const/access_explorer = "ACCESS_EXPLORER" +/datum/access/explorer + id = access_explorer + desc = "Explorer" + region = ACCESS_REGION_GENERAL diff --git a/code/modules/codex/codex_cataloguer.dm b/mods/content/exploration/equipment/cataloguer.dm similarity index 98% rename from code/modules/codex/codex_cataloguer.dm rename to mods/content/exploration/equipment/cataloguer.dm index ecca7be613bf..03487b39c1ad 100644 --- a/code/modules/codex/codex_cataloguer.dm +++ b/mods/content/exploration/equipment/cataloguer.dm @@ -2,7 +2,7 @@ name = "cataloguer" desc = "A hand-held device used for compiling information about an object by scanning it, and then uploading it to the local codex. Alt-click to highlight scannable objects around you." color = COLOR_GUNMETAL - icon = 'icons/obj/items/device/cataloguer.dmi' + icon = 'mods/content/exploration/icons/cataloguer.dmi' icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_NORMAL origin_tech = @'{"materials":2, "programming":3,"magnets":3}' @@ -208,3 +208,6 @@ if(scan_result) loaded_disk.data += scan_result.worth_points to_chat(user, SPAN_NOTICE("You complete the scan of \the [target], earning [scan_result.worth_points] good explorer point\s.")) + +/datum/fabricator_recipe/device_component/cataloguer + path = /obj/item/cataloguer diff --git a/mods/content/exploration/equipment/clothing.dm b/mods/content/exploration/equipment/clothing.dm new file mode 100644 index 000000000000..fa986fb0f32d --- /dev/null +++ b/mods/content/exploration/equipment/clothing.dm @@ -0,0 +1,131 @@ +/obj/item/clothing/suit/explorer + name = "explorer suit" + desc = "An armoured suit for exploring harsh environments." + icon = 'mods/content/exploration/icons/suit_explo.dmi' + hood = /obj/item/clothing/head/hood/explorer + item_flags = ITEM_FLAG_THICKMATERIAL + body_parts_covered = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_LEGS|SLOT_ARMS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + cold_protection = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_LEGS|SLOT_ARMS + siemens_coefficient = 0.9 + // Inferior to sec vests in bullet/laser but better for environmental protection. + armor = list( + (ARMOR_MELEE) = ARMOR_MELEE_RESISTANT, + (ARMOR_BULLET) = ARMOR_BALLISTIC_SMALL, + (ARMOR_LASER) = ARMOR_LASER_SMALL, + (ARMOR_ENERGY) = ARMOR_ENERGY_SMALL, + (ARMOR_BOMB) = ARMOR_BOMB_PADDED, + (ARMOR_BIO) = ARMOR_BIO_STRONG, + (ARMOR_RAD) = ARMOR_RAD_RESISTANT + ) + allowed = list( + /obj/item/flashlight, + /obj/item/gun, + /obj/item/ammo_magazine, + /obj/item/knife, + /obj/item/bladed, + /obj/item/tank, + /obj/item/radio, + /obj/item/tool, + /obj/item/cataloguer, + /obj/item/specimen_tagger + ) + +/obj/item/clothing/suit/explorer/get_assumed_clothing_state_modifiers() + var/static/list/expected_state_modifiers = list( + GET_DECL(/decl/clothing_state_modifier/hood) + ) + return expected_state_modifiers + +/obj/item/clothing/head/hood/explorer + name = "explorer hood" + desc = "An armoured hood for exploring harsh environments." + icon = 'mods/content/exploration/icons/hood_explo.dmi' + item_flags = ITEM_FLAG_THICKMATERIAL + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.9 + armor = list( + (ARMOR_MELEE) = ARMOR_MELEE_RESISTANT, + (ARMOR_BULLET) = ARMOR_BALLISTIC_SMALL, + (ARMOR_LASER) = ARMOR_LASER_SMALL, + (ARMOR_ENERGY) = ARMOR_ENERGY_SMALL, + (ARMOR_BOMB) = ARMOR_BOMB_PADDED, + (ARMOR_BIO) = ARMOR_BIO_STRONG, + (ARMOR_RAD) = ARMOR_RAD_RESISTANT + ) + +/obj/item/clothing/suit/explorer/xenofauna + name = "xenofauna field suit" + desc = "A lightly armoured suit for surveying harsh environments." + icon = 'mods/content/exploration/icons/suit_xeno.dmi' + hood = /obj/item/clothing/head/hood/explorer/xenofauna + siemens_coefficient = 0.5 + // Better bio/rad protection than explo, but less armour. + armor = list( + (ARMOR_MELEE) = ARMOR_MELEE_KNIVES, + (ARMOR_BULLET) = ARMOR_BALLISTIC_MINOR, + (ARMOR_LASER) = ARMOR_LASER_MINOR, + (ARMOR_ENERGY) = ARMOR_ENERGY_MINOR, + (ARMOR_BOMB) = ARMOR_BOMB_PADDED, + (ARMOR_BIO) = ARMOR_BIO_SHIELDED, + (ARMOR_RAD) = ARMOR_RAD_LARGE + ) + +/obj/item/clothing/head/hood/explorer/xenofauna + name = "xenofauna field hood" + desc = "A lightly armoured hood for surveying harsh environments." + siemens_coefficient = 0.5 + armor = list( + (ARMOR_MELEE) = ARMOR_MELEE_KNIVES, + (ARMOR_BULLET) = ARMOR_BALLISTIC_MINOR, + (ARMOR_LASER) = ARMOR_LASER_MINOR, + (ARMOR_ENERGY) = ARMOR_ENERGY_MINOR, + (ARMOR_BOMB) = ARMOR_BOMB_PADDED, + (ARMOR_BIO) = ARMOR_BIO_SHIELDED, + (ARMOR_RAD) = ARMOR_RAD_LARGE + ) + icon = 'mods/content/exploration/icons/hood_xeno.dmi' + +/obj/item/clothing/shoes/winterboots/explorer + name = "explorer winter boots" + desc = "Steel-toed winter boots for mining or exploration in hazardous environments. Very good at keeping toes warm and uncrushed." + icon = 'mods/content/exploration/icons/boots_explo.dmi' + armor = list( + (ARMOR_MELEE) = ARMOR_MELEE_RESISTANT, + (ARMOR_BULLET) = ARMOR_BALLISTIC_MINOR, + (ARMOR_LASER) = ARMOR_LASER_MINOR, + (ARMOR_ENERGY) = ARMOR_ENERGY_MINOR, + (ARMOR_BOMB) = ARMOR_BOMB_PADDED + ) + +/obj/item/clothing/jumpsuit/explorer + name = "explorer's jumpsuit" + desc = "A grey and cyan uniform for working in the field." + icon = 'mods/content/exploration/icons/uniform_explo.dmi' + +/obj/item/clothing/jumpsuit/xenofauna + name = "xenofauna technician's jumpsuit" + desc = "A grey and purple uniform for working in the field." + icon = 'mods/content/exploration/icons/uniform_xeno.dmi' + +/obj/item/clothing/mask/gas/explorer + icon = 'mods/content/exploration/icons/mask_explo.dmi' + name = "explorer gas mask" + desc = "A military-grade gas mask that can be connected to an air supply." + armor = list( + (ARMOR_MELEE) = ARMOR_MELEE_SMALL, + (ARMOR_BULLET) = ARMOR_BALLISTIC_MINOR, + (ARMOR_LASER) = ARMOR_LASER_MINOR, + (ARMOR_ENERGY) = ARMOR_ENERGY_MINOR, + (ARMOR_BIO) = ARMOR_BIO_RESISTANT, + ) + siemens_coefficient = 0.9 + +/obj/item/clothing/permit/gun/planetside + name = "planetside weapon permit" + desc = "A card indicating that the owner is allowed to carry a weapon while on the surface." + detail_color = COLOR_PALE_PINK + +/obj/item/clothing/permit/gun/planetside/exploration + name = "explorer weapon permit" + desc = "A card indicating that the owner is allowed to carry weaponry during active exploration missions." diff --git a/mods/content/exploration/equipment/device.dm b/mods/content/exploration/equipment/device.dm new file mode 100644 index 000000000000..75085d7bf7f3 --- /dev/null +++ b/mods/content/exploration/equipment/device.dm @@ -0,0 +1,16 @@ +/obj/item/gps/explorer + gps_tag = "EXP0" + color = "#4a4a4a" + decals = list( + "stripe-outside" = "#500677", + "stripe-inside" = "#68099e" + ) + +/obj/item/gps/xenofauna + color = "#b1b1b1" + decals = list( + "stripe-outside" = "#500677", + "stripe-inside" = "#68099e" + ) + gps_tag = "XEN0" + tag_categories = list("XENOFAUNA") diff --git a/mods/content/exploration/equipment/guns.dm b/mods/content/exploration/equipment/guns.dm new file mode 100644 index 000000000000..225e16f78e9d --- /dev/null +++ b/mods/content/exploration/equipment/guns.dm @@ -0,0 +1,53 @@ +/obj/item/gun/energy/gun/reloadable/phase + name = "phase carbine" + desc = "The RayZar EW26 Artemis is a downsized energy weapon, specifically designed for use against wildlife." + icon = 'mods/content/exploration/icons/phase_carbine.dmi' + icon_state = ICON_STATE_WORLD + slot_flags = SLOT_BACK|SLOT_LOWER_BODY + projectile_type = /obj/item/projectile/energy/phase // 50 damage against animals + one_hand_penalty = 15 + firemodes = null + indicator_color = COLOR_WHITE + charge_cost = 35 // ~14 shots + +/obj/item/gun/energy/gun/reloadable/phase/pistol + name = "phase pistol" + desc = "The RayZar EW15 Apollo is an energy handgun, specifically designed for self-defense against aggressive wildlife." + icon = 'mods/content/exploration/icons/phase_pistol.dmi' + w_class = ITEM_SIZE_NORMAL + slot_flags = SLOT_LOWER_BODY|SLOT_HOLSTER + projectile_type = /obj/item/projectile/energy/phase/light // 40 damage on animals + one_hand_penalty = 0 + charge_cost = 25 // 20 shots + +/obj/item/gun/energy/gun/reloadable/phase/rifle + name = "phase rifle" + desc = "The RayZar EW31 Orion is a specialist energy weapon, intended for use against hostile wildlife." + icon = 'mods/content/exploration/icons/phase_rifle.dmi' + w_class = ITEM_SIZE_LARGE + slot_flags = SLOT_BACK + projectile_type = /obj/item/projectile/energy/phase/heavy // 60 damage on animals + charge_cost = 50 // 10 shots + accuracy = 15 + one_hand_penalty = 30 + +/obj/item/gun/energy/gun/reloadable/phase/tranq_rifle + name = "tranquilizer rifle" + desc = "A niche RayZar product designed for nonlethal animal control. A specialized emitter disrupts the nervous system of the target, eventually inducing sleep. Only rated for use on wildlife." + icon = 'mods/content/exploration/icons/tranq_rifle.dmi' + w_class = ITEM_SIZE_LARGE + slot_flags = SLOT_BACK + charge_cost = 25 // 20 shots + projectile_type = /obj/item/projectile/energy/phase/tranq + accuracy = 15 + one_hand_penalty = 30 + +/obj/item/gun/energy/gun/reloadable/phase/tranq_pistol + name = "tranquilizer pistol" + desc = "A niche RayZar product designed for nonlethal animal control. A specialized emitter disrupts the nervous system of the target, eventually inducing sleep. Only rated for use on wildlife." + icon = 'mods/content/exploration/icons/tranq_pistol.dmi' + w_class = ITEM_SIZE_NORMAL + slot_flags = SLOT_LOWER_BODY|SLOT_HOLSTER + charge_cost = 15 // ~33 shots + projectile_type = /obj/item/projectile/energy/phase/tranq/weak + one_hand_penalty = 0 diff --git a/mods/content/exploration/equipment/loadout.dm b/mods/content/exploration/equipment/loadout.dm new file mode 100644 index 000000000000..30806beb0500 --- /dev/null +++ b/mods/content/exploration/equipment/loadout.dm @@ -0,0 +1,3 @@ +/decl/loadout_option/shoes/boots/get_gear_tweak_options() + . = ..() + .[/datum/gear_tweak/path/specified_types_list] |= /obj/item/clothing/shoes/winterboots/explorer diff --git a/mods/content/exploration/equipment/melee.dm b/mods/content/exploration/equipment/melee.dm new file mode 100644 index 000000000000..df69bcb97e74 --- /dev/null +++ b/mods/content/exploration/equipment/melee.dm @@ -0,0 +1,13 @@ +/obj/item/knife/folding/swiss/explorer + name = "explorer's combi-knife" + desc = "A small, purple, multi-purpose folding knife. This one adds a wood saw and prybar." + handle_color = COLOR_PURPLE + tools = list(SWISSKNF_LBLADE, SWISSKNF_SBLADE, SWISSKNF_CLIFTER, SWISSKNF_COPENER, SWISSKNF_WBLADE, SWISSKNF_CROWBAR) + +/obj/item/knife/survival + name = "survival knife" + desc = "A hunting-grade survival knife." + w_class = ITEM_SIZE_SMALL + icon = 'icons/obj/items/bladed/knife.dmi' + _base_attack_force = 6 + material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC // base icon is too dark to work with steel color diff --git a/mods/content/exploration/equipment/radios.dm b/mods/content/exploration/equipment/radios.dm new file mode 100644 index 000000000000..a92f2579a907 --- /dev/null +++ b/mods/content/exploration/equipment/radios.dm @@ -0,0 +1,22 @@ +/obj/item/encryptionkey/heads/hop/Initialize() + . = ..() + LAZYDISTINCTADD(can_decrypt, access_explorer) + +/obj/item/encryptionkey/heads/captain/Initialize() + . = ..() + LAZYDISTINCTADD(can_decrypt, access_explorer) + +/obj/item/encryptionkey/heads/ai_integrated/Initialize() + . = ..() + LAZYDISTINCTADD(can_decrypt, access_explorer) + +/obj/item/radio/headset/headset_exp + name = "explorer's headset" + desc = "A small headset used by exploration, with access to the explorer and science channels." + icon = 'icons/obj/items/device/radio/headsets/headset_science.dmi' + encryption_keys = list(/obj/item/encryptionkey/exp) + +/obj/item/encryptionkey/exp + name = "exploration radio encryption key" + inlay_color = COLOR_SCIENCE_PURPLE + can_decrypt = list(access_research, access_explorer) diff --git a/mods/content/exploration/equipment/specimen_tag.dm b/mods/content/exploration/equipment/specimen_tag.dm new file mode 100644 index 000000000000..76d959208e1c --- /dev/null +++ b/mods/content/exploration/equipment/specimen_tag.dm @@ -0,0 +1,98 @@ +// Specimen tag itself. +/obj/item/gps/specimen_tag + name = "xenofauna tracker" + gps_tag = "FAUNA0" + icon = 'mods/content/exploration/icons/specimen_tag.dmi' + decal_icon = 'mods/content/exploration/icons/specimen_tag_overlays.dmi' + w_class = ITEM_SIZE_TINY + tag_category = "XENOFAUNA" + + var/age = 0 + var/mob/living/implanted_in + var/implanted_by + var/physical_info = "No notes recorded." + var/behavioral_info = "No notes recorded." + +/obj/item/gps/specimen_tag/Initialize(mapload, _age, _implanted_by, _specimen_id, _specimen_gender, _physical_info, _behavioral_info, _specimen_type) + // If we have a specimen, set up our data. + if(_specimen_type) + var/mob/living/critter = new _specimen_type(get_turf(src)) + implant(critter, TRUE) + if(_specimen_gender) + critter.gender = _specimen_gender + if(_age) + age = _age + if(_specimen_id) + gps_tag = _specimen_id + if(_physical_info) + physical_info = _physical_info + if(_behavioral_info) + behavioral_info = _behavioral_info + if(_implanted_by) + implanted_by = _implanted_by + . = ..() + if(!tracking) + toggle_tracking() + +/obj/item/gps/specimen_tag/Destroy() + clear_implanted() + . = ..() + +/obj/item/gps/specimen_tag/Move() + . = ..() + if(implanted_in && loc != implanted_in) + clear_implanted() + +// Specimen tags are just for tracking, they don't work as held GPS. +/obj/item/gps/specimen_tag/attack_hand(mob/living/user) + SHOULD_CALL_PARENT(FALSE) + toggle_tracking(user) + return TRUE +/obj/item/gps/specimen_tag/check_visible_to_holder() + return FALSE +/obj/item/gps/specimen_tag/create_compass() + return +/obj/item/gps/specimen_tag/ui_interact(mob/user, ui_key, datum/nanoui/ui, force_open, master_ui, datum/topic_state/state) + return + +/obj/item/gps/specimen_tag/proc/has_been_implanted() + return !QDELETED(implanted_in) && istype(implanted_in) && loc == implanted_in + +/obj/item/gps/specimen_tag/proc/implant(var/mob/target, var/implanted_in_init = FALSE) + forceMove(target) + implanted_in = target + events_repository.register(/decl/observ/destroyed, implanted_in, src, /obj/item/gps/specimen_tag/proc/clear_implanted) + if(!implanted_in_init) + generate_critter_info() + +/obj/item/gps/specimen_tag/proc/generate_critter_info() + + var/list/possible_physical_info + var/list/possible_behavioral_info + var/datum/codex_entry/catalogue_data = SScodex.get_codex_entry(implanted_in.get_codex_value()) + if(istype(catalogue_data)) + var/notes = catalogue_data.get_fauna_physical_notes() + if(!isnull(notes)) + LAZYDISTINCTADD(possible_physical_info, notes) + notes = catalogue_data.get_fauna_behavior_notes() + if(!isnull(notes)) + LAZYDISTINCTADD(possible_behavioral_info, notes) + + if(LAZYLEN(possible_physical_info)) + physical_info = pick(possible_physical_info) + else + physical_info = "No notes recorded." + + if(LAZYLEN(possible_behavioral_info)) + behavioral_info = pick(possible_behavioral_info) + else + behavioral_info = "No notes recorded." + +/obj/item/gps/specimen_tag/proc/clear_implanted() + if(implanted_in) + events_repository.unregister(/decl/observ/destroyed, implanted_in, src) + implanted_in = null + +/obj/item/gps/specimen_tag/proc/update_from_animal() + return + diff --git a/mods/content/exploration/equipment/specimen_tagger.dm b/mods/content/exploration/equipment/specimen_tagger.dm new file mode 100644 index 000000000000..f3ae92479b8d --- /dev/null +++ b/mods/content/exploration/equipment/specimen_tagger.dm @@ -0,0 +1,115 @@ +// Device used to implant, remove or read specimen tags. + +/* Notes on specimen tagger and expected flow: + * - Xenofauna player uses tagger (/obj/item/specimen_tagger) on appropriate critter (appropriate type, has cataloguer info). + * - Tag (/obj/item/gps/specimen_tag) is created and registered in the critter. + * - Xenofauna players can then track the tag via GPS to scan, remove, etc. + * - When persistent specimens are committed, tags will be loaded and assigned to mobs at world init. + */ + +/obj/item/specimen_tagger + name = "specimen tagger" + desc = "A handheld device used to implant, remove and read xenofauna tracking tags from local specimens. Not for use on crewmembers." + icon = 'mods/content/exploration/icons/specimen_tagger.dmi' + icon_state = ICON_STATE_WORLD + _base_attack_force = 0 + item_flags = ITEM_FLAG_NO_BLUDGEON + var/tag_id = "FAUNA0" + +/obj/item/specimen_tagger/attack_self(mob/user) + var/new_tag = input("Please enter desired tag.", name, tag_id) as text + if(QDELETED(src) || QDELETED(user) || user.incapacitated() || loc != user) + return TRUE + new_tag = uppertext(copytext(sanitize(new_tag), 1, 11)) + if(!length(new_tag)) + return TRUE + tag_id = new_tag + to_chat(usr, "You set the tracker tag to '[tag_id]'.") + return TRUE + +/obj/item/specimen_tagger/get_examine_hints(mob/user, distance, infix, suffix) + . = ..() + . += "Use this on a living animal on help intent to read an existing tracker, grab intent to tag an animal with a tracker, and any other intent to remove an existing tracker." + +/obj/item/specimen_tagger/use_on_mob(mob/living/target, mob/living/user, animate) + SHOULD_CALL_PARENT(FALSE) + if(user.check_intent(I_FLAG_HELP)) + try_read_tag(user, target) + else if(user.check_intent(I_FLAG_GRAB)) + try_implant_tag(user, target) + else + try_remove_tag(user, target) + return TRUE + +/obj/item/specimen_tagger/proc/try_read_tag(var/mob/user, var/mob/living/target) + var/obj/item/gps/specimen_tag/xenotag = locate() in target + if(!istype(xenotag) || !xenotag.has_been_implanted()) + to_chat(user, SPAN_WARNING("\The [target] has not been tagged.")) + return FALSE + to_chat(user, "Specimen data for [xenotag.gps_tag]:") + to_chat(user, "Species: [target.real_name]") + to_chat(user, "Tag duration: [xenotag.age] shift\s") + to_chat(user, "Tagged by: [xenotag.implanted_by]") + to_chat(user, "Physical notes: [xenotag.physical_info]") + to_chat(user, "Behavioral notes: [xenotag.behavioral_info]") + return TRUE + +/obj/item/specimen_tagger/proc/check_can_tag(var/mob/user, var/mob/living/target) + if(QDELETED(target) || !istype(target) || target.stat == DEAD || target.isSynthetic()) + to_chat(user, SPAN_WARNING("Xenofauna specimens need to be living organic creatures.")) + return FALSE + if(!SScodex.get_codex_entry(target.get_codex_value())) + to_chat(user, SPAN_WARNING("There's no scientific reason to tag \the [target].")) + return FALSE + if(!target.is_tagging_suitable()) + to_chat(user, SPAN_WARNING("\The [target] is not suitable for tagging.")) + return FALSE + var/obj/item/gps/specimen_tag/xenotag = locate(/obj/item/gps/specimen_tag) in target + if(istype(xenotag) && xenotag.has_been_implanted()) + to_chat(user, SPAN_WARNING("\The [target] has already been tagged.")) + return FALSE + return TRUE + +/obj/item/specimen_tagger/proc/try_implant_tag(var/mob/user, var/mob/living/target) + if(!check_can_tag(user, target)) + return FALSE + user.visible_message(SPAN_NOTICE("\The [user] begins tagging \the [target] with \the [src]...")) + if(!do_after(user, 3 SECONDS, target) || !check_can_tag(user, target)) + return FALSE + var/obj/item/gps/specimen_tag/xenotag = new + xenotag.set_gps_tag(tag_id) + xenotag.implanted_by = user.real_name + if(user.mind) + var/user_title = user.mind.assigned_role || user.mind.role_alt_title + if(user_title) + xenotag.implanted_by = "[xenotag.implanted_by], [user_title]" + + xenotag.implant(target) + user.visible_message(SPAN_NOTICE("\The [user] tags \the [target] with \a [xenotag]!")) + return TRUE + +/obj/item/specimen_tagger/proc/can_remove_tag(var/mob/user, var/mob/living/target) + if(!istype(target)) + to_chat(user, SPAN_WARNING("\The [target] is not a xenofauna specimen.")) + return FALSE + var/obj/item/gps/specimen_tag/xenotag = locate() in target + if(!istype(xenotag) || !xenotag.has_been_implanted()) + to_chat(user, SPAN_WARNING("\The [target] has not been tagged.")) + return FALSE + return TRUE + +/obj/item/specimen_tagger/proc/try_remove_tag(var/mob/user, var/mob/living/target) + if(!can_remove_tag(user, target)) + return FALSE + var/obj/item/gps/specimen_tag/xenotag = locate() in target + if(!istype(xenotag)) + return FALSE + user.visible_message(SPAN_NOTICE("\The [user] starts removing \the [xenotag] from \the [target] with \the [src]...")) + if(!do_after(user, 3 SECONDS, target) || !can_remove_tag(user, target)) + return FALSE + if(!istype(xenotag)) + return FALSE + qdel(xenotag) + user.visible_message(SPAN_NOTICE("\The [user] removes \the [xenotag] from \the [target]!")) + return TRUE + diff --git a/mods/content/exploration/icons/boots_explo.dmi b/mods/content/exploration/icons/boots_explo.dmi new file mode 100644 index 000000000000..07c9e69e7e24 Binary files /dev/null and b/mods/content/exploration/icons/boots_explo.dmi differ diff --git a/mods/content/exploration/icons/cataloguer.dmi b/mods/content/exploration/icons/cataloguer.dmi new file mode 100644 index 000000000000..fb7fe153c8af Binary files /dev/null and b/mods/content/exploration/icons/cataloguer.dmi differ diff --git a/mods/content/exploration/icons/hood_explo.dmi b/mods/content/exploration/icons/hood_explo.dmi new file mode 100644 index 000000000000..270a990c68de Binary files /dev/null and b/mods/content/exploration/icons/hood_explo.dmi differ diff --git a/mods/content/exploration/icons/hood_xeno.dmi b/mods/content/exploration/icons/hood_xeno.dmi new file mode 100644 index 000000000000..0a6dd82c1a8e Binary files /dev/null and b/mods/content/exploration/icons/hood_xeno.dmi differ diff --git a/mods/content/exploration/icons/mask_explo.dmi b/mods/content/exploration/icons/mask_explo.dmi new file mode 100644 index 000000000000..e9c1f5b98c25 Binary files /dev/null and b/mods/content/exploration/icons/mask_explo.dmi differ diff --git a/mods/content/exploration/icons/phase_carbine.dmi b/mods/content/exploration/icons/phase_carbine.dmi new file mode 100644 index 000000000000..3e5ce1f94ea7 Binary files /dev/null and b/mods/content/exploration/icons/phase_carbine.dmi differ diff --git a/mods/content/exploration/icons/phase_pistol.dmi b/mods/content/exploration/icons/phase_pistol.dmi new file mode 100644 index 000000000000..7a074dede247 Binary files /dev/null and b/mods/content/exploration/icons/phase_pistol.dmi differ diff --git a/mods/content/exploration/icons/phase_rifle.dmi b/mods/content/exploration/icons/phase_rifle.dmi new file mode 100644 index 000000000000..b60a0b111f81 Binary files /dev/null and b/mods/content/exploration/icons/phase_rifle.dmi differ diff --git a/icons/screen/scanner.dmi b/mods/content/exploration/icons/scanner.dmi similarity index 100% rename from icons/screen/scanner.dmi rename to mods/content/exploration/icons/scanner.dmi diff --git a/mods/content/exploration/icons/specimen_tag.dmi b/mods/content/exploration/icons/specimen_tag.dmi new file mode 100644 index 000000000000..9c5446d4333f Binary files /dev/null and b/mods/content/exploration/icons/specimen_tag.dmi differ diff --git a/mods/content/exploration/icons/specimen_tag_overlays.dmi b/mods/content/exploration/icons/specimen_tag_overlays.dmi new file mode 100644 index 000000000000..75d53adb3b9d Binary files /dev/null and b/mods/content/exploration/icons/specimen_tag_overlays.dmi differ diff --git a/mods/content/exploration/icons/specimen_tagger.dmi b/mods/content/exploration/icons/specimen_tagger.dmi new file mode 100644 index 000000000000..fe1cbbda81f6 Binary files /dev/null and b/mods/content/exploration/icons/specimen_tagger.dmi differ diff --git a/icons/obj/stasis_cage.dmi b/mods/content/exploration/icons/stasis_cage.dmi similarity index 100% rename from icons/obj/stasis_cage.dmi rename to mods/content/exploration/icons/stasis_cage.dmi diff --git a/mods/content/exploration/icons/suit_explo.dmi b/mods/content/exploration/icons/suit_explo.dmi new file mode 100644 index 000000000000..98cfb4dfbc15 Binary files /dev/null and b/mods/content/exploration/icons/suit_explo.dmi differ diff --git a/mods/content/exploration/icons/suit_xeno.dmi b/mods/content/exploration/icons/suit_xeno.dmi new file mode 100644 index 000000000000..5847bb8b77de Binary files /dev/null and b/mods/content/exploration/icons/suit_xeno.dmi differ diff --git a/mods/content/exploration/icons/tranq_pistol.dmi b/mods/content/exploration/icons/tranq_pistol.dmi new file mode 100644 index 000000000000..1f47e0be9256 Binary files /dev/null and b/mods/content/exploration/icons/tranq_pistol.dmi differ diff --git a/mods/content/exploration/icons/tranq_rifle.dmi b/mods/content/exploration/icons/tranq_rifle.dmi new file mode 100644 index 000000000000..a81e4c15d49a Binary files /dev/null and b/mods/content/exploration/icons/tranq_rifle.dmi differ diff --git a/mods/content/exploration/icons/uniform_explo.dmi b/mods/content/exploration/icons/uniform_explo.dmi new file mode 100644 index 000000000000..5c7a4cf3457f Binary files /dev/null and b/mods/content/exploration/icons/uniform_explo.dmi differ diff --git a/mods/content/exploration/icons/uniform_xeno.dmi b/mods/content/exploration/icons/uniform_xeno.dmi new file mode 100644 index 000000000000..e3191b51dafb Binary files /dev/null and b/mods/content/exploration/icons/uniform_xeno.dmi differ diff --git a/mods/content/exploration/projectiles.dm b/mods/content/exploration/projectiles.dm new file mode 100644 index 000000000000..3f050ed826b7 --- /dev/null +++ b/mods/content/exploration/projectiles.dm @@ -0,0 +1,56 @@ +/obj/item/projectile/energy/phase + name = "phase wave" + icon_state = "phase" + fire_sound = 'sound/weapons/Gunshot_phase.ogg' + range = 6 + damage = 5 + var/animal_bonus_damage = 45 // 50 total on animals + +/obj/item/projectile/energy/phase/get_projectile_damage(mob/living/target) + if(isanimal(target) && !target.isSynthetic()) + return damage + animal_bonus_damage + return damage + +/obj/item/projectile/energy/phase/light + range = 4 + animal_bonus_damage = 35 // 40 total on animals + +/obj/item/projectile/energy/phase/heavy + range = 8 + animal_bonus_damage = 55 // 60 total on animals + +/obj/item/projectile/energy/phase/heavy/cannon + range = 10 + damage = 15 + animal_bonus_damage = 60 // 75 total on animals + +/obj/item/projectile/energy/phase/tranq + name = "tranquilizer wave" + range = 10 + damage = 0 + nodamage = TRUE + animal_bonus_damage = 0 + icon_state = "flight" + fire_sound = 'sound/weapons/dartgun.ogg' + fire_sound_vol_silenced = 5 + fire_sound_vol = 15 + var/tranq_delay = 6 SECONDS + var/tranq_power = 20 + +/obj/item/projectile/energy/phase/tranq/on_hit(atom/target, blocked, def_zone) + var/mob/living/victim = target + // TODO: consider just making this apply a sedative reagent when metabolism is unified. + if((. = ..()) && tranq_power && isanimal(victim) && !victim.isSynthetic()) + SET_STATUS_MAX(victim, STAT_DROWSY, ceil(tranq_delay / SSmobs.wait)) + addtimer(CALLBACK(victim, TYPE_PROC_REF(/mob/living, apply_delayed_tranq), tranq_power), tranq_delay) + +/mob/living/proc/apply_delayed_tranq(tranq_power) + if(!QDELETED(src) && stat != DEAD && !isSynthetic()) + SET_STATUS_MAX(src, STAT_ASLEEP, tranq_power) + +/obj/item/projectile/energy/phase/tranq/weak + range = 6 + tranq_power = 10 + fire_sound_vol_silenced = 5 + fire_sound_vol = 15 + tranq_delay = 9 SECONDS diff --git a/code/_onclick/hud/screen/screen_cataloguer.dm b/mods/content/exploration/screen_cataloguer.dm similarity index 97% rename from code/_onclick/hud/screen/screen_cataloguer.dm rename to mods/content/exploration/screen_cataloguer.dm index bb0965dac408..5062fb805dc9 100644 --- a/code/_onclick/hud/screen/screen_cataloguer.dm +++ b/mods/content/exploration/screen_cataloguer.dm @@ -4,7 +4,7 @@ layer = UNDER_HUD_LAYER mouse_opacity = MOUSE_OPACITY_UNCLICKABLE screen_loc = "CENTER,CENTER" - icon = 'icons/screen/scanner.dmi' + icon = 'mods/content/exploration/icons/scanner.dmi' icon_state = "blank" alpha = 180 requires_owner = FALSE diff --git a/mods/content/exploration/specimen_codex.dm b/mods/content/exploration/specimen_codex.dm new file mode 100644 index 000000000000..f307c6606809 --- /dev/null +++ b/mods/content/exploration/specimen_codex.dm @@ -0,0 +1,58 @@ +// Lists of strings used by the specimen tracking system to add flavour to +// specimens. Static lists in procs to allow overriding while also not putting +// a massive string list on every single cataloguer fauna entry. +/datum/codex_entry/proc/get_fauna_physical_notes() + var/static/list/fauna_physical_notes = list( + "Perpetually smells like mold no matter what we do about it.", + "Perpetually smells like mildew no matter what we do about it.", + "Perpetually smells like sifsap no matter what we do about it.", + "Seems to always have an itch in the spot it can't reach.", + "Vocalizations are notably harsh and loud for the species.", + "Vocalizations are notably soft and quiet for the species.", + "Has a notch in its left ear.", + "Has a notch in its right ear.", + "Is missing its left ear.", + "Is missing its right ear.", + "Is missing its left eye.", + "Is missing its right eye.", + "Has had the very tip of its tail chewed off.", + "Might have trouble with its hearing.", + "Is more scar tissue than animal at this point.", + "Walks with a limp.", + "Has two differently colored eyes.", + "Might have a cold...", + "Is allergic to nuts.", + "Is allergic to berries.", + "Is allergic to dairy." + ) + return fauna_physical_notes + +/datum/codex_entry/proc/get_fauna_behavior_notes() + var/static/list/fauna_behavior_notes = list( + "Likes rolling around in the moss with reckless abandon.", + "Enjoys munching on frostbelles the most, as a treat.", + "Enjoys munching on wabback the most, as a treat.", + "Enjoys munching on eyebulbs the most, as a treat.", + "Constantly sniffs around at everything new.", + "Seems super friendly! Probably won't bite. Probably.", + "Seems rather stand-offish. Mind the personal bubble.", + "Loves a good back scritch.", + "Loves a good head scritch.", + "Loves a good behind the ear scritch.", + "Seems to hate the world and everything in it.", + "Just tolerates being pet, but certainly doesn't enjoy it.", + "Often sticks its head into snowbanks to contemplate the state of things.", + "Enjoys singing along to songs only it can hear. Mostly just sounds like an animal wailing.", + "Would rather be fishing.", + "Partakes in many siestas.", + "Struggles with object permanence.", + "Is very picky about food; maybe it's a texture thing.", + "Is a sentient garbage disposal for anything even remotely edible.", + "Loves swimming and splashing around in water.", + "Sinks like a rock the moment it enters water.", + "Gets lost easily.", + "Enjoys soft things. It'd have a bed of plushies if it knew what a bed was.", + "Never seems to be in a rush to go anywhere...", + "Has gotta go fast at all times." + ) + return fauna_behavior_notes diff --git a/mods/content/exploration/specimen_mob.dm b/mods/content/exploration/specimen_mob.dm new file mode 100644 index 000000000000..7512cb01f06b --- /dev/null +++ b/mods/content/exploration/specimen_mob.dm @@ -0,0 +1,6 @@ +// Mob helpers/overrides. +/mob/living/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + var/obj/item/gps/specimen_tag/xenotag = locate() in src + if(istype(xenotag) && xenotag.has_been_implanted()) + . += "\The [src] has been tagged with \a [xenotag]." diff --git a/mods/content/exploration/structures/closets.dm b/mods/content/exploration/structures/closets.dm new file mode 100644 index 000000000000..bf4afd629f47 --- /dev/null +++ b/mods/content/exploration/structures/closets.dm @@ -0,0 +1,104 @@ +/obj/structure/closet/secure_closet/xenofauna + name = "xenofauna technician locker" + req_access = list(access_xenofauna) + +/obj/structure/closet/secure_closet/xenofauna/WillContain() + return list( + /obj/item/clothing/jumpsuit/xenofauna, + /obj/item/clothing/suit/explorer/xenofauna, + /obj/item/clothing/mask/gas/explorer, + /obj/item/clothing/shoes/winterboots/explorer, + /obj/item/clothing/gloves/black, + /obj/item/clothing/suit/jacket/winter/parka/purple, + /obj/item/radio/headset/headset_exp, + /obj/item/flashlight, + /obj/item/gps/xenofauna, + /obj/item/geiger, + /obj/item/cell/device, + /obj/item/radio, + /obj/item/cataloguer, + /obj/item/backpack/satchel/grey, + /obj/item/knife/survival, + /obj/item/specimen_tagger + ) + +/obj/structure/closet/secure_closet/guncabinet/phase + name = "explorer weapon cabinet" + req_access = list(access_explorer) + +/obj/structure/closet/secure_closet/guncabinet/phase/WillContain() + return list( + /obj/item/gun/energy/gun/reloadable/phase = 2, + /obj/item/gun/energy/gun/reloadable/phase/pistol, + /obj/item/cell/gun = 2, + /obj/item/clothing/permit/gun/planetside/exploration + ) + +/obj/structure/closet/secure_closet/guncabinet/tranq + name = "tranquilizer rifle cabinet" + req_access = list(access_xenofauna) + +/obj/structure/closet/secure_closet/guncabinet/tranq/WillContain() + return list( + /obj/item/gun/energy/gun/reloadable/phase/tranq_rifle = 2, + /obj/item/gun/energy/gun/reloadable/phase/tranq_pistol, + /obj/item/gun/energy/gun/reloadable/phase/pistol, + /obj/item/cell/gun = 2, + /obj/item/clothing/permit/gun/planetside + ) + +//Explorer Lockers +/obj/structure/closet/secure_closet/explorer + name = "explorer locker" + req_access = list(access_explorer) + closet_appearance = /decl/closet_appearance/secure_closet/expedition + +/obj/structure/closet/secure_closet/explorer/WillContain() + . = list( + /obj/item/clothing/jumpsuit/explorer, + /obj/item/clothing/suit/explorer, + /obj/item/clothing/mask/gas/explorer, + /obj/item/clothing/shoes/winterboots/explorer, + /obj/item/clothing/gloves/black, + /obj/item/radio/headset/headset_exp, + /obj/item/flashlight, + /obj/item/gps/explorer, + /obj/item/box/flares, + /obj/item/geiger, + /obj/item/cell/device, + /obj/item/radio, + /obj/item/stack/flag = 3, // 30, since each is a full stack of 10 + /obj/item/cataloguer) + if(prob(50)) + . += /obj/item/backpack/rucksack + else + . += /obj/item/backpack/satchel + if(prob(75)) + . += /obj/item/knife/utility + else + . += /obj/item/tool/machete + +//Xenofauna tech lockers +/obj/structure/closet/secure_closet/xenofauna + name = "xenofauna technician locker" + req_access = list(access_xenofauna) + +/obj/structure/closet/secure_closet/xenofauna/WillContain() + return list( + /obj/item/clothing/jumpsuit/xenofauna, + /obj/item/clothing/suit/explorer/xenofauna, + /obj/item/clothing/mask/gas/explorer, + /obj/item/clothing/shoes/winterboots/explorer, + /obj/item/clothing/gloves/black, + /obj/item/clothing/suit/jacket/winter/parka/purple, + /obj/item/radio/headset/headset_exp, + /obj/item/flashlight, + /obj/item/gps/xenofauna, + /obj/item/geiger, + /obj/item/cell/device, + /obj/item/radio, + /obj/item/cataloguer, + /obj/item/backpack/satchel, + /obj/item/knife/utility, + /obj/item/specimen_tagger + ) diff --git a/mods/content/exploration/structures/stasis_cage.dm b/mods/content/exploration/structures/stasis_cage.dm new file mode 100644 index 000000000000..e3ee7529b957 --- /dev/null +++ b/mods/content/exploration/structures/stasis_cage.dm @@ -0,0 +1,129 @@ +/obj/structure/stasis_cage + name = "stasis cage" + desc = "A high-tech animal cage, designed to keep contained fauna docile and safe." + icon = 'mods/content/exploration/icons/stasis_cage.dmi' + icon_state = "stasis_cage" + density = TRUE + layer = ABOVE_OBJ_LAYER + VAR_PRIVATE/weakref/_contained + +/obj/structure/stasis_cage/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + var/mob/living/simple_animal/A = locate() in loc + if(A) + contain(A) + +/obj/structure/stasis_cage/Destroy() + STOP_PROCESSING(SSobj, src) + release() + return ..() + +/obj/structure/stasis_cage/Process() + . = ..() + for(var/mob/specimen in contents) + specimen.add_mob_modifier(/decl/mob_modifier/stasis, SSobj.wait * 2, source = src) + +/obj/structure/stasis_cage/proc/get_specimen() + var/mob/living/simple_animal/critter = _contained?.resolve() + if(!critter || critter.loc != src || QDELETED(critter)) + _contained = null + return null + return critter + +/obj/structure/stasis_cage/attackby(obj/item/used_item, mob/user) + if(_contained && istype(used_item, /obj/item/scanner/xenobio)) + var/mob/living/simple_animal/specimen = get_specimen() + if(specimen) + return specimen.attackby(used_item, user) + . = ..() + +/obj/structure/stasis_cage/attack_hand(var/mob/user) + if(!user.check_dexterity(DEXTERITY_SIMPLE_MACHINES, TRUE)) + return ..() + try_release(user) + return TRUE + +/obj/structure/stasis_cage/attack_robot(var/mob/user) + if(CanPhysicallyInteract(user)) + try_release(user) + return TRUE + +/obj/structure/stasis_cage/proc/try_release(mob/user) + var/mob/living/simple_animal/specimen = get_specimen() + if(!specimen) + to_chat(user, SPAN_NOTICE("There's no animals inside \the [src]")) + return + user.visible_message("[user] begins undoing the locks and latches on \the [src].") + if(do_after(user, 20, src)) + + user.visible_message("[user] releases \the [specimen] from \the [src]!") + release() + +/obj/structure/stasis_cage/on_update_icon() + ..() + var/mob/living/simple_animal/specimen = get_specimen() + if(specimen) + icon_state = "[initial(icon_state)]_on" + else + icon_state = initial(icon_state) + +/obj/structure/stasis_cage/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + var/mob/living/simple_animal/specimen = get_specimen() + if(specimen) + . += "\The [specimen] is kept inside." + +/obj/structure/stasis_cage/proc/contain(var/mob/living/simple_animal/animal) + var/mob/living/simple_animal/specimen = get_specimen() + if(specimen || !istype(animal)) + return + + _contained = weakref(animal) + animal.forceMove(src) + update_icon() + +/obj/structure/stasis_cage/proc/release() + + var/mob/living/simple_animal/specimen = get_specimen() + if(!specimen) + return + + specimen.dropInto(src) + _contained = null + update_icon() + +/mob/living/simple_animal/handle_mouse_drop(atom/over, mob/user, params) + if(istype(over, /obj/structure/stasis_cage)) + var/obj/structure/stasis_cage/cage = over + if(!stat && !istype(buckled, /obj/effect/energy_net)) + to_chat(user, SPAN_WARNING("It's going to be difficult to convince \the [src] to move into \the [cage] without capturing it in a net.")) + return TRUE + user.visible_message( + SPAN_NOTICE("\The [user] begins loading \the [src] into \the [cage]."), + SPAN_NOTICE("You begin loading \the [src] into \the [cage].") + ) + Bumped(user) + if(do_after(user, 20, cage)) + cage.visible_message( + SPAN_NOTICE("\The [user] finishes loading \the [src] into \the [cage]."), + SPAN_NOTICE("You finishes loading \the [src] into \the [cage].") + ) + cage.contain(src) + return TRUE + . = ..() + +/obj/item/scanner/xenobio/is_valid_scan_target(atom/O) + if(istype(O, /obj/structure/stasis_cage)) + var/obj/structure/stasis_cage/cagie = O + return !!cagie.get_specimen() + return ..() + +/decl/hierarchy/supply_pack/science/stasis_cages + name = "Stasis Cage" + contains = list( + /obj/structure/stasis_cage = 1 + ) + containertype = /obj/structure/closet/crate/large + containername = "stasis cage crate" + access = access_xenofauna diff --git a/mods/content/fantasy/datum/kobaloi/markings.dm b/mods/content/fantasy/datum/kobaloi/markings.dm index 96132b522a3a..23281846c75a 100644 --- a/mods/content/fantasy/datum/kobaloi/markings.dm +++ b/mods/content/fantasy/datum/kobaloi/markings.dm @@ -1,6 +1,6 @@ /obj/item/organ/external/tail/kobaloi - tail_icon = 'mods/content/fantasy/icons/kobaloi/body.dmi' - tail_blend = ICON_MULTIPLY + tail_icon = 'mods/content/fantasy/icons/kobaloi/body.dmi' + tail_blend = ICON_MULTIPLY /decl/sprite_accessory/marking/kobaloi abstract_type = /decl/sprite_accessory/marking/kobaloi diff --git a/mods/content/fantasy/submaps/grassland/_grassland.dm b/mods/content/fantasy/submaps/grassland/_grassland.dm index 19b70b9367f0..e2860756be59 100644 --- a/mods/content/fantasy/submaps/grassland/_grassland.dm +++ b/mods/content/fantasy/submaps/grassland/_grassland.dm @@ -4,4 +4,4 @@ /datum/map_template/fantasy/cavern abstract_type = /datum/map_template/fantasy/cavern - template_categories = list(MAP_TEMPLATE_CATEGORY_FANTASY_CAVERNS) \ No newline at end of file + template_categories = list(MAP_TEMPLATE_CATEGORY_FANTASY_CAVERNS) diff --git a/mods/content/fantasy/submaps/swamp/_swamp.dm b/mods/content/fantasy/submaps/swamp/_swamp.dm index 6a3c2054ea9a..5b3401cfd6f8 100644 --- a/mods/content/fantasy/submaps/swamp/_swamp.dm +++ b/mods/content/fantasy/submaps/swamp/_swamp.dm @@ -1,3 +1,3 @@ /datum/map_template/fantasy/swamp abstract_type = /datum/map_template/fantasy/swamp - template_categories = list(MAP_TEMPLATE_CATEGORY_FANTASY_SWAMP) \ No newline at end of file + template_categories = list(MAP_TEMPLATE_CATEGORY_FANTASY_SWAMP) diff --git a/mods/content/government/away_sites/icarus/icarus-1.dmm b/mods/content/government/away_sites/icarus/icarus-1.dmm index 64d4c6491dbf..95c298b44eec 100644 --- a/mods/content/government/away_sites/icarus/icarus-1.dmm +++ b/mods/content/government/away_sites/icarus/icarus-1.dmm @@ -339,7 +339,7 @@ /area/icarus/vessel) "bu" = ( /obj/machinery/door/firedoor, -/obj/structure/sign/department/greencross{ +/obj/structure/sign/department/cross/green{ pixel_x = -30 }, /turf/floor/tiled, @@ -683,7 +683,7 @@ "cx" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/sign/department/greencross{ +/obj/structure/sign/department/cross/green{ pixel_x = -30 }, /turf/floor/tiled, @@ -1801,7 +1801,7 @@ /turf/floor/plating, /area/icarus/vessel) "fS" = ( -/obj/vehicle/train/cargo/engine, +/obj/vehicle/train/engine, /turf/floor/plating, /area/icarus/vessel) "fT" = ( @@ -1926,7 +1926,7 @@ /turf/floor/plating, /area/icarus/vessel) "gp" = ( -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/floor/plating, /area/icarus/vessel) "gq" = ( @@ -2545,7 +2545,7 @@ /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, /obj/effect/decal/cleanable/dirt/visible, -/obj/vehicle/train/cargo/trolley, +/obj/vehicle/train/trolley, /turf/unsimulated/floor/sand, /area/icarus/open) "ii" = ( diff --git a/mods/content/government/away_sites/icarus/icarus-2.dmm b/mods/content/government/away_sites/icarus/icarus-2.dmm index 2651b5a523e5..a58c0f33880d 100644 --- a/mods/content/government/away_sites/icarus/icarus-2.dmm +++ b/mods/content/government/away_sites/icarus/icarus-2.dmm @@ -750,13 +750,13 @@ /turf/floor/tiled, /area/icarus/vessel) "cB" = ( -/obj/structure/sign/double/solgovflag/left{ +/obj/structure/sign/double/flag/solgov/left{ pixel_y = -32 }, /turf/floor/tiled, /area/icarus/vessel) "cC" = ( -/obj/structure/sign/double/solgovflag/right{ +/obj/structure/sign/double/flag/solgov/right{ pixel_y = -32 }, /turf/floor/tiled, diff --git a/mods/content/government/away_sites/icarus/icarus.dm b/mods/content/government/away_sites/icarus/icarus.dm index 7715e42d5eca..5aa245d448bc 100644 --- a/mods/content/government/away_sites/icarus/icarus.dm +++ b/mods/content/government/away_sites/icarus/icarus.dm @@ -161,17 +161,17 @@ /obj/structure/sign/solgov name = "\improper SolGov Seal" desc = "A familiar seal showing this vessel is SolGov property." - icon = 'mods/content/government/away_sites/icarus/icarus_sprites.dmi' + icon = 'mods/content/government/away_sites/icarus/icarus_signs.dmi' icon_state = "solgovseal" -/obj/structure/sign/double/solgovflag +/obj/structure/sign/double/flag/solgov name = "Sol Central Government Flag" desc = "The iconic flag of the Sol Central Government, a symbol with many different meanings." - abstract_type = /obj/structure/sign/double/solgovflag - icon = 'mods/content/government/away_sites/icarus/icarus_sprites.dmi' + abstract_type = /obj/structure/sign/double/flag/solgov + icon = 'mods/content/government/away_sites/icarus/icarus_signs.dmi' -/obj/structure/sign/double/solgovflag/left +/obj/structure/sign/double/flag/solgov/left icon_state = "solgovflag-left" -/obj/structure/sign/double/solgovflag/right - icon_state = "solgovflag-right" \ No newline at end of file +/obj/structure/sign/double/flag/solgov/right + icon_state = "solgovflag-right" diff --git a/mods/content/government/away_sites/icarus/icarus_signs.dmi b/mods/content/government/away_sites/icarus/icarus_signs.dmi new file mode 100644 index 000000000000..1be188320c31 Binary files /dev/null and b/mods/content/government/away_sites/icarus/icarus_signs.dmi differ diff --git a/mods/content/government/away_sites/icarus/icarus_sprites.dmi b/mods/content/government/away_sites/icarus/icarus_sprites.dmi index 227ccc43c711..b2904fe95867 100644 Binary files a/mods/content/government/away_sites/icarus/icarus_sprites.dmi and b/mods/content/government/away_sites/icarus/icarus_sprites.dmi differ diff --git a/mods/content/integrated_electronics/_integrated_electronics.dme b/mods/content/integrated_electronics/_integrated_electronics.dme index 7a5c25290367..725bb52ed49e 100644 --- a/mods/content/integrated_electronics/_integrated_electronics.dme +++ b/mods/content/integrated_electronics/_integrated_electronics.dme @@ -10,7 +10,6 @@ #include "random.dm" #include "toggle_circuits_secret.dm" #include "assemblies\_assemblies.dm" -#include "assemblies\circuit_augment.dm" #include "components\_integrated_circuit.dm" #include "components\access.dm" #include "components\arithmetic.dm" diff --git a/mods/content/integrated_electronics/fabricator_designs.dm b/mods/content/integrated_electronics/fabricator_designs.dm index 2018e9d5bae0..0b6dc3289f5a 100644 --- a/mods/content/integrated_electronics/fabricator_designs.dm +++ b/mods/content/integrated_electronics/fabricator_designs.dm @@ -15,6 +15,3 @@ /datum/fabricator_recipe/protolathe/integrated_printer_upgrade_clone path = /obj/item/disk/integrated_circuit/upgrade/clone - -/datum/fabricator_recipe/robotics/augment/circuit - path = /obj/item/organ/internal/augment/active/simple/circuit diff --git a/mods/content/psionics/datum/antagonists/foundation.dm b/mods/content/psionics/datum/antagonists/foundation.dm index fd6b15b0bde7..8b4f204cad66 100644 --- a/mods/content/psionics/datum/antagonists/foundation.dm +++ b/mods/content/psionics/datum/antagonists/foundation.dm @@ -50,4 +50,4 @@ name = "\improper Foundation radio headset" desc = "The headset of the occult cavalry." icon = 'icons/obj/items/device/radio/headsets/headset_command.dmi' - encryption_keys = list(/obj/item/encryptionkey/ert) + encryption_keys = list(/obj/item/encryptionkey/specops) diff --git a/mods/content/response_team/_response_team.dm b/mods/content/response_team/_response_team.dm new file mode 100644 index 000000000000..e3cc2a1d78b2 --- /dev/null +++ b/mods/content/response_team/_response_team.dm @@ -0,0 +1,8 @@ +/decl/modpack/response_team + name = "Emergency Response Teams" + +/decl/modpack/response_team/pre_initialize() + global.admin_verbs_admin += /client/proc/response_team // Response Teams admin verb + for(var/client/client in global.admins) + client.add_admin_verbs() // refresh admin verbs. verbs are deduplicated so this is fine. todo: centralized modpack system for this + . = ..() \ No newline at end of file diff --git a/mods/content/response_team/_response_team.dme b/mods/content/response_team/_response_team.dme new file mode 100644 index 000000000000..a78e1496132e --- /dev/null +++ b/mods/content/response_team/_response_team.dme @@ -0,0 +1,18 @@ +// BEGIN_INCLUDE +#ifndef MODPACK_RESPONSE_TEAM +#define MODPACK_RESPONSE_TEAM +#include "_response_team.dm" +#include "ert_gear.dm" +#include "ert_rig.dm" +#include "game_mode_overrides.dm" +#include "response_team.dm" +#include "trader_overrides.dm" +#include "datum\ert_call_keycard_event.dm" +#include "datum\ert_camera_monitor.dm" +#include "datum\ert_config.dm" +#include "datum\ert_outfit.dm" +#include "datum\ert_special_role.dm" +#include "datum\spec_ops_outfit.dm" +#include "maps\ert_base.dm" +#endif +// END_INCLUDE \ No newline at end of file diff --git a/mods/content/response_team/datum/ert_call_keycard_event.dm b/mods/content/response_team/datum/ert_call_keycard_event.dm new file mode 100644 index 000000000000..87a1644b488e --- /dev/null +++ b/mods/content/response_team/datum/ert_call_keycard_event.dm @@ -0,0 +1,16 @@ +/decl/keycard_auth_event/call_ert + name = "Emergency Response Team" + uid = "keycard_event_call_ert" + +/decl/keycard_auth_event/call_ert/is_available(obj/machinery/keycard_auth/auth, mob/user) + if(get_config_value(/decl/config/toggle/ert_admin_call_only)) + return FALSE + return TRUE + +/decl/keycard_auth_event/call_ert/on_event(obj/machinery/keycard_auth/auth) + if(!SSticker.mode || SSticker.mode.ert_disabled) // disabled by mode + auth.visible_message(SPAN_WARNING("\The [src] blinks and displays a message: All emergency response teams are dispatched and can not be called at this time."), range=2) + return + + trigger_armed_response_team(1) + SSstatistics.add_field("alert_keycard_auth_ert",1) \ No newline at end of file diff --git a/mods/content/response_team/datum/ert_camera_monitor.dm b/mods/content/response_team/datum/ert_camera_monitor.dm new file mode 100644 index 000000000000..f4ef8194e871 --- /dev/null +++ b/mods/content/response_team/datum/ert_camera_monitor.dm @@ -0,0 +1,33 @@ +// ERT camera monitor +/datum/computer_file/program/camera_monitor/ert + filename = "ntcammon" + filedesc = "Advanced Camera Monitoring" + extended_desc = "This program allows remote access to a camera system. This version has an integrated database with additional encryption keys." + size = 14 + nanomodule_path = /datum/nano_module/program/camera_monitor/ert + available_on_network = FALSE + +/datum/nano_module/program/camera_monitor/ert + name = "ERT Camera Monitoring program" + +/datum/nano_module/program/camera_monitor/ert/get_forbidden_channels() + var/static/list/forbidden_channels = list( + (CAMERA_CHANNEL_MERCENARY) + ) + return forbidden_channels + +/datum/nano_module/program/camera_monitor/ert + name = "Advanced Camera Monitoring Program" + available_to_ai = FALSE + bypass_access = TRUE + +// ERT program ignores network connection requirement. +/datum/nano_module/program/camera_monitor/ert/can_connect_to_camera(datum/extension/network_device/camera/camera_device) + if(!camera_device) + return FALSE + if(!camera_device.is_functional()) + return FALSE + return TRUE + +/datum/nano_module/program/camera_monitor/ert/get_cameras_by_channel() + return camera_repository.get_devices_by_channel() \ No newline at end of file diff --git a/mods/content/response_team/datum/ert_config.dm b/mods/content/response_team/datum/ert_config.dm new file mode 100644 index 000000000000..2632497590d1 --- /dev/null +++ b/mods/content/response_team/datum/ert_config.dm @@ -0,0 +1,11 @@ +/decl/configuration_category/response_team + name = "Response Team" + desc = "Configuration options relating to emergency response teams." + configuration_file_location = "config/gamemodes/ert.txt" + associated_configuration = list( + /decl/config/toggle/ert_admin_call_only + ) + +/decl/config/toggle/ert_admin_call_only + uid = "ert_admin_call_only" + desc = "Restricted ERT to be only called by admins." \ No newline at end of file diff --git a/mods/content/response_team/datum/ert_outfit.dm b/mods/content/response_team/datum/ert_outfit.dm new file mode 100644 index 000000000000..e75f47ee3496 --- /dev/null +++ b/mods/content/response_team/datum/ert_outfit.dm @@ -0,0 +1,13 @@ +/decl/outfit/ert + name = "Spec Ops - Emergency response team" + uniform = /obj/item/clothing/pants/casual/camo/outfit_combat + shoes = /obj/item/clothing/shoes/jackboots/swat + gloves = /obj/item/clothing/gloves/thick/swat + l_ear = /obj/item/radio/headset/specops/ert + belt = /obj/item/gun/energy/gun + glasses = /obj/item/clothing/glasses/sunglasses + back = /obj/item/backpack/satchel + pda_type = /obj/item/modular_computer/pda/ert + + id_slot = slot_wear_id_str + id_type = /obj/item/card/id/centcom/ERT \ No newline at end of file diff --git a/code/game/antagonist/outsider/ert.dm b/mods/content/response_team/datum/ert_special_role.dm similarity index 100% rename from code/game/antagonist/outsider/ert.dm rename to mods/content/response_team/datum/ert_special_role.dm diff --git a/mods/content/response_team/datum/spec_ops_outfit.dm b/mods/content/response_team/datum/spec_ops_outfit.dm new file mode 100644 index 000000000000..d74ccce2da99 --- /dev/null +++ b/mods/content/response_team/datum/spec_ops_outfit.dm @@ -0,0 +1,25 @@ +/decl/outfit/spec_op_officer + name = "Spec Ops - Officer" + uniform = /obj/item/clothing/pants/casual/camo/outfit_combat + suit = /obj/item/clothing/suit/armor/officer + l_ear = /obj/item/radio/headset/specops/ert + glasses = /obj/item/clothing/glasses/thermal/plain/eyepatch + mask = /obj/item/clothing/mask/smokable/cigarette/cigar/havana + head = /obj/item/clothing/head/beret + belt = /obj/item/gun/energy/pulse_pistol + back = /obj/item/backpack/satchel + shoes = /obj/item/clothing/shoes/jackboots/swat/combat + gloves = /obj/item/clothing/gloves/thick/combat + + id_slot = slot_wear_id_str + id_type = /obj/item/card/id/centcom/ERT + id_desc = "Special operations ID." + id_pda_assignment = "Special Operations Officer" + +/decl/outfit/spec_op_officer/space + name = "Spec Ops - Officer in space" + suit = /obj/item/clothing/suit/space/void/swat + back = /obj/item/tank/jetpack/oxygen + mask = /obj/item/clothing/mask/gas/swat + + outfit_flags = OUTFIT_HAS_JETPACK|OUTFIT_RESET_EQUIPMENT \ No newline at end of file diff --git a/mods/content/response_team/ert_gear.dm b/mods/content/response_team/ert_gear.dm new file mode 100644 index 000000000000..d5311230b2fb --- /dev/null +++ b/mods/content/response_team/ert_gear.dm @@ -0,0 +1,69 @@ +//ERT backpacks. +/obj/item/backpack/ert + name = "emergency response team backpack" + desc = "A spacious backpack with lots of pockets, used by members of the Emergency Response Team." + icon = 'mods/content/response_team/icons/backpack_ert.dmi' + var/marking_state + var/marking_colour + +/obj/item/backpack/ert/on_update_icon() + . = ..() + if(marking_state) + var/image/I = image(icon, marking_state) + I.color = marking_colour + I.appearance_flags |= RESET_COLOR + add_overlay(I) + +/obj/item/backpack/ert/adjust_mob_overlay(mob/living/user_mob, bodytype, image/overlay, slot, bodypart, use_fallback_if_icon_missing = TRUE) + if(overlay && slot == slot_back_str && marking_state) + var/image/I = image(overlay.icon, "[overlay.icon_state]-[marking_state]") + I.color = marking_colour + I.appearance_flags |= RESET_COLOR + overlay.add_overlay(I) + . = ..() + +/obj/item/backpack/ert/commander + name = "emergency response team commander backpack" + desc = "A spacious backpack with lots of pockets, worn by the commander of an Emergency Response Team." + marking_colour = COLOR_BLUE_GRAY + marking_state = "com" + +/obj/item/backpack/ert/security + name = "emergency response team security backpack" + desc = "A spacious backpack with lots of pockets, worn by security members of an Emergency Response Team." + marking_colour = COLOR_NT_RED + marking_state = "sec" + +/obj/item/backpack/ert/engineer + name = "emergency response team engineer backpack" + desc = "A spacious backpack with lots of pockets, worn by engineering members of an Emergency Response Team." + marking_colour = COLOR_GOLD + marking_state = "eng" + +/obj/item/backpack/ert/medical + name = "emergency response team medical backpack" + desc = "A spacious backpack with lots of pockets, worn by medical members of an Emergency Response Team." + marking_colour = COLOR_OFF_WHITE + marking_state = "med" + +//ERT PDA preset +/obj/item/modular_computer/pda/ert + color = COLOR_OFF_WHITE + decals = list( + "stripe" = COLOR_DARK_BLUE_GRAY, + "stripe2" = COLOR_GOLD + ) + +//ERT headsets +/obj/item/encryptionkey/specops/ert + name = "\improper ERT radio encryption key" + can_decrypt = list(access_cent_specops) + +/obj/item/radio/headset/specops/ert + name = "emergency response team radio headset" + desc = "The headset of the boss's boss." + icon = 'icons/obj/items/device/radio/headsets/headset_admin.dmi' + encryption_keys = list(/obj/item/encryptionkey/specops/ert) + +/obj/item/radio/borg/ert + encryption_keys = list(/obj/item/encryptionkey/specops/ert) \ No newline at end of file diff --git a/maps/antag_spawn/ert/rig.dm b/mods/content/response_team/ert_rig.dm similarity index 100% rename from maps/antag_spawn/ert/rig.dm rename to mods/content/response_team/ert_rig.dm diff --git a/mods/content/response_team/game_mode_overrides.dm b/mods/content/response_team/game_mode_overrides.dm new file mode 100644 index 000000000000..38c8390e7d9f --- /dev/null +++ b/mods/content/response_team/game_mode_overrides.dm @@ -0,0 +1,64 @@ +/decl/game_mode + /// If TRUE, ERT cannot be called during this mode. + var/ert_disabled = FALSE + +/decl/game_mode/toggle_value(key) + if((. = ..())) + return + switch(key) + if("ert") + ert_disabled = !ert_disabled + announce_ert_disabled() + return TRUE + +/decl/game_mode/post_setup() + . = ..() + addtimer(CALLBACK(src, PROC_REF(announce_ert_disabled)), rand_waittime + rand(10 SECONDS, 15 SECONDS)) + +/// Gets a list of default reasons for the ERT to be disabled. +/decl/game_mode/proc/possible_ert_disabled_reasons() + // This uses a static var so that modpacks can add default reasons, e.g. "supermatter dust". + var/static/list/reasons = list( + "political instability", + "quantum fluctuations", + "hostile raiders", + "derelict station debris", + "REDACTED", + "ancient alien artillery", + "solar magnetic storms", + "sentient time-travelling killbots", + "gravitational anomalies", + "wormholes to another dimension", + "a telescience mishap", + "radiation flares", + "leaks into a negative reality", + "antiparticle clouds", + "residual exotic energy", + "suspected criminal operatives", + "malfunctioning von Neumann probe swarms", + "shadowy interlopers", + "a stranded xenoform", + "haywire machine constructs", + "rogue exiles", + "artifacts of eldritch horror", + "a brain slug infestation", + "killer bugs that lay eggs in the husks of the living", + "a deserted transport carrying xenofauna specimens", + "an emissary requesting a security detail", + "radical transevolutionaries", + "classified security operations", + "a gargantuan glowing goat" + ) + return reasons + +/decl/game_mode/proc/announce_ert_disabled() + if(!ert_disabled) + return + command_announcement.Announce("The presence of [pick(possible_ert_disabled_reasons())] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission") + +// add this as an option +/datum/controller/subsystem/ticker/get_game_mode_options() + var/list/options = ..() + // insert it at the start because it's important i guess + options.Insert(1, "Emergency Response Teams: [mode.ert_disabled ? "disabled" : "enabled"]") + return options \ No newline at end of file diff --git a/icons/obj/items/storage/backpack/backpack_ert.dmi b/mods/content/response_team/icons/backpack_ert.dmi similarity index 100% rename from icons/obj/items/storage/backpack/backpack_ert.dmi rename to mods/content/response_team/icons/backpack_ert.dmi diff --git a/maps/antag_spawn/ert/ert.dm b/mods/content/response_team/maps/ert_base.dm similarity index 72% rename from maps/antag_spawn/ert/ert.dm rename to mods/content/response_team/maps/ert_base.dm index 0422dfdada69..38ed2242ec83 100644 --- a/maps/antag_spawn/ert/ert.dm +++ b/mods/content/response_team/maps/ert_base.dm @@ -1,6 +1,7 @@ /datum/map_template/ruin/antag_spawn/ert name = "ERT Base" - suffixes = list("ert/ert_base.dmm") + prefix = "mods/content/response_team/maps/" + suffixes = list("ert_base.dmm") modify_tag_vars = FALSE shuttles_to_initialise = list(/datum/shuttle/autodock/multi/antag/rescue) apc_test_exempt_areas = list( @@ -42,6 +43,22 @@ landmark_tag = "nav_ert_start" docking_controller = "ert_rescue_base" +/obj/machinery/camera/network/ert + preset_channels = list(CAMERA_CHANNEL_ERT) + cameranet_enabled = FALSE + req_access = list(access_engine) + +/obj/machinery/computer/modular/preset/full/ert + default_software = list( + /datum/computer_file/program/camera_monitor/ert, + /datum/computer_file/program/email_client, + /datum/computer_file/program/alarm_monitor, + /datum/computer_file/program/comm, + /datum/computer_file/program/aidiag, + /datum/computer_file/program/records, + /datum/computer_file/program/wordprocessor + ) + // Areas /area/map_template/rescue_base @@ -59,7 +76,4 @@ /area/map_template/rescue_base/start name = "\improper Response Team Base" icon_state = "shuttlered" - base_turf = /turf/unsimulated/floor/rescue_base - -// Separated in preparation for making ERTs into a modpack. -#include "rig.dm" \ No newline at end of file + base_turf = /turf/unsimulated/floor/rescue_base \ No newline at end of file diff --git a/maps/antag_spawn/ert/ert_base.dmm b/mods/content/response_team/maps/ert_base.dmm similarity index 99% rename from maps/antag_spawn/ert/ert_base.dmm rename to mods/content/response_team/maps/ert_base.dmm index 331f3bca8288..dbb532ffeb55 100644 --- a/maps/antag_spawn/ert/ert_base.dmm +++ b/mods/content/response_team/maps/ert_base.dmm @@ -904,7 +904,7 @@ /turf/unsimulated/floor/vault, /area/map_template/rescue_base/base) "cy" = ( -/obj/machinery/chemical_dispenser/ert, +/obj/machinery/chemical_dispenser/medicine, /obj/item/chems/glass/beaker/large, /turf/unsimulated/floor/vault, /area/map_template/rescue_base/base) diff --git a/code/game/response_team.dm b/mods/content/response_team/response_team.dm similarity index 100% rename from code/game/response_team.dm rename to mods/content/response_team/response_team.dm diff --git a/mods/content/response_team/trader_overrides.dm b/mods/content/response_team/trader_overrides.dm new file mode 100644 index 000000000000..89633a51e39c --- /dev/null +++ b/mods/content/response_team/trader_overrides.dm @@ -0,0 +1,4 @@ +/datum/trader/trading_beacon/New() + ..() + if(type == /datum/trader/trading_beacon) // don't affect subtypes. ugh, i hate that this is necessary + possible_trading_items[/obj/item/backpack/ert] = TRADER_BLACKLIST_ALL \ No newline at end of file diff --git a/mods/content/standard_jobs/outfits/cargo.dm b/mods/content/standard_jobs/outfits/cargo.dm index 03430ec15bc0..92e2760ed57c 100644 --- a/mods/content/standard_jobs/outfits/cargo.dm +++ b/mods/content/standard_jobs/outfits/cargo.dm @@ -27,7 +27,7 @@ uniform = /obj/item/clothing/jumpsuit/miner id_type = /obj/item/card/id/cargo pda_type = /obj/item/modular_computer/pda/science - backpack_contents = list(/obj/item/crowbar = 1, /obj/item/ore = 1) + backpack_contents = list(/obj/item/crowbar = 1, /obj/item/ore_satchel = 1) outfit_flags = OUTFIT_HAS_BACKPACK | OUTFIT_EXTENDED_SURVIVAL | OUTFIT_HAS_VITALS_SENSOR /decl/outfit/job/cargo/mining/Initialize() diff --git a/mods/content/supermatter/overrides/sm_strings.dm b/mods/content/supermatter/overrides/sm_strings.dm index 72413b05d274..510a49fac07a 100644 --- a/mods/content/supermatter/overrides/sm_strings.dm +++ b/mods/content/supermatter/overrides/sm_strings.dm @@ -1,11 +1,3 @@ -/decl/game_mode/possible_ert_disabled_reasons() - var/static/sm_injected = FALSE - if(sm_injected) - return ..() - sm_injected = TRUE - . = ..() - . += "supermatter dust" - /obj/item/disk/secret_project/get_secret_project_nouns() var/static/sm_injected = FALSE if(sm_injected) diff --git a/mods/content/xenobiology/slime/slime_reagents.dm b/mods/content/xenobiology/slime/slime_reagents.dm index a88694656b6c..6fd150032bd5 100644 --- a/mods/content/xenobiology/slime/slime_reagents.dm +++ b/mods/content/xenobiology/slime/slime_reagents.dm @@ -12,6 +12,7 @@ heating_message = "becomes clear." color = "#cf3600" metabolism = REM * 0.25 + opacity = 0.7 // copied from cherry jelly exoplanet_rarity_gas = MAT_RARITY_EXOTIC /decl/material/liquid/water/affect_touch(var/mob/living/victim, var/removed, var/datum/reagents/holder) diff --git a/mods/content/xenobiology/species/golem.dm b/mods/content/xenobiology/species/golem.dm index 51b9a2046e88..afb79c1f4cad 100644 --- a/mods/content/xenobiology/species/golem.dm +++ b/mods/content/xenobiology/species/golem.dm @@ -36,7 +36,7 @@ force_background_info = list( /decl/background_category/heritage = /decl/background_detail/heritage/hidden/cultist, /decl/background_category/homeworld = /decl/background_detail/location/stateless, - /decl/background_category/citizenship = /decl/background_detail/citizenship/other, + /decl/background_category/citizenship = /decl/background_detail/citizenship/synthetic, /decl/background_category/faction = /decl/background_detail/faction/other ) diff --git a/mods/gamemodes/cult/mobs/constructs/constructs.dm b/mods/gamemodes/cult/mobs/constructs/constructs.dm index f12c1282f4d8..ad1932964381 100644 --- a/mods/gamemodes/cult/mobs/constructs/constructs.dm +++ b/mods/gamemodes/cult/mobs/constructs/constructs.dm @@ -130,9 +130,10 @@ /mob/living/simple_animal/construct/armoured/bullet_act(var/obj/item/projectile/P) if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) - var/reflectchance = 80 - round(P.damage/3) + var/damage = P.get_projectile_damage(src) + var/reflectchance = 80 - round(damage/3) if(prob(reflectchance)) - take_damage(P.damage * 0.5) + take_damage(damage * 0.5) visible_message("The [P.name] gets reflected by [src]'s shell!", \ "The [P.name] gets reflected by [src]'s shell!") diff --git a/mods/gamemodes/heist/outfit.dm b/mods/gamemodes/heist/outfit.dm index fc04cd392389..435b90296478 100644 --- a/mods/gamemodes/heist/outfit.dm +++ b/mods/gamemodes/heist/outfit.dm @@ -45,7 +45,7 @@ /obj/item/gun/energy/taser, /obj/item/gun/energy/crossbow/largecrossbow, /obj/item/gun/launcher/bow/crossbow/powered, - /obj/item/gun/launcher/grenade/loaded, + /obj/item/gun/launcher/grenade/random, /obj/item/gun/launcher/pneumatic, /obj/item/gun/projectile/automatic/smg, /obj/item/gun/projectile/automatic/assault_rifle, diff --git a/mods/gamemodes/mercenary/mercenary_uplink.dm b/mods/gamemodes/mercenary/mercenary_uplink.dm index 935c254c2d1d..e2de8ff768c9 100644 --- a/mods/gamemodes/mercenary/mercenary_uplink.dm +++ b/mods/gamemodes/mercenary/mercenary_uplink.dm @@ -33,7 +33,7 @@ desc = "A pump action grenade launcher loaded with a random assortment of grenades" item_cost = 60 antag_roles = list(/decl/special_role/mercenary) - path = /obj/item/gun/launcher/grenade/loaded + path = /obj/item/gun/launcher/grenade/random /datum/uplink_item/item/visible_weapons/smg name = "Standard Submachine Gun" diff --git a/mods/gamemodes/ninja/maps/ninja_base.dmm b/mods/gamemodes/ninja/maps/ninja_base.dmm index 2cb311efb7ae..05709304e93f 100644 --- a/mods/gamemodes/ninja/maps/ninja_base.dmm +++ b/mods/gamemodes/ninja/maps/ninja_base.dmm @@ -122,7 +122,7 @@ /turf/unsimulated/floor/white, /area/map_template/ninja_dojo/dojo) "aD" = ( -/obj/machinery/chemical_dispenser/ert, +/obj/machinery/chemical_dispenser/medicine, /obj/item/chems/glass/beaker/large, /turf/unsimulated/floor/white, /area/map_template/ninja_dojo/dojo) @@ -352,7 +352,7 @@ /turf/unsimulated/floor/snow_plating, /area/map_template/ninja_dojo/dojo) "bo" = ( -/obj/effect/floor_decal/snow, +/obj/effect/floor_decal/snow_floor, /turf/unsimulated/floor/snow_plating, /area/map_template/ninja_dojo/dojo) "bp" = ( @@ -445,7 +445,7 @@ /turf/unsimulated/floor/freezer, /area/map_template/ninja_dojo/dojo) "bE" = ( -/obj/effect/floor_decal/snow, +/obj/effect/floor_decal/snow_floor, /turf/unsimulated/floor/freezer, /area/map_template/ninja_dojo/dojo) "bF" = ( @@ -469,7 +469,7 @@ /turf/unsimulated/floor/carpet, /area/map_template/ninja_dojo/dojo) "bJ" = ( -/obj/effect/floor_decal/snow, +/obj/effect/floor_decal/snow_floor, /turf/unsimulated/floor/ice, /area/map_template/ninja_dojo/dojo) "bK" = ( @@ -484,7 +484,7 @@ /turf/unsimulated/floor/wood, /area/map_template/ninja_dojo/dojo) "bM" = ( -/obj/effect/floor_decal/snow, +/obj/effect/floor_decal/snow_floor, /obj/abstract/modpack_compat/aliumizer, /turf/unsimulated/floor/ice, /area/map_template/ninja_dojo/dojo) diff --git a/mods/pyrelight/_pyrelight.dme b/mods/pyrelight/_pyrelight.dme index ffb1b4057f8e..1644ac27db6b 100644 --- a/mods/pyrelight/_pyrelight.dme +++ b/mods/pyrelight/_pyrelight.dme @@ -1,24 +1,24 @@ -// BEGIN_INCLUDE -#ifndef MODPACK_PYRELIGHT -#define MODPACK_PYRELIGHT -#include "_pyrelight.dm" -#include "datum\traits\_wyrd.dm" -#include "datum\traits\_wyrd_categories.dm" -#include "datum\traits\wyrd_wild.dm" -#include "datum\wyrdling\ears.dm" -#include "datum\wyrdling\mask.dm" -#include "datum\wyrdling\tails.dm" -#include "datum\culture.dm" -#include "datum\factions.dm" -#include "datum\locations.dm" -#include "datum\religions.dm" -#include "plants\_plants.dm" -#include "plants\_plot.dm" -#include "plants\_subsystem.dm" -#include "plants\plants_fruit.dm" -#include "plants\plants_fruit_segment.dm" -#include "plants\plants_fruit_template.dm" -#include "plants\fruit_subtypes\nightweave.dm" -#include "plants\plant_subtypes\nightweave.dm" -#endif -// END_INCLUDE +// BEGIN_INCLUDE +#ifndef MODPACK_PYRELIGHT +#define MODPACK_PYRELIGHT +#include "_pyrelight.dm" +#include "datum\traits\_wyrd.dm" +#include "datum\traits\_wyrd_categories.dm" +#include "datum\traits\wyrd_wild.dm" +#include "datum\wyrdling\ears.dm" +#include "datum\wyrdling\mask.dm" +#include "datum\wyrdling\tails.dm" +#include "datum\culture.dm" +#include "datum\factions.dm" +#include "datum\locations.dm" +#include "datum\religions.dm" +#include "plants\_plants.dm" +#include "plants\_plot.dm" +#include "plants\_subsystem.dm" +#include "plants\plants_fruit.dm" +#include "plants\plants_fruit_segment.dm" +#include "plants\plants_fruit_template.dm" +#include "plants\fruit_subtypes\nightweave.dm" +#include "plants\plant_subtypes\nightweave.dm" +#endif +// END_INCLUDE diff --git a/mods/pyrelight/plants/plants_fruit.dm b/mods/pyrelight/plants/plants_fruit.dm index f6ac60541a6e..b6f8e5bb765c 100644 --- a/mods/pyrelight/plants/plants_fruit.dm +++ b/mods/pyrelight/plants/plants_fruit.dm @@ -5,18 +5,19 @@ icon_state = "base" material = /decl/material/solid/organic/plantmatter is_spawnable_type = FALSE + abstract_type = /obj/item/food/fruit var/examine_info var/examine_info_skill = SKILL_BOTANY var/examine_info_rank = SKILL_BASIC var/list/removed_segments -/obj/item/food/fruit/initialize_reagents(populate) +/obj/item/food/fruit/Initialize(ml, material_key, skip_plate) var/segment_amount = 0 for(var/datum/fruit_segment/comp as anything in get_composition()) if(comp.contributes_to_fruit_reagents) segment_amount += comp.reagent_total - create_reagents(segment_amount) - return ..() + chem_volume = segment_amount + . = ..() /obj/item/food/fruit/populate_reagents() for(var/datum/fruit_segment/comp as anything in get_composition()) diff --git a/mods/pyrelight/plants/plants_fruit_segment.dm b/mods/pyrelight/plants/plants_fruit_segment.dm index c1cbe47dd5b4..4e5678471de6 100644 --- a/mods/pyrelight/plants/plants_fruit_segment.dm +++ b/mods/pyrelight/plants/plants_fruit_segment.dm @@ -21,10 +21,7 @@ icon = _fruit.icon icon_state = "seg_[_template.icon_state]" fruit_template = _template - return ..() - -/obj/item/food/fruit_segment/initialize_reagents(populate) - create_reagents(fruit_template.reagent_total) + chem_volume = fruit_template?.reagent_total || chem_volume return ..() /obj/item/food/fruit_segment/populate_reagents() diff --git a/mods/species/adherent/datum/species.dm b/mods/species/adherent/datum/species.dm index 89913e40651c..a9130f4fe2f4 100644 --- a/mods/species/adherent/datum/species.dm +++ b/mods/species/adherent/datum/species.dm @@ -56,7 +56,7 @@ available_background_info = list( /decl/background_category/citizenship = list( - /decl/background_detail/citizenship/other + /decl/background_detail/citizenship/synthetic ), /decl/background_category/heritage = list( /decl/background_detail/heritage/adherent diff --git a/mods/species/ascent/_ascent.dme b/mods/species/ascent/_ascent.dme index acb7c9b9a93e..63f077b54d9e 100644 --- a/mods/species/ascent/_ascent.dme +++ b/mods/species/ascent/_ascent.dme @@ -7,7 +7,6 @@ #include "datum\antagonist.dm" #include "datum\codex.dm" #include "datum\culture.dm" -#include "datum\descriptors.dm" #include "datum\emotes.dm" #include "datum\languages.dm" #include "datum\species.dm" diff --git a/mods/species/ascent/datum/species.dm b/mods/species/ascent/datum/species.dm index 2a26f97bfba3..c7bbea3566f1 100644 --- a/mods/species/ascent/datum/species.dm +++ b/mods/species/ascent/datum/species.dm @@ -88,7 +88,7 @@ /decl/species/mantid/handle_sleeping(var/mob/living/human/H) return -/decl/species/mantid/equip_survival_gear(var/mob/living/human/H, var/extendedtank = 1) +/decl/species/mantid/equip_survival_gear(mob/living/wearer, box_type = /obj/item/box/survival) return /decl/species/mantid/gyne diff --git a/mods/species/ascent/machines/ship_machines.dm b/mods/species/ascent/machines/ship_machines.dm index 447467987067..b761299d5138 100644 --- a/mods/species/ascent/machines/ship_machines.dm +++ b/mods/species/ascent/machines/ship_machines.dm @@ -149,7 +149,7 @@ MANTIDIFY(/obj/item/chems/chem_disp_cartridge, "canister", "chemical storage") // This is an absolutely stupid machine. Basically the same as the debug one with some alterations. // It is a placeholder for a proper reactor setup (probably a RUST descendant) -/obj/machinery/power/ascent_reactor +/obj/machinery/ascent_reactor name = "mantid fusion stack" desc = "A tall, gleaming assemblage of advanced alien machinery. It hums and crackles with restrained power." icon = 'icons/obj/machines/power/fusion_core.dmi' @@ -159,7 +159,7 @@ MANTIDIFY(/obj/item/chems/chem_disp_cartridge, "canister", "chemical storage") var/output_power = 9000 KILOWATTS var/image/field_image -/obj/machinery/power/ascent_reactor/attack_hand(mob/user) +/obj/machinery/ascent_reactor/attack_hand(mob/user) if(!user.check_dexterity(DEXTERITY_COMPLEX_TOOLS, TRUE)) return ..() if(ishuman(user)) @@ -175,7 +175,7 @@ MANTIDIFY(/obj/item/chems/chem_disp_cartridge, "canister", "chemical storage") update_icon() return TRUE -/obj/machinery/power/ascent_reactor/on_update_icon() +/obj/machinery/ascent_reactor/on_update_icon() . = ..() if(!field_image) @@ -198,13 +198,13 @@ MANTIDIFY(/obj/item/chems/chem_disp_cartridge, "canister", "chemical storage") set_light(0) icon_state = "core0" -/obj/machinery/power/ascent_reactor/Initialize() +/obj/machinery/ascent_reactor/Initialize() . = ..() update_icon() -/obj/machinery/power/ascent_reactor/Process() +/obj/machinery/ascent_reactor/Process() if(on) - add_avail(output_power) + generate_power(output_power) /obj/machinery/power/smes/buildable/power_shuttle/ascent name = "mantid battery" diff --git a/mods/species/neoavians/_neoavians.dme b/mods/species/neoavians/_neoavians.dme index 6c5b71cb056f..d992fb25eba8 100644 --- a/mods/species/neoavians/_neoavians.dme +++ b/mods/species/neoavians/_neoavians.dme @@ -4,11 +4,14 @@ #include "_neoavians.dm" #include "_overrides.dm" #include "clothing.dm" -#include "datum\accessory.dm" +#include "datum\ears.dm" +#include "datum\hair.dm" #include "datum\language.dm" #include "datum\loadout.dm" +#include "datum\markings.dm" #include "datum\species.dm" #include "datum\species_bodytypes.dm" +#include "datum\tails.dm" #include "machinery\suit_cycler.dm" // END_INCLUDE #endif diff --git a/mods/species/neoavians/clothing.dm b/mods/species/neoavians/clothing.dm index 3c78ace7940f..127c9ccf2505 100644 --- a/mods/species/neoavians/clothing.dm +++ b/mods/species/neoavians/clothing.dm @@ -35,6 +35,12 @@ /obj/item/clothing/suit/cloak _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/accessory/cloak.dmi' +/obj/item/clothing/suit/hooded_cloak + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/accessory/cloak_hooded.dmi' + +/obj/item/clothing/head/hood/cloak + _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/head/cloak_hood.dmi' + /obj/item/clothing/suit/cloak/hide _avian_onmob_icon = 'mods/species/neoavians/icons/clothing/accessory/cloak_hide.dmi' @@ -94,3 +100,20 @@ _base_attack_force = 1 item_flags = ITEM_FLAG_SILENT w_class = ITEM_SIZE_SMALL + +/obj/item/clothing/suit/hooded_cloak/avian + name = "striped cloak" + paint_color = "#333333" + markings_color = "#ff7519" + markings_state_modifier = "-stripe" + bodytype_equip_flags = BODY_EQUIP_FLAG_AVIAN + icon = 'mods/species/neoavians/icons/clothing/accessory/cloak_hooded.dmi' + hood = /obj/item/clothing/head/hood/cloak/avian + +/obj/item/clothing/head/hood/cloak/avian + name = "striped hood" + paint_color = "#333333" + markings_color = "#ff7519" + markings_state_modifier = "-stripe" + bodytype_equip_flags = BODY_EQUIP_FLAG_AVIAN + icon = 'mods/species/neoavians/icons/clothing/head/cloak_hood.dmi' diff --git a/mods/species/neoavians/datum/ears.dm b/mods/species/neoavians/datum/ears.dm new file mode 100644 index 000000000000..d26961d4cf29 --- /dev/null +++ b/mods/species/neoavians/datum/ears.dm @@ -0,0 +1,24 @@ +/decl/sprite_accessory/ears/avian + name = "Avian Ears" + icon_state = "ears" + icon = 'mods/species/neoavians/icons/ears.dmi' + uid = "acc_ears_avian" + mask_to_bodypart = FALSE + species_allowed = list(/decl/species/neoavian::uid) + +/decl/sprite_accessory/ears/avian/raptor_add + name = "Avian Ears, Additive" + icon_state = "ears-add" + color_blend = ICON_ADD + uid = "acc_ears_avian_add" + +/decl/sprite_accessory/ears/avian/large + name = "Avian Large Ears" + icon_state = "ears-large" + uid = "acc_ears_avian_large" + +/decl/sprite_accessory/ears/avian/large/add + name = "Avian Large Ears, Additive" + color_blend = ICON_ADD + icon_state = "ears-large-add" + uid = "acc_ears_avian_large_add" diff --git a/mods/species/neoavians/datum/accessory.dm b/mods/species/neoavians/datum/hair.dm similarity index 61% rename from mods/species/neoavians/datum/accessory.dm rename to mods/species/neoavians/datum/hair.dm index 978711e67460..d8a76a49c9ea 100644 --- a/mods/species/neoavians/datum/accessory.dm +++ b/mods/species/neoavians/datum/hair.dm @@ -1,4 +1,3 @@ -//hair /decl/sprite_accessory/hair/avian name = "Avian Plumage" icon_state = "avian_default" @@ -53,11 +52,6 @@ color_blend = ICON_ADD uid = "acc_hair_avian_plumage_alt" -/decl/sprite_accessory/hair/avian/alt/ears - name = "Avian Ears" - icon_state = "avian_ears" - uid = "acc_hair_avian_ears_alt" - /decl/sprite_accessory/hair/avian/alt/excited name = "Avian Spiky Alt" icon_state = "avian_spiky_alt" @@ -93,11 +87,6 @@ icon_state = "avian_upright_alt" uid = "acc_hair_avian_upright_alt" -/decl/sprite_accessory/hair/avian/alt/mane_beardless - name = "Avian Large Ears" - icon_state = "avian_mane_beardless" - uid = "acc_hair_avian_large_ears" - /decl/sprite_accessory/hair/avian/alt/droopy name = "Avian Droopy" icon_state = "avian_droopy" @@ -117,53 +106,3 @@ name = "Avian Long way" icon_state = "avian_longway" uid = "acc_hair_avian_longway" - -//markings -/decl/sprite_accessory/marking/avian - name = "Beak (Head)" - icon_state = "beak" - body_parts = list(BP_HEAD) - icon = 'mods/species/neoavians/icons/markings.dmi' - species_allowed = list(/decl/species/neoavian::uid) - color_blend = ICON_MULTIPLY - uid = "acc_marking_avian_beak" - -/decl/sprite_accessory/marking/avian/avian - name = "Raptor Ears (Head)" - icon_state = "ears" - uid = "acc_marking_avian_raptorears" - -/decl/sprite_accessory/marking/avian/wing_feathers - name = "Wing Feathers (Left)" - body_parts = list(BP_L_HAND) - icon_state = "wing_feathers" - uid = "acc_marking_avian_wingfeathers_left" - -/decl/sprite_accessory/marking/avian/wing_feathers/right - name = "Wing Feathers (Right)" - body_parts = list(BP_R_HAND) - uid = "acc_marking_avian_wingfeathers_right" - -/decl/sprite_accessory/marking/avian/additive - name = "Beak, Additive (Head)" - icon_state = "beak-add" - color_blend = ICON_ADD - uid = "acc_marking_avian_beak_alt" - -/decl/sprite_accessory/marking/avian/resomi - name = "Raptor Ears, Additive (Head)" - icon_state = "ears-add" - color_blend = ICON_ADD - uid = "acc_marking_avian_raptorears_alt" - -/decl/sprite_accessory/marking/avian/wing_feathers/additive - name = "Wing Feathers, Additive (Left)" - icon_state = "wing_feathers-add" - color_blend = ICON_ADD - uid = "acc_marking_avian_wingfeathers_left_alt" - -/decl/sprite_accessory/marking/avian/wing_feathers/right/additive - name = "Wing Feathers, Additive (Right)" - icon_state = "wing_feathers-add" - color_blend = ICON_ADD - uid = "acc_marking_avian_wingfeathers_right_alt" diff --git a/mods/species/neoavians/datum/loadout.dm b/mods/species/neoavians/datum/loadout.dm index 608757094363..125b2de01b4b 100644 --- a/mods/species/neoavians/datum/loadout.dm +++ b/mods/species/neoavians/datum/loadout.dm @@ -32,3 +32,10 @@ loadout_flags = GEAR_HAS_COLOR_SELECTION slot = slot_shoes_str uid = "gear_shoes_avian" + +/decl/loadout_option/avian/hooded_cloak + name = "striped cloak, hooded" + path = /obj/item/clothing/suit/hooded_cloak/avian + uid = "gear_cloak_avian" + slot = slot_wear_suit_str + loadout_flags = GEAR_HAS_COLOR_SELECTION diff --git a/mods/species/neoavians/datum/markings.dm b/mods/species/neoavians/datum/markings.dm new file mode 100644 index 000000000000..232ca94ace93 --- /dev/null +++ b/mods/species/neoavians/datum/markings.dm @@ -0,0 +1,37 @@ +/decl/sprite_accessory/marking/avian + name = "Beak (Head)" + icon_state = "beak" + body_parts = list(BP_HEAD) + icon = 'mods/species/neoavians/icons/markings.dmi' + species_allowed = list(/decl/species/neoavian::uid) + color_blend = ICON_MULTIPLY + uid = "acc_marking_avian_beak" + +/decl/sprite_accessory/marking/avian/wing_feathers + name = "Wing Feathers (Left)" + body_parts = list(BP_L_HAND) + icon_state = "wing_feathers" + uid = "acc_marking_avian_wingfeathers_left" + +/decl/sprite_accessory/marking/avian/wing_feathers/right + name = "Wing Feathers (Right)" + body_parts = list(BP_R_HAND) + uid = "acc_marking_avian_wingfeathers_right" + +/decl/sprite_accessory/marking/avian/additive + name = "Beak, Additive (Head)" + icon_state = "beak-add" + color_blend = ICON_ADD + uid = "acc_marking_avian_beak_alt" + +/decl/sprite_accessory/marking/avian/wing_feathers/additive + name = "Wing Feathers, Additive (Left)" + icon_state = "wing_feathers-add" + color_blend = ICON_ADD + uid = "acc_marking_avian_wingfeathers_left_alt" + +/decl/sprite_accessory/marking/avian/wing_feathers/right/additive + name = "Wing Feathers, Additive (Right)" + icon_state = "wing_feathers-add" + color_blend = ICON_ADD + uid = "acc_marking_avian_wingfeathers_right_alt" diff --git a/mods/species/neoavians/datum/species_bodytypes.dm b/mods/species/neoavians/datum/species_bodytypes.dm index a83f74e9b60c..c5898d11020e 100644 --- a/mods/species/neoavians/datum/species_bodytypes.dm +++ b/mods/species/neoavians/datum/species_bodytypes.dm @@ -8,22 +8,17 @@ mob_size = /decl/bodytype/avian::mob_size eye_icon = /decl/bodytype/avian::eye_icon nail_noun = /decl/bodytype/avian::nail_noun - override_limb_types = list(BP_TAIL = /obj/item/organ/external/tail/avian) // lists cannot use initial() uid = "bodytype_prosthetic_avian" - var/tail = "tail_avian" - var/tail_icon = 'mods/species/neoavians/icons/tail_synthetic.dmi' - var/tail_blend = ICON_OVERLAY - var/tail_hair - var/tail_hair_blend - var/tail_states /decl/bodytype/prosthetic/avian/raptor name = "synthetic raptor" icon_base = 'mods/species/neoavians/icons/body_synthetic_raptor.dmi' - tail = "tail_raptor" - tail_hair = "over" - tail_hair_blend = ICON_MULTIPLY uid = "bodytype_prosthetic_raptor" + default_sprite_accessories = list( + SAC_HAIR = list(/decl/sprite_accessory/hair/avian/spiky = list(SAM_COLOR = "#252525")), + SAC_EARS = list(/decl/sprite_accessory/ears/avian = list(SAM_COLOR = "#252525")), + SAC_TAIL = list(/decl/sprite_accessory/tail/avian = list(SAM_COLOR = "#252525")) + ) /decl/bodytype/avian name = "avian" @@ -44,7 +39,8 @@ BP_R_FOOT = /obj/item/organ/external/foot/right/avian, BP_L_HAND = /obj/item/organ/external/hand/clawed, BP_R_HAND = /obj/item/organ/external/hand/right/clawed, - BP_HEAD = /obj/item/organ/external/head/sharp_bite + BP_HEAD = /obj/item/organ/external/head/sharp_bite, + BP_TAIL = /obj/item/organ/external/tail/avian ) has_organ = list( BP_STOMACH = /obj/item/organ/internal/stomach, @@ -55,7 +51,6 @@ BP_BRAIN = /obj/item/organ/internal/brain, BP_EYES = /obj/item/organ/internal/eyes ) - override_limb_types = list(BP_TAIL = /obj/item/organ/external/tail/avian) default_sprite_accessories = list( SAC_HAIR = list(/decl/sprite_accessory/hair/avian = list(SAM_COLOR = "#252525")), SAC_MARKINGS = list(/decl/sprite_accessory/marking/avian = list(SAM_COLOR = "#454545")) @@ -67,37 +62,26 @@ ) uid = "bodytype_avian" - var/tail = "tail_avian" - var/tail_icon = 'mods/species/neoavians/icons/tail.dmi' - var/tail_blend = ICON_MULTIPLY - var/tail_hair - var/tail_hair_blend - var/tail_animation_states - /decl/bodytype/avian/raptor name = "raptor" icon_base = 'mods/species/neoavians/icons/body_raptor.dmi' - tail_icon = 'mods/species/neoavians/icons/tail.dmi' - tail = "tail_raptor" - tail_hair = "over" - tail_hair_blend = ICON_MULTIPLY uid = "bodytype_avian_raptor" + default_sprite_accessories = list( + SAC_HAIR = list(/decl/sprite_accessory/hair/avian = list(SAM_COLOR = "#252525")), + SAC_EARS = list(/decl/sprite_accessory/ears/avian = list(SAM_COLOR = "#252525")), + SAC_TAIL = list(/decl/sprite_accessory/tail/avian = list(SAM_COLOR = "#252525")) + ) /decl/bodytype/avian/additive name = "avian, additive" icon_base = 'mods/species/neoavians/icons/body_add.dmi' health_hud_intensity = 3 limb_blend = ICON_ADD - tail_blend = ICON_ADD - tail = "tail_avian_add" uid = "bodytype_avian_additive" /decl/bodytype/avian/additive/raptor name = "raptor, additive" icon_base = 'mods/species/neoavians/icons/body_raptor_add.dmi' - tail = "tail_raptor_add" - tail_hair = "over" - tail_hair_blend = ICON_ADD uid = "bodytype_avian_additive_raptor" /decl/bodytype/avian/Initialize() @@ -185,51 +169,6 @@ var/static/unarmed_attack = GET_DECL(/decl/natural_attack/stomp/weak) return unarmed_attack -/obj/item/organ/external/tail/avian/get_tail() - if(istype(bodytype, /decl/bodytype/avian)) - var/decl/bodytype/avian/bird_bod = bodytype - return bird_bod.tail - // This is gross but a broader rework would take some thinking. - if(istype(bodytype, /decl/bodytype/prosthetic/avian)) - var/decl/bodytype/prosthetic/avian/bird_bod = bodytype - return bird_bod.tail - -/obj/item/organ/external/tail/avian/get_tail_icon() - if(istype(bodytype, /decl/bodytype/avian)) - var/decl/bodytype/avian/bird_bod = bodytype - return bird_bod.tail_icon - if(istype(bodytype, /decl/bodytype/prosthetic/avian)) - var/decl/bodytype/prosthetic/avian/bird_bod = bodytype - return bird_bod.tail_icon - -/obj/item/organ/external/tail/avian/get_tail_animation_states() - if(istype(bodytype, /decl/bodytype/avian)) - var/decl/bodytype/avian/bird_bod = bodytype - return bird_bod.tail_animation_states - if(istype(bodytype, /decl/bodytype/prosthetic/avian)) - var/decl/bodytype/prosthetic/avian/bird_bod = bodytype - return bird_bod.tail_states - -/obj/item/organ/external/tail/avian/get_tail_blend() - if(istype(bodytype, /decl/bodytype/avian)) - var/decl/bodytype/avian/bird_bod = bodytype - return bird_bod.tail_blend - if(istype(bodytype, /decl/bodytype/prosthetic/avian)) - var/decl/bodytype/prosthetic/avian/bird_bod = bodytype - return bird_bod.tail_blend - -/obj/item/organ/external/tail/avian/get_tail_hair() - if(istype(bodytype, /decl/bodytype/avian)) - var/decl/bodytype/avian/bird_bod = bodytype - return bird_bod.tail_hair - if(istype(bodytype, /decl/bodytype/prosthetic/avian)) - var/decl/bodytype/prosthetic/avian/bird_bod = bodytype - return bird_bod.tail_hair - -/obj/item/organ/external/tail/avian/get_tail_hair_blend() - if(istype(bodytype, /decl/bodytype/avian)) - var/decl/bodytype/avian/bird_bod = bodytype - return bird_bod.tail_hair_blend - if(istype(bodytype, /decl/bodytype/prosthetic/avian)) - var/decl/bodytype/prosthetic/avian/bird_bod = bodytype - return bird_bod.tail_hair_blend +/obj/item/organ/external/tail/avian + tail_icon = 'mods/species/neoavians/icons/tail.dmi' + tail_blend = ICON_MULTIPLY diff --git a/mods/species/neoavians/datum/tails.dm b/mods/species/neoavians/datum/tails.dm new file mode 100644 index 000000000000..b7dfa6953d00 --- /dev/null +++ b/mods/species/neoavians/datum/tails.dm @@ -0,0 +1,20 @@ +/decl/sprite_accessory/tail/avian + name = "Raptor Tail" + icon = 'mods/species/neoavians/icons/tail.dmi' + icon_state = "tail_raptor" + uid = "acc_tail_avian" + accessory_metadata_types = list(SAM_COLOR, SAM_COLOR_INNER) + species_allowed = list(/decl/species/neoavian::uid) + +/decl/sprite_accessory/tail/avian/add + name = "Avian Tail, Additive" + icon_state = "tail_avian_add" + uid = "acc_tail_avian_add" + color_blend = ICON_ADD + accessory_metadata_types = list(SAM_COLOR) + +/decl/sprite_accessory/tail/avian/raptor_add + name = "Raptor Tail, Additive" + uid = "acc_tail_avian_raptor_add" + icon_state = "tail_raptor_add" + color_blend = ICON_ADD diff --git a/mods/species/neoavians/icons/body_raptor.dmi b/mods/species/neoavians/icons/body_raptor.dmi index ad66f40abda8..8906e2a4ea2e 100644 Binary files a/mods/species/neoavians/icons/body_raptor.dmi and b/mods/species/neoavians/icons/body_raptor.dmi differ diff --git a/mods/species/neoavians/icons/body_raptor_add.dmi b/mods/species/neoavians/icons/body_raptor_add.dmi index 4f1e9c20d687..7a2b91c115b6 100644 Binary files a/mods/species/neoavians/icons/body_raptor_add.dmi and b/mods/species/neoavians/icons/body_raptor_add.dmi differ diff --git a/mods/species/neoavians/icons/body_synthetic_raptor.dmi b/mods/species/neoavians/icons/body_synthetic_raptor.dmi index 5685f2613dbe..10659c8c7870 100644 Binary files a/mods/species/neoavians/icons/body_synthetic_raptor.dmi and b/mods/species/neoavians/icons/body_synthetic_raptor.dmi differ diff --git a/mods/species/neoavians/icons/clothing/accessory/cloak.dmi b/mods/species/neoavians/icons/clothing/accessory/cloak.dmi index a9c8664d8e7c..3ff9c8f6c2f8 100644 Binary files a/mods/species/neoavians/icons/clothing/accessory/cloak.dmi and b/mods/species/neoavians/icons/clothing/accessory/cloak.dmi differ diff --git a/mods/species/neoavians/icons/clothing/accessory/cloak_hooded.dmi b/mods/species/neoavians/icons/clothing/accessory/cloak_hooded.dmi new file mode 100644 index 000000000000..f19114b211a3 Binary files /dev/null and b/mods/species/neoavians/icons/clothing/accessory/cloak_hooded.dmi differ diff --git a/mods/species/neoavians/icons/clothing/head/cloak_hood.dmi b/mods/species/neoavians/icons/clothing/head/cloak_hood.dmi new file mode 100644 index 000000000000..eb63cd76f236 Binary files /dev/null and b/mods/species/neoavians/icons/clothing/head/cloak_hood.dmi differ diff --git a/mods/species/neoavians/icons/ears.dmi b/mods/species/neoavians/icons/ears.dmi new file mode 100644 index 000000000000..876e831defa2 Binary files /dev/null and b/mods/species/neoavians/icons/ears.dmi differ diff --git a/mods/species/neoavians/icons/hair.dmi b/mods/species/neoavians/icons/hair.dmi index df5a5f7010aa..cfc280f1e8aa 100644 Binary files a/mods/species/neoavians/icons/hair.dmi and b/mods/species/neoavians/icons/hair.dmi differ diff --git a/mods/species/neoavians/icons/markings.dmi b/mods/species/neoavians/icons/markings.dmi index 1615c45bf748..a7307dc368d3 100644 Binary files a/mods/species/neoavians/icons/markings.dmi and b/mods/species/neoavians/icons/markings.dmi differ diff --git a/mods/species/neoavians/icons/tail.dmi b/mods/species/neoavians/icons/tail.dmi index 1edfc99e00a0..abe28e59b43b 100644 Binary files a/mods/species/neoavians/icons/tail.dmi and b/mods/species/neoavians/icons/tail.dmi differ diff --git a/mods/species/skrell/gear/gear_suit.dm b/mods/species/skrell/gear/gear_suit.dm index f9b5947f421d..adcf99b37f3b 100644 --- a/mods/species/skrell/gear/gear_suit.dm +++ b/mods/species/skrell/gear/gear_suit.dm @@ -6,7 +6,7 @@ /obj/item/rcd, /obj/item/tool, /obj/item/t_scanner, - /obj/item/ore, + /obj/item/ore_satchel, /obj/item/tank ) armor = list( diff --git a/mods/species/tajaran/datum/species_bodytypes.dm b/mods/species/tajaran/datum/species_bodytypes.dm index 60d933c5ab83..3a0eef87ae26 100644 --- a/mods/species/tajaran/datum/species_bodytypes.dm +++ b/mods/species/tajaran/datum/species_bodytypes.dm @@ -72,6 +72,6 @@ return ..() /obj/item/organ/external/tail/cat - tail_icon = 'mods/species/tajaran/icons/tail.dmi' + tail_icon = 'mods/species/tajaran/icons/tail.dmi' tail_blend = ICON_MULTIPLY tail_animation_states = 1 diff --git a/mods/species/unathi/datum/species.dm b/mods/species/unathi/datum/species.dm index 02b118b2e68a..58f34e552448 100644 --- a/mods/species/unathi/datum/species.dm +++ b/mods/species/unathi/datum/species.dm @@ -102,6 +102,6 @@ LAZYDISTINCTADD(available_background_info[/decl/background_category/heritage], /decl/background_detail/heritage/unathi) LAZYSET(default_background_info, /decl/background_category/heritage, /decl/background_detail/heritage/unathi) -/decl/species/unathi/equip_survival_gear(var/mob/living/human/H) - ..() - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), slot_shoes_str) +/decl/species/unathi/equip_survival_gear(mob/living/wearer, box_type = /obj/item/box/survival) + . = ..() + wearer.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(wearer), slot_shoes_str) diff --git a/mods/species/unathi/datum/species_bodytypes.dm b/mods/species/unathi/datum/species_bodytypes.dm index cad31a0824b4..985523ed2b3b 100644 --- a/mods/species/unathi/datum/species_bodytypes.dm +++ b/mods/species/unathi/datum/species_bodytypes.dm @@ -83,7 +83,7 @@ uid = "bodytype_unathi_masc" /obj/item/organ/external/tail/unathi - tail_icon = 'mods/species/unathi/icons/tail.dmi' + tail_icon = 'mods/species/unathi/icons/tail.dmi' tail_animation_states = 9 /obj/item/organ/external/tail/unathi/get_natural_attacks() diff --git a/mods/species/utility_frames/species.dm b/mods/species/utility_frames/species.dm index 367ddeccad7f..5de54ba13040 100644 --- a/mods/species/utility_frames/species.dm +++ b/mods/species/utility_frames/species.dm @@ -36,7 +36,8 @@ /decl/pronouns/neuter ) available_background_info = list( - /decl/background_category/heritage = list(/decl/background_detail/heritage/synthetic) + /decl/background_category/citizenship = list(/decl/background_detail/citizenship/synthetic), + /decl/background_category/heritage = list(/decl/background_detail/heritage/synthetic) ) exertion_effect_chance = 10 diff --git a/mods/species/vox/datum/outfits.dm b/mods/species/vox/datum/outfits.dm index 564db2796e4d..f5ab5f436f0a 100644 --- a/mods/species/vox/datum/outfits.dm +++ b/mods/species/vox/datum/outfits.dm @@ -1,20 +1,40 @@ -/decl/outfit/vox_raider - name = "Job - Vox Raider" - l_ear = /obj/item/radio/headset/raider - shoes = /obj/item/clothing/shoes/magboots/vox - gloves = /obj/item/clothing/gloves/vox - mask = /obj/item/clothing/mask/gas/swat/vox - back = /obj/item/tank/nitrogen - uniform = /obj/item/clothing/suit/robe/vox - glasses = /obj/item/clothing/glasses/thermal - holster = /obj/item/clothing/webbing/holster/armpit - suit_store = /obj/item/flashlight - hands = list(/obj/item/gun/launcher/alien/spikethrower) - id_type = /obj/item/card/id/syndicate +/decl/outfit/vox + abstract_type = /decl/outfit/vox + mask = /obj/item/clothing/mask/gas/swat/vox + back = /obj/item/tank/nitrogen + uniform = /obj/item/clothing/suit/robe/vox + shoes = /obj/item/clothing/shoes/magboots/vox + gloves = /obj/item/clothing/gloves/vox + var/list/glasses_types + var/list/holster_types -/decl/outfit/vox_raider/equip_outfit(mob/living/wearer, assignment, equip_adjustments, datum/job/job, datum/mil_rank/rank) +/decl/outfit/vox/equip_outfit(mob/living/wearer, assignment, equip_adjustments, datum/job/job, datum/mil_rank/rank) uniform = pick(/obj/item/clothing/suit/robe/vox, /obj/item/clothing/pants/vox) - glasses = pick(/obj/item/clothing/glasses/thermal, /obj/item/clothing/glasses/thermal/plain/eyepatch, /obj/item/clothing/glasses/thermal/plain/monocle) - holster = pick(/obj/item/clothing/webbing/holster/armpit, /obj/item/clothing/webbing/holster/waist, /obj/item/clothing/webbing/holster/hip) + if(length(glasses_types)) + glasses = pick(glasses_types) + if(length(holster_types)) + holster = pick(holster_types) . = ..() - wearer.set_internals(locate(/obj/item/tank) in wearer.contents) \ No newline at end of file + wearer.set_internals(locate(/obj/item/tank) in wearer.contents) + +/decl/outfit/vox/survivor + name = "Job - Vox Survivor" + +/decl/outfit/vox/raider + name = "Job - Vox Raider" + l_ear = /obj/item/radio/headset/raider + glasses = /obj/item/clothing/glasses/thermal + holster = /obj/item/clothing/webbing/holster/armpit + suit_store = /obj/item/flashlight + hands = list(/obj/item/gun/launcher/alien/spikethrower) + id_type = /obj/item/card/id/syndicate + holster_types = list( + /obj/item/clothing/webbing/holster/armpit, + /obj/item/clothing/webbing/holster/waist, + /obj/item/clothing/webbing/holster/hip + ) + glasses_types = list( + /obj/item/clothing/glasses/thermal, + /obj/item/clothing/glasses/thermal/plain/eyepatch, + /obj/item/clothing/glasses/thermal/plain/monocle + ) diff --git a/mods/species/vox/datum/species.dm b/mods/species/vox/datum/species.dm index e5f6c03faf25..07b969c16b04 100644 --- a/mods/species/vox/datum/species.dm +++ b/mods/species/vox/datum/species.dm @@ -59,7 +59,7 @@ speech_sounds = list('sound/voice/shriek1.ogg') speech_chance = 20 - preview_outfit = /decl/outfit/vox_raider + preview_outfit = /decl/outfit/vox/raider gluttonous = GLUT_TINY|GLUT_ITEM_NORMAL stomach_capacity = 12 @@ -129,19 +129,19 @@ /decl/emote/exertion/synthetic/creak ) -/decl/species/vox/equip_survival_gear(var/mob/living/human/H) - H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/vox(H), slot_wear_mask_str) - var/obj/item/backpack/backpack = H.get_equipped_item(slot_back_str) +/decl/species/vox/equip_survival_gear(mob/living/wearer, box_type = /obj/item/box/survival) + wearer.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/vox(wearer), slot_wear_mask_str) + var/obj/item/backpack/backpack = wearer.get_equipped_item(slot_back_str) if(istype(backpack)) - H.equip_to_slot_or_del(new /obj/item/box/vox(backpack), slot_in_backpack_str) - var/obj/item/tank/nitrogen/tank = new(H) - H.equip_to_slot_or_del(tank, BP_R_HAND) + wearer.equip_to_slot_or_del(new /obj/item/box/vox(backpack), slot_in_backpack_str) + var/obj/item/tank/nitrogen/tank = new(wearer) + wearer.equip_to_slot_or_del(tank, BP_R_HAND) if(tank) - H.set_internals(tank) + wearer.set_internals(tank) else - H.equip_to_slot_or_del(new /obj/item/tank/nitrogen(H), slot_back_str) - H.equip_to_slot_or_del(new /obj/item/box/vox(H), BP_R_HAND) - H.set_internals(backpack) + wearer.equip_to_slot_or_del(new /obj/item/tank/nitrogen(wearer), slot_back_str) + wearer.equip_to_slot_or_del(new /obj/item/box/vox(wearer), BP_R_HAND) + wearer.set_internals(backpack) // Ideally this would all be on bodytype, but pressure is handled per-mob currently. var/global/list/vox_current_pressure_toggle = list() diff --git a/mods/~compatibility/patches/circuits.dm b/mods/~compatibility/patches/circuits.dm index 457c3b4f855f..afa15ae3f36c 100644 --- a/mods/~compatibility/patches/circuits.dm +++ b/mods/~compatibility/patches/circuits.dm @@ -5,4 +5,8 @@ // Add circuit items to dungeon loot. #ifdef MODPACK_DUNGEON_LOOT #include "circuits/loot_circuits.dm" +#endif +// Add augment assembly for circuits. +#ifdef CONTENT_PACK_AUGMENTS +#include "circuits/augment_circuits.dm" #endif \ No newline at end of file diff --git a/mods/content/integrated_electronics/assemblies/circuit_augment.dm b/mods/~compatibility/patches/circuits/augment_circuits.dm similarity index 90% rename from mods/content/integrated_electronics/assemblies/circuit_augment.dm rename to mods/~compatibility/patches/circuits/augment_circuits.dm index cc3b522aa6ee..842d89dad232 100644 --- a/mods/content/integrated_electronics/assemblies/circuit_augment.dm +++ b/mods/~compatibility/patches/circuits/augment_circuits.dm @@ -35,4 +35,8 @@ holding.canremove = 0 playsound(loc, 'sound/items/Crowbar.ogg', 50, 1) return TRUE - return ..() \ No newline at end of file + return ..() + +// And add a fabricator design +/datum/fabricator_recipe/robotics/augment/circuit + path = /obj/item/organ/internal/augment/active/simple/circuit \ No newline at end of file diff --git a/mods/~compatibility/patches/exploration.dm b/mods/~compatibility/patches/exploration.dm new file mode 100644 index 000000000000..670b41a1ced8 --- /dev/null +++ b/mods/~compatibility/patches/exploration.dm @@ -0,0 +1,4 @@ +// Add explorer loot pile. +#ifdef MODPACK_DUNGEON_LOOT +#include "exploration/loot_explo.dm" +#endif diff --git a/mods/~compatibility/patches/exploration/loot_explo.dm b/mods/~compatibility/patches/exploration/loot_explo.dm new file mode 100644 index 000000000000..ad8b17b17581 --- /dev/null +++ b/mods/~compatibility/patches/exploration/loot_explo.dm @@ -0,0 +1,25 @@ +/obj/structure/loot_pile/exosuit/explorer + name = "exploration exosuit wreckage" + desc = "The ruins of some unfortunate exploration exosuit. Perhaps something is salvageable." + +/obj/structure/loot_pile/exosuit/explorer/get_common_loot() + var/static/list/common_loot = list( + /obj/item/mech_component/manipulators/powerloader/exploration, + /obj/item/mech_component/chassis/pod/exploration, + /obj/item/mech_equipment/light + ) + return common_loot + +/obj/structure/loot_pile/exosuit/explorer/get_uncommon_loot() + var/static/list/uncommon_loot = list( + /obj/item/mech_component/propulsion/tracks/exploration, + /obj/item/mech_equipment/clamp + ) + return uncommon_loot + +/obj/structure/loot_pile/exosuit/explorer/get_rare_loot() + var/static/list/rare_loot = list( + /obj/item/mech_component/sensors/light/painted, + /obj/item/mech_equipment/mounted_system/taser/plasma + ) + return rare_loot diff --git a/mods/~compatibility/patches/heist_vox.dm b/mods/~compatibility/patches/heist_vox.dm index 6b145ba4a502..1447a09a9516 100644 --- a/mods/~compatibility/patches/heist_vox.dm +++ b/mods/~compatibility/patches/heist_vox.dm @@ -1,6 +1,6 @@ /decl/special_role/raider/Initialize() . = ..() - LAZYSET(outfits_per_species, /decl/species/vox::uid, /decl/outfit/vox_raider) + LAZYSET(outfits_per_species, /decl/species/vox::uid, /decl/outfit/vox/raider) // The following mirror is ~special~. /obj/structure/mirror/raider @@ -22,7 +22,7 @@ if(choice != "Yes") return TRUE - var/decl/outfit/outfit = GET_DECL(/decl/outfit/vox_raider) + var/decl/outfit/outfit = GET_DECL(/decl/outfit/vox/raider) var/mob/living/human/vox/vox = new(get_turf(src), /decl/species/vox::uid) outfit.equip_outfit(vox) if(user.mind) diff --git a/mods/~compatibility/patches/supermatter.dm b/mods/~compatibility/patches/supermatter.dm index f0de98b9c7c1..be367240f965 100644 --- a/mods/~compatibility/patches/supermatter.dm +++ b/mods/~compatibility/patches/supermatter.dm @@ -13,4 +13,8 @@ // Add supermatter grenades to the mercenary uplink #ifdef GAMEMODE_PACK_MERCENARY #include "supermatter/sm_mercenary.dm" +#endif +// Add extra response team denial reasons +#ifdef MODPACK_RESPONSE_TEAM +#include "supermatter/sm_ert.dm" #endif \ No newline at end of file diff --git a/mods/~compatibility/patches/supermatter/sm_ert.dm b/mods/~compatibility/patches/supermatter/sm_ert.dm new file mode 100644 index 000000000000..8017985ae13b --- /dev/null +++ b/mods/~compatibility/patches/supermatter/sm_ert.dm @@ -0,0 +1,7 @@ +/decl/game_mode/possible_ert_disabled_reasons() + var/static/sm_injected = FALSE + if(sm_injected) + return ..() + sm_injected = TRUE + . = ..() + . += "supermatter dust" // this intentionally mutates the static list in the parent call \ No newline at end of file diff --git a/mods/~compatibility/~compatibility.dm b/mods/~compatibility/~compatibility.dm index 81c4f3403d12..ea29d9c2d73e 100644 --- a/mods/~compatibility/~compatibility.dm +++ b/mods/~compatibility/~compatibility.dm @@ -37,4 +37,8 @@ #ifdef CONTENT_PACK_VENTCRAWL #include "patches/ventcrawl.dm" -#endif \ No newline at end of file +#endif + +#ifdef CONTENT_PACK_EXPLORATION +#include "patches/exploration.dm" +#endif diff --git a/nebula.dme b/nebula.dme index e36aa4201a37..1315351f3503 100644 --- a/nebula.dme +++ b/nebula.dme @@ -80,6 +80,7 @@ #include "code\__defines\organs.dm" #include "code\__defines\overmap.dm" #include "code\__defines\paperwork.dm" +#include "code\__defines\persistence.dm" #include "code\__defines\power.dm" #include "code\__defines\proc_presets.dm" #include "code\__defines\qdel.dm" @@ -87,6 +88,7 @@ #include "code\__defines\reactions.dm" #include "code\__defines\reagent_data_fields.dm" #include "code\__defines\research.dm" +#include "code\__defines\serde.dm" #include "code\__defines\shields.dm" #include "code\__defines\shuttle.dm" #include "code\__defines\skills.dm" @@ -151,6 +153,7 @@ #include "code\_helpers\profiling.dm" #include "code\_helpers\radio.dm" #include "code\_helpers\sanitize_values.dm" +#include "code\_helpers\serde.dm" #include "code\_helpers\storage.dm" #include "code\_helpers\text.dm" #include "code\_helpers\time.dm" @@ -202,7 +205,6 @@ #include "code\_onclick\hud\screen\screen_action_button.dm" #include "code\_onclick\hud\screen\screen_ai_button.dm" #include "code\_onclick\hud\screen\screen_attack_selector.dm" -#include "code\_onclick\hud\screen\screen_cataloguer.dm" #include "code\_onclick\hud\screen\screen_cinematic.dm" #include "code\_onclick\hud\screen\screen_click_catcher.dm" #include "code\_onclick\hud\screen\screen_constructs.dm" @@ -296,6 +298,7 @@ #include "code\controllers\subsystems\overlays.dm" #include "code\controllers\subsystems\overmap.dm" #include "code\controllers\subsystems\pathfinding.dm" +#include "code\controllers\subsystems\persistence.dm" #include "code\controllers\subsystems\radiation.dm" #include "code\controllers\subsystems\shuttle.dm" #include "code\controllers\subsystems\skybox.dm" @@ -324,7 +327,6 @@ #include "code\controllers\subsystems\initialization\materials.dm" #include "code\controllers\subsystems\initialization\misc.dm" #include "code\controllers\subsystems\initialization\modpacks.dm" -#include "code\controllers\subsystems\initialization\persistence.dm" #include "code\controllers\subsystems\initialization\robots.dm" #include "code\controllers\subsystems\initialization\secrets.dm" #include "code\controllers\subsystems\initialization\webhooks.dm" @@ -352,6 +354,7 @@ #include "code\datums\category.dm" #include "code\datums\cinematic.dm" #include "code\datums\datum.dm" +#include "code\datums\datum_serde.dm" #include "code\datums\footsteps.dm" #include "code\datums\hierarchy.dm" #include "code\datums\local_network.dm" @@ -397,6 +400,7 @@ #include "code\datums\composite_sounds\fire_sounds.dm" #include "code\datums\composite_sounds\loom.dm" #include "code\datums\composite_sounds\machinery_sounds.dm" +#include "code\datums\composite_sounds\vehicle_engine.dm" #include "code\datums\config\_config.dm" #include "code\datums\config\_config_categories.dm" #include "code\datums\config\config_enum.dm" @@ -756,10 +760,11 @@ #include "code\game\atoms_movable_grabs.dm" #include "code\game\atoms_movable_interactions.dm" #include "code\game\atoms_movable_overlay.dm" +#include "code\game\atoms_movable_serde.dm" +#include "code\game\atoms_serde.dm" #include "code\game\atoms_temperature.dm" #include "code\game\base_turf.dm" #include "code\game\movietitles.dm" -#include "code\game\response_team.dm" #include "code\game\sound.dm" #include "code\game\world.dm" #include "code\game\world_topic_commands.dm" @@ -775,13 +780,13 @@ #include "code\game\antagonist\antagonist_place.dm" #include "code\game\antagonist\antagonist_print.dm" #include "code\game\antagonist\antagonist_update.dm" -#include "code\game\antagonist\outsider\ert.dm" #include "code\game\area\area_abstract.dm" #include "code\game\area\area_access.dm" #include "code\game\area\area_fishing.dm" #include "code\game\area\area_power.dm" #include "code\game\area\area_space.dm" #include "code\game\area\areas.dm" +#include "code\game\area\areas_serde.dm" #include "code\game\gamemodes\game_mode.dm" #include "code\game\gamemodes\game_mode_latespawn.dm" #include "code\game\gamemodes\calamity\calamity.dm" @@ -807,6 +812,7 @@ #include "code\game\jobs\_access_defs.dm" #include "code\game\jobs\access.dm" #include "code\game\jobs\access_datum.dm" +#include "code\game\jobs\alt_titles.dm" #include "code\game\jobs\server_whitelist.dm" #include "code\game\jobs\job\_job.dm" #include "code\game\machinery\ai_slipper.dm" @@ -1030,7 +1036,6 @@ #include "code\game\objects\effects\landmarks.dm" #include "code\game\objects\effects\landmarks_endgame.dm" #include "code\game\objects\effects\landmarks_latejoin.dm" -#include "code\game\objects\effects\mines.dm" #include "code\game\objects\effects\misc.dm" #include "code\game\objects\effects\overlays.dm" #include "code\game\objects\effects\portals.dm" @@ -1046,12 +1051,30 @@ #include "code\game\objects\effects\decals\cleanable.dm" #include "code\game\objects\effects\decals\crayon.dm" #include "code\game\objects\effects\decals\decal.dm" +#include "code\game\objects\effects\decals\decal_serde.dm" #include "code\game\objects\effects\decals\misc.dm" #include "code\game\objects\effects\decals\warning_stripes.dm" #include "code\game\objects\effects\decals\Cleanable\humans.dm" #include "code\game\objects\effects\decals\Cleanable\misc.dm" #include "code\game\objects\effects\decals\Cleanable\robots.dm" #include "code\game\objects\effects\decals\Cleanable\tracks.dm" +#include "code\game\objects\effects\map_effect\_map_effect.dm" +#include "code\game\objects\effects\map_effect\interval\_interval.dm" +#include "code\game\objects\effects\map_effect\interval\effect_emitter.dm" +#include "code\game\objects\effects\map_effect\interval\screen_shaker.dm" +#include "code\game\objects\effects\map_effect\interval\sound_emitter.dm" +#include "code\game\objects\effects\mines\_mine.dm" +#include "code\game\objects\effects\mines\_mine_payload.dm" +#include "code\game\objects\effects\mines\mine_assembly.dm" +#include "code\game\objects\effects\mines\mine_emp.dm" +#include "code\game\objects\effects\mines\mine_frag.dm" +#include "code\game\objects\effects\mines\mine_incendiary.dm" +#include "code\game\objects\effects\mines\mine_kick.dm" +#include "code\game\objects\effects\mines\mine_napalm.dm" +#include "code\game\objects\effects\mines\mine_radiation.dm" +#include "code\game\objects\effects\mines\mine_sleeping.dm" +#include "code\game\objects\effects\mines\mine_stun.dm" +#include "code\game\objects\effects\mines\mine_training.dm" #include "code\game\objects\effects\spawners\bombspawner.dm" #include "code\game\objects\effects\spawners\gibspawner.dm" #include "code\game\objects\items\__item.dm" @@ -1063,6 +1086,7 @@ #include "code\game\objects\items\_item_materials.dm" #include "code\game\objects\items\_item_melting.dm" #include "code\game\objects\items\_item_reagents.dm" +#include "code\game\objects\items\_item_serde.dm" #include "code\game\objects\items\_item_sharpness.dm" #include "code\game\objects\items\blackout.dm" #include "code\game\objects\items\blueprints.dm" @@ -1076,6 +1100,7 @@ #include "code\game\objects\items\documents.dm" #include "code\game\objects\items\fleece.dm" #include "code\game\objects\items\glassjar.dm" +#include "code\game\objects\items\helping_hands.dm" #include "code\game\objects\items\holosign_creator.dm" #include "code\game\objects\items\horseshoe.dm" #include "code\game\objects\items\hourglass.dm" @@ -1097,6 +1122,7 @@ #include "code\game\objects\items\toys.dm" #include "code\game\objects\items\training_dummy.dm" #include "code\game\objects\items\trash.dm" +#include "code\game\objects\items\trash_serde.dm" #include "code\game\objects\items\umbrella.dm" #include "code\game\objects\items\waterskin.dm" #include "code\game\objects\items\artifice\chain.dm" @@ -1113,6 +1139,7 @@ #include "code\game\objects\items\blades\swords_one_handed.dm" #include "code\game\objects\items\blades\swords_two_handed.dm" #include "code\game\objects\items\books\_book.dm" +#include "code\game\objects\items\books\_book_serde.dm" #include "code\game\objects\items\books\fluff\_fluff.dm" #include "code\game\objects\items\books\fluff\science.dm" #include "code\game\objects\items\books\manuals\_manual.dm" @@ -1237,6 +1264,7 @@ #include "code\game\objects\items\stacks\nanopaste.dm" #include "code\game\objects\items\stacks\rods.dm" #include "code\game\objects\items\stacks\stack.dm" +#include "code\game\objects\items\stacks\stack_serde.dm" #include "code\game\objects\items\stacks\telecrystal.dm" #include "code\game\objects\items\stacks\medical\_medical.dm" #include "code\game\objects\items\stacks\medical\medical_bandage.dm" @@ -1381,6 +1409,7 @@ #include "code\game\objects\random\subtypes\multi.dm" #include "code\game\objects\random\subtypes\paperwork.dm" #include "code\game\objects\random\subtypes\plants.dm" +#include "code\game\objects\random\subtypes\salvage.dm" #include "code\game\objects\random\subtypes\suits.dm" #include "code\game\objects\random\subtypes\tech.dm" #include "code\game\objects\random\subtypes\tools.dm" @@ -1392,6 +1421,7 @@ #include "code\game\objects\structures\_structure_interactions.dm" #include "code\game\objects\structures\_structure_lock.dm" #include "code\game\objects\structures\_structure_materials.dm" +#include "code\game\objects\structures\_structure_serde.dm" #include "code\game\objects\structures\ai_decoy.dm" #include "code\game\objects\structures\armor_stand.dm" #include "code\game\objects\structures\barricade.dm" @@ -1401,6 +1431,7 @@ #include "code\game\objects\structures\bookcase.dm" #include "code\game\objects\structures\catwalk.dm" #include "code\game\objects\structures\charge_pylon.dm" +#include "code\game\objects\structures\cliffs.dm" #include "code\game\objects\structures\coathanger.dm" #include "code\game\objects\structures\compost.dm" #include "code\game\objects\structures\crematorium.dm" @@ -1452,11 +1483,9 @@ #include "code\game\objects\structures\safe.dm" #include "code\game\objects\structures\seaweed.dm" #include "code\game\objects\structures\showcase.dm" -#include "code\game\objects\structures\signs.dm" #include "code\game\objects\structures\skele_stand.dm" #include "code\game\objects\structures\snowman.dm" #include "code\game\objects\structures\sofa.dm" -#include "code\game\objects\structures\stasis_cage.dm" #include "code\game\objects\structures\structure_reagents.dm" #include "code\game\objects\structures\tables.dm" #include "code\game\objects\structures\tank_dispenser.dm" @@ -1527,13 +1556,19 @@ #include "code\game\objects\structures\flora\bush.dm" #include "code\game\objects\structures\flora\grass.dm" #include "code\game\objects\structures\flora\plant.dm" +#include "code\game\objects\structures\flora\plant_serde.dm" #include "code\game\objects\structures\flora\potted.dm" #include "code\game\objects\structures\flora\stump.dm" #include "code\game\objects\structures\flora\tree.dm" +#include "code\game\objects\structures\signs\_signs.dm" #include "code\game\objects\structures\signs\bar_signs.dm" -#include "code\game\objects\structures\signs\department_signs.dm" +#include "code\game\objects\structures\signs\decks.dm" +#include "code\game\objects\structures\signs\departments.dm" #include "code\game\objects\structures\signs\diploma.dm" -#include "code\game\objects\structures\signs\direction_signs.dm" +#include "code\game\objects\structures\signs\directions.dm" +#include "code\game\objects\structures\signs\flags.dm" +#include "code\game\objects\structures\signs\hangar.dm" +#include "code\game\objects\structures\signs\levels.dm" #include "code\game\objects\structures\signs\maps.dm" #include "code\game\objects\structures\signs\paintings.dm" #include "code\game\objects\structures\signs\plaques.dm" @@ -1552,6 +1587,7 @@ #include "code\game\turfs\turf_material.dm" #include "code\game\turfs\turf_navigation.dm" #include "code\game\turfs\turf_ramps.dm" +#include "code\game\turfs\turf_serde.dm" #include "code\game\turfs\flooring\_flooring.dm" #include "code\game\turfs\flooring\_flooring_decals.dm" #include "code\game\turfs\flooring\flooring_carpet.dm" @@ -1579,6 +1615,7 @@ #include "code\game\turfs\floors\floor_icon.dm" #include "code\game\turfs\floors\floor_layers.dm" #include "code\game\turfs\floors\floor_materials.dm" +#include "code\game\turfs\floors\floor_serde.dm" #include "code\game\turfs\floors\subtypes\floor_carpet.dm" #include "code\game\turfs\floors\subtypes\floor_circuit.dm" #include "code\game\turfs\floors\subtypes\floor_concrete.dm" @@ -1616,6 +1653,7 @@ #include "code\game\turfs\walls\wall_natural_ramps.dm" #include "code\game\turfs\walls\wall_natural_subtypes.dm" #include "code\game\turfs\walls\wall_natural_xenoarch.dm" +#include "code\game\turfs\walls\wall_serde.dm" #include "code\game\turfs\walls\wall_types.dm" #include "code\game\turfs\walls\wall_wattle.dm" #include "code\game\verbs\byond_membership.dm" @@ -1774,25 +1812,11 @@ #include "code\modules\atmospherics\components\unary\heat_source.dm" #include "code\modules\atmospherics\components\unary\outlet_injector.dm" #include "code\modules\atmospherics\components\unary\tank.dm" +#include "code\modules\atmospherics\components\unary\temperature_base.dm" #include "code\modules\atmospherics\components\unary\thermal_plate.dm" #include "code\modules\atmospherics\components\unary\unary_base.dm" #include "code\modules\atmospherics\components\unary\vent_pump.dm" #include "code\modules\atmospherics\components\unary\vent_scrubber.dm" -#include "code\modules\augment\active.dm" -#include "code\modules\augment\augment.dm" -#include "code\modules\augment\helping_hands.dm" -#include "code\modules\augment\simple.dm" -#include "code\modules\augment\active\armblades.dm" -#include "code\modules\augment\active\cyberbrain.dm" -#include "code\modules\augment\active\polytool.dm" -#include "code\modules\augment\active\tool\engineering.dm" -#include "code\modules\augment\active\tool\surgical.dm" -#include "code\modules\augment\passive\armor.dm" -#include "code\modules\augment\passive\boost.dm" -#include "code\modules\augment\passive\nanoaura.dm" -#include "code\modules\augment\passive\boost\muscle.dm" -#include "code\modules\augment\passive\boost\reflex.dm" -#include "code\modules\augment\passive\boost\shooting.dm" #include "code\modules\backgrounds\_background.dm" #include "code\modules\backgrounds\background_categories.dm" #include "code\modules\backgrounds\citizenship\_citizenship.dm" @@ -1898,11 +1922,9 @@ #include "code\modules\client\preference_setup\global\05_settings.dm" #include "code\modules\client\preference_setup\global\preferences.dm" #include "code\modules\client\preference_setup\global\prefixes.dm" -#include "code\modules\client\preference_setup\loadout\_defines.dm" #include "code\modules\client\preference_setup\loadout\gear_tweaks.dm" #include "code\modules\client\preference_setup\loadout\loadout.dm" #include "code\modules\client\preference_setup\loadout\lists\accessories.dm" -#include "code\modules\client\preference_setup\loadout\lists\augmentations.dm" #include "code\modules\client\preference_setup\loadout\lists\clothing.dm" #include "code\modules\client\preference_setup\loadout\lists\earwear.dm" #include "code\modules\client\preference_setup\loadout\lists\eyegear.dm" @@ -2149,7 +2171,6 @@ #include "code\modules\clothing\webbing\pouches.dm" #include "code\modules\clothing\webbing\vest.dm" #include "code\modules\codex\codex_atom.dm" -#include "code\modules\codex\codex_cataloguer.dm" #include "code\modules\codex\codex_client.dm" #include "code\modules\codex\codex_implant.dm" #include "code\modules\codex\codex_mob.dm" @@ -2402,7 +2423,6 @@ #include "code\modules\fabrication\designs\protolathe\designs_weapons.dm" #include "code\modules\fabrication\designs\replicator\designs_food.dm" #include "code\modules\fabrication\designs\robotics\_designs_robotics.dm" -#include "code\modules\fabrication\designs\robotics\designs_augments.dm" #include "code\modules\fabrication\designs\robotics\designs_misc.dm" #include "code\modules\fabrication\designs\robotics\designs_organs.dm" #include "code\modules\fabrication\designs\robotics\designs_prosthetics.dm" @@ -2689,6 +2709,7 @@ #include "code\modules\materials\material_coatings.dm" #include "code\modules\materials\material_data.dm" #include "code\modules\materials\material_debris.dm" +#include "code\modules\materials\material_debris_fragment.dm" #include "code\modules\materials\material_display.dm" #include "code\modules\materials\material_drying.dm" #include "code\modules\materials\material_gas_overlay.dm" @@ -2806,6 +2827,7 @@ #include "code\modules\mob\mob_layering.dm" #include "code\modules\mob\mob_movement.dm" #include "code\modules\mob\mob_organs.dm" +#include "code\modules\mob\mob_serde.dm" #include "code\modules\mob\mob_snapshot.dm" #include "code\modules\mob\mob_status.dm" #include "code\modules\mob\mob_temperature.dm" @@ -2918,6 +2940,7 @@ #include "code\modules\mob\living\human\whisper.dm" #include "code\modules\mob\living\human\descriptors\_descriptors.dm" #include "code\modules\mob\living\human\descriptors\descriptors_age.dm" +#include "code\modules\mob\living\human\descriptors\descriptors_body_length.dm" #include "code\modules\mob\living\human\descriptors\descriptors_generic.dm" #include "code\modules\mob\living\maneuvers\_maneuver.dm" #include "code\modules\mob\living\maneuvers\maneuver_leap.dm" @@ -2996,6 +3019,7 @@ #include "code\modules\mob\living\simple_animal\natural_weapons.dm" #include "code\modules\mob\living\simple_animal\simple_animal_codex.dm" #include "code\modules\mob\living\simple_animal\simple_animal_damage.dm" +#include "code\modules\mob\living\simple_animal\simple_animal_serde.dm" #include "code\modules\mob\living\simple_animal\alien\alien.dm" #include "code\modules\mob\living\simple_animal\aquatic\_aquatic.dm" #include "code\modules\mob\living\simple_animal\aquatic\_aquatic_hostile.dm" @@ -3048,9 +3072,9 @@ #include "code\modules\mob\living\simple_animal\hostile\giant_spiders\spitter.dm" #include "code\modules\mob\living\simple_animal\hostile\hivebots\_hivebot.dm" #include "code\modules\mob\living\simple_animal\hostile\hivebots\megabot.dm" -#include "code\modules\mob\living\simple_animal\hostile\hivebots\range.dm" -#include "code\modules\mob\living\simple_animal\hostile\hivebots\rapid.dm" -#include "code\modules\mob\living\simple_animal\hostile\hivebots\strong.dm" +#include "code\modules\mob\living\simple_animal\hostile\hivebots\melee\_melee.dm" +#include "code\modules\mob\living\simple_animal\hostile\hivebots\melee\armored.dm" +#include "code\modules\mob\living\simple_animal\hostile\hivebots\ranged\_ranged.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\exoplanet.dm" @@ -3111,7 +3135,6 @@ #include "code\modules\mob_modifiers\definitions\modifiers_cloaked.dm" #include "code\modules\mob_modifiers\definitions\modifiers_light.dm" #include "code\modules\mob_modifiers\definitions\modifiers_mech_shields.dm" -#include "code\modules\mob_modifiers\definitions\modifiers_nanoswarm.dm" #include "code\modules\mob_modifiers\definitions\modifiers_object.dm" #include "code\modules\mob_modifiers\definitions\modifiers_prone.dm" #include "code\modules\mob_modifiers\definitions\modifiers_regeneration.dm" @@ -3250,6 +3273,9 @@ #include "code\modules\multiz\hoist.dm" #include "code\modules\multiz\ladder.dm" #include "code\modules\multiz\level_data.dm" +#include "code\modules\multiz\level_persistence_handler.dm" +#include "code\modules\multiz\level_persistence_handler_json.dm" +#include "code\modules\multiz\level_persistence_serialization.dm" #include "code\modules\multiz\map_data.dm" #include "code\modules\multiz\mobile_ladder.dm" #include "code\modules\multiz\movement.dm" @@ -3366,6 +3392,8 @@ #include "code\modules\overmap\ships\machines\fusion_thruster.dm" #include "code\modules\overmap\ships\machines\gas_thruster.dm" #include "code\modules\overmap\ships\machines\ion_thruster.dm" +#include "code\modules\paperwork\_paper.dm" +#include "code\modules\paperwork\_paper_serde.dm" #include "code\modules\paperwork\adminpaper.dm" #include "code\modules\paperwork\bodyscan_paper.dm" #include "code\modules\paperwork\carbonpaper.dm" @@ -3375,7 +3403,6 @@ #include "code\modules\paperwork\folders.dm" #include "code\modules\paperwork\handlabeler.dm" #include "code\modules\paperwork\helpers.dm" -#include "code\modules\paperwork\paper.dm" #include "code\modules\paperwork\paper_bundle.dm" #include "code\modules\paperwork\paper_plane.dm" #include "code\modules\paperwork\paper_sticky.dm" @@ -3531,6 +3558,7 @@ #include "code\modules\projectiles\projectile\bullets.dm" #include "code\modules\projectiles\projectile\change.dm" #include "code\modules\projectiles\projectile\energy.dm" +#include "code\modules\projectiles\projectile\fireball.dm" #include "code\modules\projectiles\projectile\force.dm" #include "code\modules\projectiles\projectile\magnetic.dm" #include "code\modules\projectiles\projectile\pellets.dm" @@ -3730,6 +3758,19 @@ #include "code\modules\research\design_database.dm" #include "code\modules\research\design_database_analyzer.dm" #include "code\modules\research\research_fields.dm" +#include "code\modules\salvage\salvage.dm" +#include "code\modules\salvage\salvage_ballistic.dm" +#include "code\modules\salvage\salvage_energy.dm" +#include "code\modules\salvage\salvage_launcher.dm" +#include "code\modules\salvage\salvage_magnetic.dm" +#include "code\modules\salvage\salvage_repair_option.dm" +#include "code\modules\salvage\salvage_repair_requirement.dm" +#include "code\modules\salvage\structure.dm" +#include "code\modules\salvage\structure_computer.dm" +#include "code\modules\salvage\structure_console.dm" +#include "code\modules\salvage\structure_machine.dm" +#include "code\modules\salvage\structure_misc.dm" +#include "code\modules\salvage\structure_terminal.dm" #include "code\modules\scanners\_scanner.dm" #include "code\modules\scanners\breath.dm" #include "code\modules\scanners\gas.dm" @@ -3748,6 +3789,7 @@ #include "code\modules\security_levels\_security_level.dm" #include "code\modules\security_levels\alarm_appearance.dm" #include "code\modules\security_levels\keycard_authentication.dm" +#include "code\modules\security_levels\keycard_authentication_events.dm" #include "code\modules\security_levels\security_levels.dm" #include "code\modules\security_levels\security_state.dm" #include "code\modules\shield_generators\floor_diffuser.dm" @@ -3864,6 +3906,7 @@ #include "code\modules\synthesized_instruments\real_instruments\Trumpet\trumpet.dm" #include "code\modules\synthesized_instruments\real_instruments\Violin\violin.dm" #include "code\modules\tools\tool.dm" +#include "code\modules\tools\tool_serde.dm" #include "code\modules\tools\archetypes\_tool_defines.dm" #include "code\modules\tools\archetypes\tool_archetype.dm" #include "code\modules\tools\archetypes\tool_archetype_definition_pen.dm" @@ -3896,7 +3939,10 @@ #include "code\modules\turbolift\turbolift_turfs.dm" #include "code\modules\vehicles\bike.dm" #include "code\modules\vehicles\cargo_train.dm" +#include "code\modules\vehicles\cargo_trolley.dm" #include "code\modules\vehicles\engine.dm" +#include "code\modules\vehicles\quad_bike.dm" +#include "code\modules\vehicles\quad_trailer.dm" #include "code\modules\vehicles\train.dm" #include "code\modules\vehicles\vehicle.dm" #include "code\modules\weather\_weather.dm" @@ -4051,7 +4097,6 @@ #include "maps\__map_modpack_compatibility.dm" #include "maps\__map_names.dm" #include "maps\_map_include.dm" -#include "maps\antag_spawn\ert\ert.dm" #include "maps\away_sites_testing\away_sites_testing_define.dm" #include "maps\example\example_define.dm" #include "maps\exodus\exodus_define.dm" diff --git a/sound/effects/break_stone.ogg b/sound/effects/break_stone.ogg new file mode 100644 index 000000000000..711fd50d482c Binary files /dev/null and b/sound/effects/break_stone.ogg differ diff --git a/sound/machines/vehicle/engine_end.ogg b/sound/machines/vehicle/engine_end.ogg new file mode 100644 index 000000000000..7f3c179ad802 Binary files /dev/null and b/sound/machines/vehicle/engine_end.ogg differ diff --git a/sound/machines/vehicle/engine_mid.ogg b/sound/machines/vehicle/engine_mid.ogg new file mode 100644 index 000000000000..cf7173c8e81d Binary files /dev/null and b/sound/machines/vehicle/engine_mid.ogg differ diff --git a/sound/machines/vehicle/engine_start.ogg b/sound/machines/vehicle/engine_start.ogg new file mode 100644 index 000000000000..e4b5ac744b9d Binary files /dev/null and b/sound/machines/vehicle/engine_start.ogg differ diff --git a/sound/machines/vehicle/ignition.ogg b/sound/machines/vehicle/ignition.ogg new file mode 100644 index 000000000000..cf0718c6e3b3 Binary files /dev/null and b/sound/machines/vehicle/ignition.ogg differ diff --git a/sound/weapons/Gunshot_phase.ogg b/sound/weapons/Gunshot_phase.ogg new file mode 100644 index 000000000000..abfe12c0d9cd Binary files /dev/null and b/sound/weapons/Gunshot_phase.ogg differ diff --git a/sound/weapons/dartgun.ogg b/sound/weapons/dartgun.ogg new file mode 100644 index 000000000000..99a5465d161f Binary files /dev/null and b/sound/weapons/dartgun.ogg differ diff --git a/sound/weapons/ionrifle.ogg b/sound/weapons/ionrifle.ogg new file mode 100644 index 000000000000..b808068e55f7 Binary files /dev/null and b/sound/weapons/ionrifle.ogg differ diff --git a/sound/weapons/sear.ogg b/sound/weapons/sear.ogg new file mode 100644 index 000000000000..c6d5f6846c9e Binary files /dev/null and b/sound/weapons/sear.ogg differ diff --git a/sound/weapons/searwall.ogg b/sound/weapons/searwall.ogg new file mode 100644 index 000000000000..6a29326f24b8 Binary files /dev/null and b/sound/weapons/searwall.ogg differ diff --git a/sound/weapons/zapbang.ogg b/sound/weapons/zapbang.ogg new file mode 100644 index 000000000000..4e14e30a11bd Binary files /dev/null and b/sound/weapons/zapbang.ogg differ diff --git a/test/annotate_od.sh b/test/annotate_od.sh old mode 100644 new mode 100755 diff --git a/test/compile-od-all-modpacks.sh b/test/compile-od-all-modpacks.sh new file mode 100755 index 000000000000..36ebe40fdc58 --- /dev/null +++ b/test/compile-od-all-modpacks.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +set -o pipefail + +dmepath="" + +for var; do + if [[ $var != -* && $var == *.dme ]]; then + dmepath=$(echo $var | sed -r 's/.{4}$//') + break + fi +done + +if [[ $dmepath == "" ]]; then + echo "No .dme file specified, aborting." + exit 1 +fi + +if [[ -a $dmepath.m.dme ]]; then + rm $dmepath.m.dme +fi + +cp $dmepath.dme $dmepath.m.dme +if [[ $? != 0 ]]; then + echo "Failed to make modified dme, aborting." + exit 2 +fi + +sed -i '1s/^/#define MAP_OVERRIDE\n/' $dmepath.m.dme +sed -i 's!#include "maps\\_map_include.dm"!#include "maps\\modpack_testing\\modpack_testing.dm"!' $dmepath.m.dme +failed=0 +./DMCompiler_linux-x64/DMCompiler "$dmepath.m.dme" --define=UNIT_TEST --suppress-unimplemented --skip-anything-typecheck --version=${BYOND_MAJOR}.${BYOND_MINOR} | bash test/annotate_od.sh +# Check the return value +if [[ $? -ne 0 ]]; then + failed=1 +fi +rm $dmepath.m.dme +if [[ $failed -eq 1 ]]; then + echo "Modpack testing map failed to pass validation." + exit 1 +fi \ No newline at end of file diff --git a/test/lint-all-modpacks.sh b/test/lint-all-modpacks.sh index eb477c205f61..e04ad04182e6 100755 --- a/test/lint-all-modpacks.sh +++ b/test/lint-all-modpacks.sh @@ -3,7 +3,6 @@ set -o pipefail dmepath="" -retval=1 for var; do if [[ $var != -* && $var == *.dme ]]; then @@ -28,33 +27,16 @@ if [[ $? != 0 ]]; then fi sed -i '1s/^/#define MAP_OVERRIDE\n/' $dmepath.m.dme -sed -i 's!#include "maps\\_map_include.dm"!#include "maps\\example\\example.dm"!' $dmepath.m.dme - -# find feature DMEs to include. they're located in mods/**/**/*.dme. for example: mods/content/fantasy/_fantasy.dme -# it is guaranteed that each feature folder has only one DME file, so wildcard matching will work -dme_features=$(find mods -mindepth 3 -type f -name '*.dme') -# some features may be bare DM files, located at mods/**/*.dm. for example: mods/content/mundane.dm -# it is guaranteed that all bare DM files directly in mods/content, mods/gamemodes, and similar folders are able to be included as features -dm_features=$(find mods -mindepth 2 -maxdepth 2 -name '*.dm' ! -path 'mods/~*/*' ! -path 'mods/*/~.dm' ) -# do some sort of for loop to insert a feature at the end of the DME prior to #include "mods\~compatibility\~compatibility.dm" -# run ~/dreamchecker -e $dmepath.m.dme 2>&1 | tee -a ${GITHUB_WORKSPACE}/output-annotations.txt -# then remove the inserted feature after so the next step in the loop can test a new feature +sed -i 's!#include "maps\\_map_include.dm"!#include "maps\\modpack_testing\\modpack_testing.dm"!' $dmepath.m.dme failed=0 -for feature in $dme_features $dm_features; do - echo "Testing feature: $feature" - # Include the feature - sed -i "/#include \"mods\\\\_modpack.dm\"/a#include \"$feature\"" "$dmepath.m.dme" - # Run the linter, only doing parsing to catch undefined vars and keywords - ~/dreamchecker -e "$dmepath.m.dme" --parse-only 2>&1 | tee -a "${GITHUB_WORKSPACE}/output-annotations.txt" - # Check the return value - if [[ $? -ne 0 ]]; then - failed=1 - fi - # Remove the feature include - sed -i "\\:#include \"$feature\":d" "$dmepath.m.dme" -done +# Run the linter, doing a full linting rather than just parsing +~/dreamchecker -e "$dmepath.m.dme" 2>&1 | tee -a "${GITHUB_WORKSPACE}/output-annotations.txt" +# Check the return value +if [[ $? -ne 0 ]]; then + failed=1 +fi rm $dmepath.m.dme if [[ $failed -eq 1 ]]; then - echo "One or more modpacks failed to pass solo validation." + echo "Modpack testing map failed to pass validation." exit 1 fi \ No newline at end of file diff --git a/test/lint-each-modpack.sh b/test/lint-each-modpack.sh new file mode 100755 index 000000000000..b28ada773bff --- /dev/null +++ b/test/lint-each-modpack.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +set -o pipefail + +dmepath="" + +for var; do + if [[ $var != -* && $var == *.dme ]]; then + dmepath=$(echo $var | sed -r 's/.{4}$//') + break + fi +done + +if [[ $dmepath == "" ]]; then + echo "No .dme file specified, aborting." + exit 1 +fi + +if [[ -a $dmepath.m.dme ]]; then + rm $dmepath.m.dme +fi + +cp $dmepath.dme $dmepath.m.dme +if [[ $? != 0 ]]; then + echo "Failed to make modified dme, aborting." + exit 2 +fi + +sed -i '1s/^/#define MAP_OVERRIDE\n/' $dmepath.m.dme +sed -i 's!#include "maps\\_map_include.dm"!#include "maps\\example\\example.dm"!' $dmepath.m.dme + +# find feature DMEs to include. they're located in mods/**/**/*.dme. for example: mods/content/fantasy/_fantasy.dme +# it is guaranteed that each feature folder has only one DME file, so wildcard matching will work +dme_features=$(find mods -mindepth 3 -type f -name '*.dme') +# some features may be bare DM files, located at mods/**/*.dm. for example: mods/content/mundane.dm +# it is guaranteed that all bare DM files directly in mods/content, mods/gamemodes, and similar folders are able to be included as features +dm_features=$(find mods -mindepth 2 -maxdepth 2 -name '*.dm' ! -path 'mods/~*/*' ! -path 'mods/*/~.dm' ) +# do some sort of for loop to insert a feature at the end of the DME prior to #include "mods\~compatibility\~compatibility.dm" +# run ~/dreamchecker -e $dmepath.m.dme 2>&1 | tee -a ${GITHUB_WORKSPACE}/output-annotations.txt +# then remove the inserted feature after so the next step in the loop can test a new feature +failed=0 +for feature in $dme_features $dm_features; do + echo "Testing feature: $feature" + # Include the feature + sed -i "/#include \"mods\\\\_modpack.dm\"/a#include \"$feature\"" "$dmepath.m.dme" + # Run the linter, only doing parsing to catch undefined vars and keywords + ~/dreamchecker -e "$dmepath.m.dme" --parse-only 2>&1 | tee -a "${GITHUB_WORKSPACE}/output-annotations.txt" + # Check the return value + if [[ $? -ne 0 ]]; then + failed=1 + fi + # Remove the feature include + sed -i "\\:#include \"$feature\":d" "$dmepath.m.dme" +done +rm $dmepath.m.dme +if [[ $failed -eq 1 ]]; then + echo "One or more modpacks failed to pass solo validation." + exit 1 +fi \ No newline at end of file diff --git a/tools/map_migrations/5281_freezer_heater.txt b/tools/map_migrations/5281_freezer_heater.txt new file mode 100644 index 000000000000..ea420f32ec3b --- /dev/null +++ b/tools/map_migrations/5281_freezer_heater.txt @@ -0,0 +1,2 @@ +/obj/machinery/atmospherics/unary/heater/@SUBTYPES : /obj/machinery/atmospherics/unary/temperature/heater{@OLD} +/obj/machinery/atmospherics/unary/freezer/@SUBTYPES : /obj/machinery/atmospherics/unary/temperature/freezer{@OLD} \ No newline at end of file diff --git a/tools/map_migrations/5320_power_debug.txt b/tools/map_migrations/5320_power_debug.txt new file mode 100644 index 000000000000..b5a1d738e495 --- /dev/null +++ b/tools/map_migrations/5320_power_debug.txt @@ -0,0 +1,2 @@ +/obj/machinery/power/debug_items/@SUBTYPES : /obj/machinery/debug_items/@SUBTYPES{@OLD} +/obj/machinery/power/ascent_reactor/@SUBTYPES : /obj/machinery/ascent_reactor/@SUBTYPES{@OLD} \ No newline at end of file