diff --git a/.gitattributes b/.gitattributes index d5d501e8560..81a615f78eb 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 100cba7cc7d..b1e9a685237 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 155ccb22f4a..c43e7cc2f26 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 ffd20f690f9..eaa502407f5 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 c29a24a164d..9935dab6b5c 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 787488e127f..b157b96d5f8 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 4d5a639708f..06d5c69bf2a 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 75c9ac6ef8c..77b1dae70dd 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 00000000000..bbd7ffbbf0c --- /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 00000000000..a1e84e3fe30 --- /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 127f3b2d114..87f4b099d0f 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 a819fa7c527..c77cb035a88 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 80c11924a91..7a21627335d 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 78cd73fe25b..741c433285d 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 bab80ae12be..fec9867b5f0 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 942d9d9807f..e94e11f5d01 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 296a04974a0..abac165dce8 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 00000000000..9d50f3fae34 --- /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 90210911f1e..14f373cda70 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 d5178a09093..ed43cf149ee 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 51398a81da0..727108ce080 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 e6599e5ac2b..70cc620cb20 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 9e65b258399..5a64f04f09a 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 5d4f34b32da..851fdf1d868 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 90849e6e193..b1d41c05376 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 baf065bd0fd..68e2940bcb4 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 b05b4ed11f8..e0248091c4e 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 c1119f8ca85..00000000000 --- 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 a166bb41242..886d58c7a9c 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 bcf8a692236..a03221cd513 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 00000000000..79a994a2da0 --- /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 77f0d4083cb..6871edd8db1 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 56f789d94d4..31d3e3aec9a 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 32383c214d2..209d504cffa 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 5c097d91ba2..f431bbc339c 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 00000000000..824d0044134 --- /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 330f993c1a0..a2e0c6720a7 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 881b69bb4e3..30e00f5a657 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 00000000000..cafee1161d6 --- /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 53985ff5862..ab28746a558 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 308bacd0a86..138e0c1d509 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 086db0ce899..3567e31a848 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 1f900ae9f4d..a2a30b9c5ba 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 67b759c682c..ecde7fc83db 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 fa1c3f9627b..a3ce8adfbdd 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 5effd811586..1e2bb91ce31 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 a84f10736b7..2cfa4bd9ff3 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 3803830d60f..b94d406d1e6 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 09c51789dc3..0ec89477c5e 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 5505d65f464..93c9197bbc0 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 319d1dee408..7544b406d35 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 16096eac4eb..e9c52c310f2 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 51d038d968b..4176f4556e3 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 b81dfc3ec32..43545577485 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 df05b830089..60a982a81ac 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 dfd47b4726a..1aa23a3c086 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 5c2f8c44471..b8cfc6e116d 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 00000000000..d2b8270006c --- /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 28a1dc589ec..e698467579d 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 94c8602eefe..628b5880042 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 00000000000..e47ee52da8f --- /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 00000000000..e8212228384 --- /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 584abf220f3..4ac6e53ff06 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 20cd9d5a395..4acbcff8b72 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 00000000000..cea4dafc837 --- /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 0fe1d526de0..2b278bc95e4 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 fd685f5d9a8..f08f9ac4984 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 17dfd16d50d..3b1aec75600 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 7145a9762c5..67718407629 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 3e0327cc03a..81ea2fc50ed 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 99d2fd9681f..af1f1e4423b 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 b1042ee71ea..f001ba66263 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 90f5287221f..6d1b954bab9 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 01cc88b50e3..feebd929384 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 d7b2823e0d7..11cc7b85239 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 421c415cb24..48a282d2fdd 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 634c454322a..9b96d693941 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 036251e4ea3..aa0e391befb 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 e7f36574fa5..7c4a8c775cb 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 00000000000..d598f384dcc --- /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 8c2df8b9f8b..833ddaf2b0a 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 52c615b4dae..1bf090caf26 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 00000000000..50cad4fa38c --- /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 00000000000..ba898570c76 --- /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 00000000000..0daf63388e0 --- /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 00000000000..724bb7a256d --- /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 00000000000..058434882b3 --- /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 ac59e8322d4..00000000000 --- 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 00000000000..a377a9d8286 --- /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 00000000000..99139be493d --- /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 00000000000..6a40fa0ae30 --- /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 00000000000..5f8a54ad12f --- /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 00000000000..de11402ca5a --- /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 00000000000..f8b8a49ab2d --- /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 00000000000..ab38a2a2fb3 --- /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 00000000000..6bceace3f0e --- /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 00000000000..08b00d2a659 --- /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 00000000000..b6d5ad9a0de --- /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 00000000000..26b5704b304 --- /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 00000000000..13b23f31945 --- /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 b56d38275f1..c5efa1c7e91 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 f548bcbfada..d9164b23c5e 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 00000000000..c4aba6ca932 --- /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 8d9e6d3d06b..2d2e4cddf96 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 00000000000..25251936e1d --- /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 1b123c42034..bd5fcd533af 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 bf81f6b51ee..6ac21c7a784 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 d592062c431..fa29f431665 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 ead756343e3..475a5e51590 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 eab3b5bd6e0..6ce348ceaaa 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 906824d52bc..2c5d3b10570 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 cbe87ec2d29..f2a7ee2d6d4 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 00000000000..53702ee7672 --- /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 00000000000..ad791d2e8d3 --- /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 305f721f173..34a48cec61b 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 fc19d7e17dd..20fec64b1b0 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 92faaca3265..8e18a94c2a5 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 33db97ea49c..b91369cf235 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 2b3e166a2d2..4ac41d06e88 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 fa38daee600..3dc1ec39db8 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 0bb25ee3ac0..1d7cc0f7c27 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 01481422311..8a90eb63636 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 13c9162b87f..2995bcc8270 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 8524aff474c..a54cac010f9 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 27d600db80a..990d11c5a1a 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 03e86f8f5f4..d26977a5bf1 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 00000000000..298557addca --- /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 a1b1ab86e65..2bd7302535c 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 02b5aa65728..255e50a2bb1 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 9414c6c2d2b..14a526bda1d 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 be2c2102ae5..27239ec9127 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 00000000000..1f69c129422 --- /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 3f5b94ea071..8598d9db8d5 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 5bc36823d97..e22920c145a 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 00000000000..5745913052a --- /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 f8a63b95011..78be7fdb88b 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 6b85c90efb6..3d4ff0479f3 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 909a5fcc418..fa5524530c0 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 0ef9f5dfbce..e15b1fc302c 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 3feffa51de9..6f116ab4451 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 05eb791fb89..1ed302b304a 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 c9ab4bc5f72..2e828ed6e80 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 b3b305a9f94..06ba909c7b4 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 00000000000..26cfbedac5e --- /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 1ed4d581c72..1ff9947a566 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 cda6216899a..4830025c353 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 b04f59f9a15..776c5162398 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 0b1245e25d7..2241befe625 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 51396fea7bb..1d53cf2fa3a 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 00000000000..3fc6dadc59f --- /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 92fa661be71..0018ae5d6a8 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 1b331afc994..00000000000 --- 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 00000000000..c89ae69448f --- /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 00000000000..8fce965bc1a --- /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 00000000000..0ad300afe06 --- /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 00000000000..214b43bc650 --- /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 19d72c4e1ef..4a4257437d1 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 7165a62c8a0..2bcb946dec8 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 5474cb223cf..00000000000 --- 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 8fbd7f7f983..bca175038d2 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 3f0d24378d2..6aa5066cea2 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 a93566d69e3..f30adf01de7 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 50ca2d36ab7..b1847ad05ab 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 e5d6edd5f17..89ed297a1f7 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 dc53aaf4680..f51600fa91d 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 bdce5b2859e..e7f26e889be 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 128bdc34cbc..932289909a9 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 6a934d5c5eb..8632af3b386 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 13836a0a7da..94227641346 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 3a7c0843f65..5b4f13501ea 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 881da81bdcc..72362928c06 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 3982c3dab82..ece70741f5c 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 8968d6020a5..43057b4dc0c 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 2b799313b9b..0f684b9a5a4 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 8fb98f91fa6..24e1629b664 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 213deaca53b..5a7ff3459fd 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 00000000000..9e88c47f2a8 --- /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 7684009c32a..77bb38e13a4 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 60303ef1ee7..f52e6614450 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 ae735053580..9a73b28f4d1 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 b67e06bf1b7..bded9334f69 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 a0e1194fda7..e34bc367a3c 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 24c79df35f4..17404b65e48 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 33dc224c196..9777e814671 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 abbcfecc695..186c84cb264 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 c3b4c1a4ddf..74dd2d6c05e 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 0c4d7107c01..ff76d205ad2 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 33828543cd5..df7d8c2b68a 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 5c849feec9a..0dfa901cf98 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 fe248f68977..aafbb2bb193 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 00000000000..d8f7515f2b8 --- /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 1b82356696e..a380cfa054d 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 1d26dbb74a6..3706a99fcfa 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 28f4df68b14..1c529c70ab2 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 a71c306e041..f82b5d0be9a 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 4710e89e562..b8ba4f36922 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 c36d31b5bd8..76526f13e7a 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 976b793664f..dc072ac2519 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 00000000000..53d737fffa2 --- /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 ed23150edc8..c0052e2a67f 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 151a6da3771..f1726ee6ac0 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 52587d2a21d..d4d3e91d6e5 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 d9977490a91..4d7bc4f561e 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 19916f831e9..2c7947e4a9b 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 8ff55c421de..e39ed6b84fa 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 d588df659e2..606ac3bedc7 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 18ab66f0386..7f4eb8bb7eb 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 435bf70e9f1..c69833d1de1 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 00000000000..819c6a16155 --- /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 306de571064..00000000000 --- 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 d6c349aa1ed..e16cc9455ac 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 d121a81af01..cfeed30a29c 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 745de870f6b..fab17e290c6 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 d4b4a0c45a1..f41ebb0157e 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 fd6206eefa4..600043bdc11 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 d7156bf6bdc..6d6f47ff139 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 67bb7164983..691812c2301 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 6bfbe052376..59a3471aba4 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 c98f5c4d313..00000000000 --- 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 c7c50e1603f..b805d002e79 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 72760aca59c..b9990a778ba 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 071c2a139ca..660190b9bbb 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 ceb5eaaeac5..d8b2da85e07 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 f6980156009..952ab86c409 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 50adae6297c..eb812a3b3b0 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 b2bc5a1fc94..f08fdefeb3b 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 205a8af7997..21be4c2e731 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 afdbbdd6419..46d60d8112c 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 01cd25482a7..0cb84f4778a 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 1f42fce9e5c..e3bb8aadda7 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 589a7f0d79e..8b90dd6119a 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 d36aa54c14d..3812b8c9206 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 06c837cbe4a..e43c91fc845 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 fe48b0e24b0..609bc791eeb 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 74a7ace494e..6f6af449a48 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 3aaaba4e5f7..42e02944dd1 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 b34c74a2d43..ae70473b182 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 6509270555d..cc138ce2dd8 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 0aef22c0c0c..84ccc0d6b84 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 ff61c4c392f..715aee90cc2 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 4c7e8993f07..6bbd45733ce 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 3ff1832811c..aa76aafb403 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 96d91b2e784..4ffb83499d1 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 519938245c6..d88132261a9 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 39f8dc118d9..b32701c519b 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 5400a5e174c..fa1cb513fef 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 0686eee43d9..d0964b37e3a 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 eb92c1ef0f3..2fd82b73938 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 aa961aef563..dd732867b35 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 d8ece5c95cb..aaf9d2e8fc3 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 8dd93caf898..8e2449f56fc 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 47a48f0f5e9..7b3f2a33384 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 9d5fbc13b44..8e4b1179e84 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 d854110a206..02ae238c326 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 f31aa5eb279..960ad52b5ed 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 06e8126b16f..ff68fdb348e 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 f43ef762a6b..a49d409fba9 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 4b34c863ed9..60992f1fcb5 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 639b0a02387..ea6f99e9a91 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 5ebc210383e..84bccc89757 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 08d7fadca5c..5543d1166c1 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 8b27d9c6926..13e82ac10b7 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 71bedba3639..9f7dff60818 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 3e8c60a5d1a..d9d6608eac8 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 221ee935055..869920b3417 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 920e63862dc..ada5e9a86c7 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 f60b1158b6d..e8e94319782 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 0d6b27a9c82..366f0460834 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 3bb5692d776..d9a0ad5866d 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 00000000000..d34e944b6dc --- /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 75f373f981b..a4c0e5957bd 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 afd7fbfe51a..64017b9fdd6 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 4ac27861125..391166a8bb1 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 373af3a552e..f18d56ea2e0 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 6450fa1d8ba..420596abfc9 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 bd70d153e5e..fbad7d1b235 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 ab20aa117d1..cc4cb28c8c6 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 f4861ede315..c4a5015aa4d 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 b306780544b..a565d1e1aae 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 a296f441344..00c52094798 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 7c0e00da127..56a25031101 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 d1d64b56f8a..76378ffb374 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 4d3d35090dc..c4cc217a30e 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 ff9a8613746..52d3b884092 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 956ae903000..24bb4de3f4b 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 a870b6c2c26..e94c963c06f 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 a18bd6148c4..31761055946 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 9d2b5f06f82..a8773d85d50 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 e09c3548c82..0e76a273749 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 d1e76232d72..5aaaf39486a 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 bcc2945bf46..e6093860338 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 c8da4e16f14..de977e45c63 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 36f1ba370c4..b5508d6d221 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 ab53e5630e3..7b62adcc2ed 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 99e38a6baa0..cfaaab05eb0 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 ef8c318ade2..9b77858189e 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 d958928874e..6566f241e2f 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 ead786ab340..4dec711741c 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 00000000000..42d3f588fbe --- /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 00000000000..6556736ec9c --- /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 3d1ecb678aa..00000000000 --- 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 00000000000..800b7b0fe90 --- /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 6cdc6ffbd32..00000000000 --- 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 4105339c6bd..00000000000 --- 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 3e450fd82b2..97345aeccd7 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 c9590c54714..cdb5d4895cb 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 09567e7abf1..d8f8ad22527 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 72d3815596e..5139242d27e 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 4fe5386ebc3..32676d4ab40 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 aae33697bfb..cbe5c004b25 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 00000000000..377ffd04b6c --- /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 dadb2de1b99..286b28d1689 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 27ae2d426d9..360f26b7555 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 00000000000..d6bb36e8c97 --- /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 01398160314..c876a47857c 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 77ef427f3e4..0b7f6c091e9 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 ddb6d6023ea..00000000000 --- 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 1bc6fd711de..8450e7d86a2 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 0783c94d1fc..1ad6d0aceb0 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 6f167e4682b..b38d86878e2 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 292b7c9b0c8..b6167cf7419 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 00d213dd49d..7afe7441668 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 9522183087d..4ea853d3219 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 389f3e24923..e9d994279e3 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 00000000000..39337f5c246 --- /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 00000000000..bebf101a2b7 --- /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 00000000000..a72affa8ded --- /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 4240ed507b2..5461aee03b7 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 df31a9f6bdf..fb33c094c7c 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 c689d9c477d..61678c157ea 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 3d54995fc28..c7b1c8fe87e 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 6ca145d4336..2431326554d 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 b78c313b71c..d7047a14aa8 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 981048c93c8..656d6e533c6 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 fa5cbde8286..aeea2012542 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 5c98aeea901..52016ae3967 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 6f63a9edf78..96d9180a240 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 7ebe9b07788..68f0907ac83 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 e971f2d5a38..989d9f1be8e 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 a74d6a20a28..5ad561f38a0 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 29ae2a58e6c..e8dde83b7da 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 2a7e5b3fb64..03f019080ec 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 4d6f541b3f7..7ccc7550135 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 891382ad00a..f6988ec8a79 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 3978b1a4c58..221fa1200d5 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 4f2e43fbe4c..b855f1b207d 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 24f4d999c0a..93adc809767 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 034c88c3ae9..af411daf7cd 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 00000000000..8fbfe734b9b --- /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 185771010d8..b5f0fdc5e78 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 ab432d17a7b..07af39cfc8d 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 cb65ce6ca0c..6c6515ec6ba 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 9ec2db2954f..02daf6cc11c 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 08acd039c57..db43dff3f8a 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 839f0ed29a6..9b96bca8dff 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 9f877efeab0..249535eed99 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 8dd10f4806d..dab2fcfed35 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 0bc733ab2f0..3e30ab2c4ec 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 94749f01ded..a14cb7d7987 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 4b6f849ee0b..2eeee9e7a37 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 3ed7700fd6f..ffa26d4d0f2 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 576a53032a4..77d53d3fa7d 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 0339f9ce702..b6721a8aa9c 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 b18e5bd1681..7067ecc7974 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 dc923451622..d9e93ff2da1 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 dcb428ebdf8..35a6efc1207 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 ff6d5274e98..8a49627444c 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 6220dc117d2..6727118ba2f 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 f85c32b16eb..cd70fdbc44a 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 e76803494d6..29c458486a8 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 91c1085e432..13b739cf041 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 f7d2efa622b..7671a3a759c 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 cf4b659eb7e..a9af3a3c1e4 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 1e1e7f0cda2..2fc0015f266 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 4feed2fd06c..d36ec1783cc 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 8df181e9195..1c8b74eeb56 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 b8094057708..e94f5926a34 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 eb0cc1835e4..e08ffc8d1f0 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 00000000000..6221b8bf134 --- /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 a3c2e748d0b..e0b8ea2745f 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 c4a244c802a..5c19804a9c2 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 d648f6d70c3..d7ee296a8f1 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 a7e1f58c34f..6abad14dbe3 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 544b625a331..3e45c90f4fc 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 c6b536cc2e5..91b22c5c42e 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 15f6277f693..91667bc86fa 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 ad1d3c26c0e..10c390dfcd4 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 1c4f5c85190..d71b5f24b7e 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 629091fd109..961da084398 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 b4a6f089ba8..29994461235 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 d14189ec181..007ce32ffa6 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 cb305d78e23..f080406738f 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 6578646b9e2..b771084ef23 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 efe6040294c..e1b478c93fa 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 5d23dc4e523..95685fbc3db 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 fd688adbcd9..6a04df74b4e 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 35300fc4310..daee57f2381 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 e6d1634b844..afc9ccd606c 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 89c9ecc84b3..847d7996b24 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 d4d7e5a425b..dd072542c41 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 572561fc288..07d45320e23 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 bca089ca6fd..fd4593aee2b 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 0ed92151816..afc2c082b15 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 00000000000..ecf3196f883 --- /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 00000000000..904f6d0276c --- /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 00000000000..935a3467abb --- /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 00000000000..571f69b6a13 --- /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 00000000000..78b8bcf47ba --- /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 00000000000..f63d88afc61 --- /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 00000000000..fdf524bb61e --- /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 00000000000..1d71f206157 --- /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 00000000000..12b9f700eee --- /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 00000000000..7f31e5be6ad --- /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 00000000000..62fa97c6bbf --- /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 00000000000..e1515b69c93 --- /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 00000000000..6e2ef10be5c --- /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 2b9095692b2..9da785d77ce 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 3fc507c50de..b7afc80b7a6 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 eca9534b53c..cb91e3c505c 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 00000000000..076501f1b32 --- /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 661bab8cf89..4029741fcbd 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 f2003eb0129..5d56b4285d4 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 b9e342c4225..394c6abd75b 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 6c67caea5d2..17ab4381d16 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 d2cdcebc5f5..58e9b96498f 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 3d02bc64b08..25eafb7f5f3 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 311e416366e..031e7ac4079 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 c2fde5b895d..93bad99848d 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 1ed731ee454..4d25ab85a85 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 07dd1eb2d65..ce4bd511bd6 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 00000000000..f94e251715b --- /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 7522827dc86..49e6274920d 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 f828d2f4e1c..4fcb9ee46ce 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 00000000000..f41875d4cd9 --- /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 00000000000..3d01876bc16 --- /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 00000000000..a730ab54b65 --- /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 0cb43501e1e..59665c6b326 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 111eb94818e..6086aa1fab2 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 c56e354022c..c2d37385970 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 3111921fc19..1a0da34cc8b 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 3a139013982..4c1ebac20fd 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 279daba8a34..89814527ca6 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 20ba36dac7d..434712cb0f9 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 2a0cc80fdbb..9e334a8d147 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 72d20645b58..69f4266c6b4 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 0d0d7118d49..12467d8b13c 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 cef4aba20aa..c0c100bcd7c 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 6e4661c6884..9e0ead821a4 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 235786373a5..8e915de101a 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 4a934dba46d..00000000000 --- 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 00000000000..7b006dea424 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 0a3fee24ee2..ef68f54325e 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 4d6f7d87266..8a3891ef624 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 00000000000..acc4af64870 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 5502343f0d5..a43e0694162 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 00000000000..a3239f69fe2 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 00000000000..8b2dc22e3c4 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 00000000000..7e93f98b5ce 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 d7947b3d21b..f94a41218de 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 6561ca1e9d4..c830e2f9fbd 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 dff5a696fb4..00000000000 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 00000000000..af859e82b12 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 00000000000..32ab4636993 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 00000000000..ead79af5a6b 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 00000000000..421e47424ae 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 8f0a174d5c2..107bd192153 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 2636de48299..aeb3e439378 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 a42d14ecff5..dedf006c922 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 00000000000..a6afb68bab5 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 00000000000..1cd54c4bcab 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 00000000000..4f1935caabf 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 00000000000..6f94ce14c3c 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 8bb69581951..737cde5906d 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 cf1781d4235..a4493a5ee3e 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 00000000000..aee1b2fc710 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 77851d7d8f3..00000000000 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 00000000000..1c28f577595 Binary files /dev/null and b/icons/obj/mine.dmi differ diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index 7225f19d780..042118ab8a4 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 c2f207f87c3..e624d02927c 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 00000000000..0b3bb48a4ed 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 0ff60dbce78..fb224e2799f 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 00000000000..e3af8b280d6 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 00000000000..46531830bd8 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 096a46f79cf..00000000000 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 7b4c8d9f4eb..00000000000 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 00000000000..022fb48f4c7 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 02957d4097a..00000000000 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 4f8ada811c7..00000000000 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 c33488ac81f..00000000000 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 1bb003b6d99..c6952b814f0 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 00000000000..4713360313f 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 00000000000..ea2004324aa 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 00000000000..50cc6457594 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 3ac9258ed80..d81b442969a 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 cf784259590..534accc465e 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 14b782b9dba..ceeee078bfa 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 dc523a800a1..8d4b0c4ee5d 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 efcdd053605..46a25943bbf 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 b5179c2bd94..0f53db80c75 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 77c6de05aef..e7ea44f352b 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 d6e536822aa..376eed3145d 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 f5ed1e294bf..ccb4c5e12c0 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 43c5ed43367..03c69778701 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 129c5b08e97..500a16664c7 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 f1bf69ddda7..51b091fc0d0 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 7dda2d9ff4c..018cb124270 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 2fa3d828f5e..6454cc22344 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 c9e92ad6347..a6fd60b3e00 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 cde6caa5150..493d426ffd7 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 4308072cd47..a5bb07e061c 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 9db56fdf9a4..19bb80bf3a2 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 9691d2c27ff..c7abc4523d2 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 5adbc61c706..1075bf425aa 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 fc9f404b88e..45af5c6f8e6 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 1ff78b8cade..fc3ebff786d 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 c503f8dbd57..36d8182f59f 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 f4aafdce320..a44a244232b 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 0c4e64faab0..631204576f2 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 28b2618475f..d334f4879d6 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 8b1dd94704b..d149ef82ff8 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 8f79725b6aa..92cdc72ce31 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 dc2d59ddcc5..411714f57c5 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 d51ed84765b..5c3098b3c2c 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 6a0e4d9d359..e13f0fffa54 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 3647a632760..d4dd0bdff30 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 c799611aca0..34a51a33bfa 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 3e2abcf9d90..f724fda4781 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 542a6dcb22f..28f3b208c5a 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 04ce37b278c..f552714156e 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 db59a1035ae..320d177359c 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 025460d9724..e25b6cbebb8 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 00000000000..702d94917fa --- /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 00000000000..ec0fba60a72 --- /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 e338d1e763c..773215a8fa6 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 8e40dc7c43e..38d043f9a32 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 20fec8cfe1a..2aff3eedd2a 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 e2f387cd6ec..3fd9d8efa98 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 63105dc6e02..8f097a8874b 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 00000000000..4fd09ed6a0e --- /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 00000000000..296d4c33706 --- /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 00000000000..2dff1139cba --- /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 751ed5541ab..e22f8331cd6 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 269d8ed89b8..591e68506de 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 6b393f8984a..28311da174e 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 b89a49fad8d..47c3fe5848c 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 29ab815a119..e8a237e802c 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 00000000000..f96b87fbdd0 --- /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 00000000000..5c69cbfa8f0 --- /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 00000000000..48e7c0abd2d --- /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 ecca7be613b..03487b39c1a 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 00000000000..fa986fb0f32 --- /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 00000000000..75085d7bf7f --- /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 00000000000..225e16f78e9 --- /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 00000000000..30806beb050 --- /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 00000000000..df69bcb97e7 --- /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 00000000000..a92f2579a90 --- /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 00000000000..76d959208e1 --- /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 00000000000..f3ae92479b8 --- /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 00000000000..07c9e69e7e2 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 00000000000..fb7fe153c8a 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 00000000000..270a990c68d 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 00000000000..0a6dd82c1a8 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 00000000000..e9c1f5b98c2 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 00000000000..3e5ce1f94ea 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 00000000000..7a074dede24 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 00000000000..b60a0b111f8 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 00000000000..9c5446d4333 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 00000000000..75d53adb3b9 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 00000000000..fe1cbbda81f 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 00000000000..98cfb4dfbc1 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 00000000000..5847bb8b77d 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 00000000000..1f47e0be925 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 00000000000..a81e4c15d49 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 00000000000..5c7a4cf3457 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 00000000000..e3191b51daf 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 00000000000..3f050ed826b --- /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 bb0965dac40..5062fb805dc 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 00000000000..f307c660680 --- /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 00000000000..7512cb01f06 --- /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 00000000000..bf4afd629f4 --- /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 00000000000..e3ee7529b95 --- /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 96132b522a3..23281846c75 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 19b70b9367f..e2860756be5 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 6a3c2054ea9..5b3401cfd6f 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 64d4c6491db..95c298b44ee 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 2651b5a523e..a58c0f33880 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 7715e42d5ec..5aa245d448b 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 00000000000..1be188320c3 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 227ccc43c71..b2904fe9586 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 7a5c2529036..725bb52ed49 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 2018e9d5bae..0b6dc3289f5 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 fd6b15b0bde..8b4f204cad6 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 00000000000..e3cc2a1d78b --- /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 00000000000..a78e1496132 --- /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 00000000000..87a1644b488 --- /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 00000000000..f4ef8194e87 --- /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 00000000000..2632497590d --- /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 00000000000..e75f47ee349 --- /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 00000000000..d74ccce2da9 --- /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 00000000000..d5311230b2f --- /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 00000000000..38c8390e7d9 --- /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 0422dfdada6..38ed2242ec8 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 331f3bca828..dbb532ffeb5 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 00000000000..89633a51e39 --- /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 03430ec15bc..92e2760ed57 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 72413b05d27..510a49fac07 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 a88694656b6..6fd150032bd 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 51b9a2046e8..afb79c1f4ca 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 f12c1282f4d..ad193296438 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 fc04cd39238..435b9029647 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 935c254c2d1..e2de8ff768c 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 2cb311efb7a..05709304e93 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 ffb1b4057f8..1644ac27db6 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 f6ac60541a6..b6f8e5bb765 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 c1cbe47dd5b..4e5678471de 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 89913e40651..a9130f4fe2f 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 acb7c9b9a93..63f077b54d9 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 2a26f97bfba..c7bbea3566f 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 44746798706..b761299d513 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 6c5b71cb056..d992fb25eba 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 3c78ace7940..127c9ccf250 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 00000000000..d26961d4cf2 --- /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 978711e6746..d8a76a49c9e 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 60875709436..125b2de01b4 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 00000000000..232ca94ace9 --- /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 a83f74e9b60..c5898d11020 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 00000000000..b7dfa6953d0 --- /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 ad66f40abda..8906e2a4ea2 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 4f1e9c20d68..7a2b91c115b 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 5685f2613db..10659c8c787 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 a9c8664d8e7..3ff9c8f6c2f 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 00000000000..f19114b211a 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 00000000000..eb63cd76f23 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 00000000000..876e831defa 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 df5a5f7010a..cfc280f1e8a 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 1615c45bf74..a7307dc368d 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 1edfc99e00a..abe28e59b43 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 f9b5947f421..adcf99b37f3 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 60d933c5ab8..3a0eef87ae2 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 02b118b2e68..58f34e55244 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 cad31a0824b..985523ed2b3 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 367ddeccad7..5de54ba1304 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 564db2796e4..f5ab5f436f0 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 e5f6c03faf2..07b969c16b0 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 457c3b4f855..afa15ae3f36 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 cc3b522aa6e..842d89dad23 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 00000000000..670b41a1ced --- /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 00000000000..ad8b17b1758 --- /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 6b145ba4a50..1447a09a951 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 f0de98b9c7c..be367240f96 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 00000000000..8017985ae13 --- /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 81c4f3403d1..ea29d9c2d73 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 e36aa4201a3..1315351f350 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 00000000000..711fd50d482 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 00000000000..7f3c179ad80 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 00000000000..cf7173c8e81 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 00000000000..e4b5ac744b9 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 00000000000..cf0718c6e3b 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 00000000000..abfe12c0d9c 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 00000000000..99a5465d161 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 00000000000..b808068e55f 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 00000000000..c6d5f6846c9 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 00000000000..6a29326f24b 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 00000000000..4e14e30a11b 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 00000000000..36ebe40fdc5 --- /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 eb477c205f6..e04ad04182e 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 00000000000..b28ada773bf --- /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 00000000000..ea420f32ec3 --- /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 00000000000..b5a1d738e49 --- /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