diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b1e9a685237..a7671ea7196 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ on: env: BYOND_MAJOR: "516" - BYOND_MINOR: "1663" + BYOND_MINOR: "1685" SPACEMAN_DMM_VERSION: suite-1.11 jobs: @@ -106,6 +106,11 @@ jobs: with: path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} + - name: Install Dependencies + run: | + sudo dpkg --add-architecture i386 + sudo apt update || true + sudo apt install -o APT::Immediate-Configure=false curl:i386 - name: Run Tests env: TEST: MAP diff --git a/.gitignore b/.gitignore index 185303ed487..83d58a21245 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,10 @@ *.lk *.backup *.before -codex/ -data/ -dmdoc/ -cfg/ +/codex/ +/data/ +/dmdoc/ +/cfg/ build_log.txt use_map stopserver @@ -17,9 +17,9 @@ reboot_called atupdate # ignore config, but not subdirs -!config/*/ -config/* -sql/test_db +/!config/*/ +/config/* +/sql/test_db # misc OS garbage Thumbs.db @@ -27,7 +27,7 @@ Thumbs.db:encryptable .DS_Store # vscode -.vscode/* +/.vscode/* *.code-workspace .history diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index d9c934a0fb5..0662b49a090 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -25,6 +25,11 @@ #define TURF_IS_AMBIENT_LIT_UNSAFE(T) (T:ambient_active) #define TURF_IS_AMBIENT_LIT(T) (isturf(T) && TURF_IS_AMBIENT_LIT_UNSAFE(T)) +// These are centered around zero to simplify logic; 'up' is 'is greater than -1', 'down' is 'is less than 0'. 0 matches both conditions. +#define LIGHTING_CORNER_GENERATE_UP 1 +#define LIGHTING_CORNER_GENERATE_BOTH 0 +#define LIGHTING_CORNER_GENERATE_DOWN -1 + // If I were you I'd leave this alone. #define LIGHTING_BASE_MATRIX \ list \ diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 3721efb82c0..9fc564c109c 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -402,3 +402,9 @@ #define MM_ATTACK_RESULT_NONE 0 #define MM_ATTACK_RESULT_DEFLECTED BITFLAG(0) #define MM_ATTACK_RESULT_BLOCKED BITFLAG(1) + +// Effectively a speed modifier for how fast pollen is produced by flowering plants. Pollen per second. +// In theory, one pollen every 5 seconds (at time of writing) +#define POLLEN_PER_SECOND 0.2 +#define POLLEN_PRODUCTION_MULT (POLLEN_PER_SECOND * (SSplants.wait / 10)) +#define MAX_POLLEN_PER_FLOWER 10 diff --git a/code/_onclick/MouseDrag.dm b/code/_onclick/MouseDrag.dm deleted file mode 100644 index 6f7e0ee7c3e..00000000000 --- a/code/_onclick/MouseDrag.dm +++ /dev/null @@ -1,39 +0,0 @@ -//If we intercept it return true else return false -/atom/proc/RelayMouseDrag(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params, mob/user) - return FALSE - -/atom/proc/RelayMouseDown(atom/object, location, control, params, mob/user) - return FALSE - -/atom/proc/RelayMouseUp(atom/object, location, control, params, mob/user) - return FALSE - -/mob/proc/OnMouseDrag(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params) - if(loc) - var/atom/A = loc - if(A.RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, src)) - return - - var/obj/item/gun/gun = get_active_held_item() - if(check_intent(I_FLAG_HARM) && istype(over_object) && (isturf(over_object) || isturf(over_object.loc)) && !incapacitated() && istype(gun)) - gun.set_autofire(over_object, src) - -/mob/proc/OnMouseDown(atom/object, location, control, params) - if(loc) - var/atom/A = loc - if(A.RelayMouseDown(object, location, control, params, src)) - return - - var/obj/item/gun/gun = get_active_held_item() - if(check_intent(I_FLAG_HARM) && istype(object) && (isturf(object) || isturf(object.loc)) && !incapacitated() && istype(gun)) - gun.set_autofire(object, src) - -/mob/proc/OnMouseUp(atom/object, location, control, params) - if(loc) - var/atom/A = loc - if(A.RelayMouseUp(object, location, control, params, src)) - return - - var/obj/item/gun/gun = get_active_held_item() - if(istype(gun)) - gun.clear_autofire() diff --git a/code/_onclick/mouse_drag.dm b/code/_onclick/mouse_drag.dm new file mode 100644 index 00000000000..06a79c5a596 --- /dev/null +++ b/code/_onclick/mouse_drag.dm @@ -0,0 +1,91 @@ +/atom/proc/relayed_mouse_down(mob/user, object, location, control, params) + return null + +/atom/proc/relayed_mouse_held(mob/user, atom/target) + return null + +/atom/proc/relayed_mouse_up(mob/user, atom/target) + return null + +/mob/proc/on_mouse_down(object, location, control, params) + + // We are mouse down outside the map or on a screen element, assume it is not relevant. + if(!isatom(object) || istype(object, /obj/screen)) + return FALSE + + // Do not do this for things that are not in the world. + var/atom/atom = object + if(!isturf(atom) && !isturf(atom.loc)) + return FALSE + + // Debounce, we might already be holding. + if(_is_holding_mouse) + return FALSE + + // Ignore right click and middle click currently. + // Might be worth handling these in the future. + var/list/modifiers = params2list(params) + if(modifiers["middle"] || modifiers["right"]) + return FALSE + + // Keep track of when we started holding the mouse down so that we can check if we hold it long enough to start the drag behavior. + _started_mouse_down = world.time + + // Might be inside an exosuit or such that has its own handling for these inputs. + . = loc?.relayed_mouse_down(src, object, location, control, params) + if(isnull(.)) + + // Handle our actual 'drag beginning' logic. + var/obj/item/held = get_active_held_item() + . = istype(held) && held.wielder_mouse_drag_down(src, object, location, control, params) + + if(.) + update_mouse_pointer() + SSclickdrag.active_wielders[src] = TRUE + _is_holding_mouse = TRUE + +/mob/proc/on_mouse_held() + + if(!_is_holding_mouse) + return FALSE + + // Grace period before we start holding (rather than a single click) + if(world.time < _started_mouse_down + MOUSE_DRAG_DELAY) + return TRUE + + var/atom/mouse_over = _last_mouse_over_atom?.resolve() + if(QDELETED(mouse_over) || !istype(mouse_over)) + mouse_over = null + + // Might be inside an exosuit or such that has its own handling for these inputs. + . = loc?.relayed_mouse_held(src, mouse_over) + if(isnull(.)) + var/obj/item/held = get_active_held_item() + . = istype(held) && held.wielder_mouse_drag_held(src, mouse_over) + + if(.) + update_mouse_pointer() + set_dir(get_dir(src, mouse_over)) + else + on_mouse_up() + +/mob/proc/on_mouse_up(remove_from_processing = TRUE) + + if(!_is_holding_mouse) + return FALSE + + // Don't block the follow-up Click() if this wasn't an 'official' drag. + if(world.time >= _started_mouse_down + MOUSE_DRAG_DELAY) + var/atom/mouse_over = _last_mouse_over_atom?.resolve() + if(QDELETED(mouse_over) || !istype(mouse_over)) + mouse_over = null + . = loc?.relayed_mouse_up(src, mouse_over) + if(isnull(.)) + var/obj/item/held = get_active_held_item() + . = istype(held) && held.wielder_mouse_drag_up(src, mouse_over) + + update_mouse_pointer() + _is_holding_mouse = FALSE + SSclickdrag.active_wielders -= src + if(remove_from_processing && length(SSclickdrag.processing_wielders)) + SSclickdrag.processing_wielders -= src diff --git a/code/controllers/subsystems/clickdrag.dm b/code/controllers/subsystems/clickdrag.dm new file mode 100644 index 00000000000..97164a2fa64 --- /dev/null +++ b/code/controllers/subsystems/clickdrag.dm @@ -0,0 +1,54 @@ +SUBSYSTEM_DEF(clickdrag) + name = "Clickdrag" + wait = 1 + flags = SS_TICKER | SS_NO_INIT + + var/tmp/list/active_wielders = list() + var/tmp/active_wielders_copied_yet = FALSE + var/tmp/list/processing_wielders + +/datum/controller/subsystem/clickdrag/stat_entry() + ..("W:[active_wielders.len]") + +/datum/controller/subsystem/clickdrag/fire(resumed = 0) + + if(!resumed) + active_wielders_copied_yet = FALSE + + if(!active_wielders_copied_yet) + active_wielders_copied_yet = TRUE + processing_wielders = active_wielders.Copy() + + var/mob/wielder + var/i = 0 + while(i < processing_wielders.len) + i++ + wielder = processing_wielders[i] + if(!wielder.on_mouse_held()) + wielder.on_mouse_up(remove_from_processing = FALSE) // we will do this via our list iteration anyway + if (MC_TICK_CHECK) + processing_wielders.Cut(1, i+1) + return + processing_wielders.Cut() + +/client + // (BOOL) Flag for whether or not the next Click() should be blocked - Click() is called immediately after MouseUp() which isn't desirable. + VAR_PRIVATE/tmp/_block_next_click = FALSE + +/mob + // (DATUM) Tracker for clickdrag subsystem, + VAR_PRIVATE/tmp/weakref/_last_mouse_over_atom + // (BOOL) Flag for keeping track of if we're already processing or not. + VAR_PRIVATE/tmp/_is_holding_mouse = FALSE + // (INT) Time that we started holding the mouse down. + VAR_PRIVATE/tmp/_started_mouse_down = 0 + // (FLOAT) Delay before a hold is considered a hold rather than a single click. + VAR_PRIVATE/const/MOUSE_DRAG_DELAY = 0.25 SECONDS + +/client/MouseEntered(object,location,control,params) + UNLINT(mob?._last_mouse_over_atom = weakref(object)) + . = ..() + +/client/MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) + UNLINT(mob?._last_mouse_over_atom = weakref(over_object)) + . = ..() diff --git a/code/controllers/subsystems/initialization/customitems.dm b/code/controllers/subsystems/initialization/customitems.dm index d571851c618..d3bdc56698b 100644 --- a/code/controllers/subsystems/initialization/customitems.dm +++ b/code/controllers/subsystems/initialization/customitems.dm @@ -49,6 +49,8 @@ SUBSYSTEM_DEF(customitems) //gets the relevant list for the key from the listlist if it exists, check to make sure they are meant to have it and then calls the giving function /datum/controller/subsystem/customitems/proc/equip_custom_items(mob/living/human/M) + if(!istype(M) || !M.ckey) + return var/list/key_list = custom_items_by_ckey[M.ckey] if(!length(key_list)) return diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index 886d58c7a9c..63bc4995bee 100644 --- a/code/controllers/subsystems/jobs.dm +++ b/code/controllers/subsystems/jobs.dm @@ -565,7 +565,7 @@ SUBSYSTEM_DEF(jobs) job.post_equip_job_title(H, alt_title || job_title) - H.client.show_location_blurb(30) + H.client?.show_location_blurb(30) return H diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index 1eb7ecbbf52..90cdbebd8c4 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -48,8 +48,6 @@ SUBSYSTEM_DEF(mapping) var/base_floor_area /// A list of connected z-levels to avoid repeatedly rebuilding connections var/list/connected_z_cache = list() - /// A list of turbolift holders to initialize. - var/list/turbolifts_to_initialize = list() ///Associative list of planetoid/exoplanet data currently registered. The key is the planetoid id, the value is the planetoid_data datum. var/list/planetoid_data_by_id ///List of all z-levels in the world where the index corresponds to a z-level, and the key at that index is the planetoid_data datum for the associated planet @@ -169,9 +167,9 @@ SUBSYSTEM_DEF(mapping) 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() + for(var/modpack_name in SSmodpacks.loaded_modpacks) + var/decl/modpack/loaded_modpack = SSmodpacks.loaded_modpacks[modpack_name] + loaded_modpack.on_mapping_pre_finalize() // With levels set up and serde complete (and levels flagged) we can do any remaining level generation. global.using_map.finalize_map_generation() @@ -256,20 +254,34 @@ SUBSYSTEM_DEF(mapping) planetoid_data_by_z.len = world.maxz connected_z_cache.Cut() + SSzcopy?.calculate_zstack_limits() + //Update SSWeather's indexed lists, if we can. if(SSweather?.weather_by_z) SSweather.weather_by_z.len = world.maxz +/// This is equivalent to calling `increment_world_z_size()` in a loop, but more efficient. +/datum/controller/subsystem/mapping/proc/bulk_increment_world_z_size(num_z_levels, new_level_type, defer_setup = FALSE) + ASSERT(num_z_levels > 0) + var/old_max = world.maxz + world.maxz += num_z_levels + + reindex_lists() + + if (!new_level_type) + CRASH("Missing z-level data type for z[old_max] through z[old_max + num_z_levels]!") + + for (var/i in 1 to num_z_levels) + var/datum/level_data/level = new new_level_type(old_max + i, defer_setup) + level.initialize_new_level() + /datum/controller/subsystem/mapping/proc/increment_world_z_size(var/new_level_type, var/defer_setup = FALSE) world.maxz++ reindex_lists() - if(SSzcopy.zlev_maximums.len) - SSzcopy.calculate_zstack_limits() if(!new_level_type) - PRINT_STACK_TRACE("Missing z-level data type for z["[world.maxz]"]!") - return + CRASH("Missing z-level data type for z[world.maxz]!") var/datum/level_data/level = new new_level_type(world.maxz, defer_setup) level.initialize_new_level() diff --git a/code/controllers/subsystems/misc_late.dm b/code/controllers/subsystems/misc_late.dm index 7f9ceb8ce6e..45f0efac238 100644 --- a/code/controllers/subsystems/misc_late.dm +++ b/code/controllers/subsystems/misc_late.dm @@ -3,18 +3,15 @@ SUBSYSTEM_DEF(misc_late) name = "Late Initialization" init_order = SS_INIT_MISC_LATE flags = SS_NO_FIRE - var/list/turbolifts_to_open = list() /datum/controller/subsystem/misc_late/Initialize() var/decl/asset_cache/asset_cache = GET_DECL(/decl/asset_cache) asset_cache.load() - // This is gross but I'm not sure where else to handle it. Sorry. - for(var/datum/turbolift/lift in turbolifts_to_open) - if(!QDELETED(lift)) - lift.open_doors() - turbolifts_to_open.Cut() + for(var/modpack_name in SSmodpacks.loaded_modpacks) + var/decl/modpack/loaded_modpack = SSmodpacks.loaded_modpacks[modpack_name] + loaded_modpack.on_misc_late_init() // Pre-populate the emote list. decls_repository.get_decls_of_type(/decl/emote) diff --git a/code/controllers/subsystems/zcopy.dm b/code/controllers/subsystems/zcopy.dm index e0dfe3f0b1c..e6290e99300 100644 --- a/code/controllers/subsystems/zcopy.dm +++ b/code/controllers/subsystems/zcopy.dm @@ -168,7 +168,7 @@ SUBSYSTEM_DEF(zcopy) // Flush the queue. fire(FALSE, TRUE) -// If you add a new Zlevel or change Z-connections, call this. +/// (Re)generate Z-group information. You should run this every time world.maxz (or z-connections) change. ZM's behavior is undefined between resizing the world and calling this proc. /datum/controller/subsystem/zcopy/proc/calculate_zstack_limits() zlev_maximums = new(world.maxz) var/start_zlev = 1 diff --git a/code/datums/movement/automove.dm b/code/datums/movement/automove.dm index a07d65d0324..6c3533f758f 100644 --- a/code/datums/movement/automove.dm +++ b/code/datums/movement/automove.dm @@ -37,4 +37,4 @@ /// Generalized entrypoint for checking CanMove and such on /mob. /atom/movable/proc/can_do_automated_move(variant_move_delay) - return FALSE + return MayMove(src) diff --git a/code/datums/movement/automove_controller.dm b/code/datums/movement/automove_controller.dm index b413d999849..e593f4315c9 100644 --- a/code/datums/movement/automove_controller.dm +++ b/code/datums/movement/automove_controller.dm @@ -1,11 +1,32 @@ /// Implements automove logic; can be overridden on mob procs if you want to vary the logic from the below. /decl/automove_controller - var/completion_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom reaches its target. - var/failure_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom fails to move. + // these could be proper bools but i assumed they were set up this way for a reason, like supporting other return values in the future + var/completion_signal = FALSE // Set to PROCESS_KILL if you want movement to stop processing when the atom reaches its target. + var/failure_signal = FALSE // Set to PROCESS_KILL if you want movement to stop processing when the atom fails to move. var/try_avoid_obstacles = TRUE // Will try to move 90 degrees around an obstacle. -/decl/automove_controller/proc/handle_mover(atom/movable/mover, datum/automove_metadata/metadata) +/decl/automove_controller/proc/check_move_completion(atom/movable/mover, datum/automove_metadata/metadata) + // Null target means abandon pathing, regardless of return signals. + var/atom/target = mover.get_automove_target(metadata) + if(!istype(target)) + return TRUE + // Cease automovement if we're already at the target. + var/acceptable_move_dist = isnull(metadata?.acceptable_distance) ? mover.get_acceptable_automove_distance_from_target() : metadata.acceptable_distance + var/current_distance = get_dist(mover, target) + if(metadata?.avoid_target) + return current_distance >= acceptable_move_dist + else + if(get_turf(mover) == get_turf(target)) + return TRUE + if(ismovable(target) && (target.density && mover.density) && mover.Adjacent(target)) + return TRUE + if(current_distance <= acceptable_move_dist) + return TRUE + return FALSE + +/// Return PROCESS_KILL to terminate automovement. Will return completion_signal when the atom reaches its target and failure_signal if it fails to move. +/decl/automove_controller/proc/handle_mover(atom/movable/mover, datum/automove_metadata/metadata) // Cease automovement if we got an invalid mover.. if(!istype(mover)) return PROCESS_KILL @@ -19,48 +40,60 @@ if(ismob(mover)) var/mob/mover_mob = mover if(mover_mob.moving) - return TRUE + return - // Cease automovement if we're already at the target. - var/avoid_target = metadata?.avoid_target - if(!avoid_target && (get_turf(mover) == get_turf(target) || (ismovable(target) && mover.Adjacent(target)))) + if(check_move_completion(mover, metadata)) mover.finished_automove() return completion_signal - // Cease movement if we're close enough to the target. - var/acceptable_move_dist = isnull(metadata?.acceptable_distance) ? mover.get_acceptable_automove_distance_from_target() : metadata.acceptable_distance - if(avoid_target ? (get_dist(mover, target) >= acceptable_move_dist) : (get_dist(mover, target) <= acceptable_move_dist)) - mover.finished_automove() - return completion_signal + // Skip automovement if we aren't allowed to move yet. + // This is for checks that are expected to fail sometimes (movedelay, incapacitation, etc), so we don't send the failure signal when this happens. + if(!mover.can_do_automated_move(metadata?.move_delay)) + return - // Cease automovement if we failed to move a turf. - if(mover.can_do_automated_move(metadata?.move_delay)) - if(avoid_target) - target = get_edge_target_turf(target, get_dir(target, mover)) - - // Note for future coders: SelfMove() only confirms if a handler handled the move, not if the atom moved. - var/old_loc = mover.loc + var/avoid_target = metadata?.avoid_target + if(avoid_target) + target = get_edge_target_turf(target, get_dir(target, mover)) - // Try to move directly. - var/target_dir = get_dir(mover, target) - if(!target_dir) - if(avoid_target) - target_dir = pick(global.cardinal) - else - return TRUE // no idea how we would get into this position + // Note for future coders: SelfMove() only confirms if a handler handled the move, not if the atom moved. + var/old_loc = mover.loc - if(mover.SelfMove(target_dir) && (old_loc != mover.loc)) - mover.handle_post_automoved(old_loc) - return (mover.get_automove_target() == mover.loc) // We may have transitioned to the next step in a path. + // Try to move directly. + var/target_dir = get_dir(mover, target) + if(!target_dir) + if(avoid_target) + target_dir = pick(global.cardinal) + else + return // no idea how we would get into this position + var/old_next_move_time = mover.get_next_move_time() // to reset to later, so obstacle bumps don't let us ignore move delay + if(mover.SelfMove(target_dir) && (old_loc != mover.loc)) + mover.handle_post_automoved(old_loc) + // check if we're done, and if so, return the completion signal + if(check_move_completion(mover, metadata)) + mover.finished_automove() + return completion_signal + return // we moved, so we didn't fail, but we also aren't finished yet + else if(try_avoid_obstacles) // Try to move around any obstacle. var/static/list/_alt_dir_rot = list(45, -45) for(var/alt_dir in shuffle(_alt_dir_rot)) - mover.reset_movement_delay() + mover.set_next_move_time(old_next_move_time) if(mover.SelfMove(turn(target_dir, alt_dir)) && (old_loc != mover.loc)) - return TRUE + mover.handle_post_automoved(old_loc) + // check if we're done, and if so, return the completion signal + if(check_move_completion(mover, metadata)) + mover.finished_automove() + return completion_signal + return // see above; we succeeded on the retry but aren't done moving mover.failed_automove() + return failure_signal + +/decl/automove_controller/stop_on_completion + completion_signal = PROCESS_KILL - return failure_signal +/decl/automove_controller/stop_on_fail_or_completion + completion_signal = PROCESS_KILL + failure_signal = PROCESS_KILL \ No newline at end of file diff --git a/code/datums/movement/mob.dm b/code/datums/movement/mob.dm index 996c6215085..ee52cd4ff8c 100644 --- a/code/datums/movement/mob.dm +++ b/code/datums/movement/mob.dm @@ -120,7 +120,7 @@ next_move += max(0, delay) // Stop effect -/datum/movement_handler/mob/DoMove(direction, mob/mover, is_external) +/datum/movement_handler/mob/stop_effect/DoMove(direction, mob/mover, is_external) if(MayMove(mover, is_external) == MOVEMENT_STOP) return MOVEMENT_HANDLED diff --git a/code/datums/trading/traders/misc.dm b/code/datums/trading/traders/misc.dm index 6356be83f0d..13c1828ffef 100644 --- a/code/datums/trading/traders/misc.dm +++ b/code/datums/trading/traders/misc.dm @@ -119,8 +119,7 @@ /obj/item/chems/spray/waterflower = TRADER_THIS_TYPE, /obj/item/gun/launcher/pneumatic/small = TRADER_THIS_TYPE, /obj/item/gun/projectile/revolver/capgun = TRADER_THIS_TYPE, - /obj/item/clothing/mask/fakemoustache = TRADER_THIS_TYPE, - /obj/item/grenade/spawnergrenade/fake_carp = TRADER_THIS_TYPE + /obj/item/clothing/mask/fakemoustache = TRADER_THIS_TYPE ) /datum/trader/ship/replica_shop diff --git a/code/game/area/area_abstract.dm b/code/game/area/area_abstract.dm index 853b2907a95..55e8c249a71 100644 --- a/code/game/area/area_abstract.dm +++ b/code/game/area/area_abstract.dm @@ -1,9 +1,11 @@ /area/hallway + abstract_type = /area/hallway name = "hallway" holomap_color = HOLOMAP_AREACOLOR_HALLWAYS area_start_lit = TRUE /area/maintenance + abstract_type = /area/maintenance area_flags = AREA_FLAG_RAD_SHIELDED sound_env = TUNNEL_ENCLOSED turf_initializer = /decl/turf_initializer/maintenance @@ -12,6 +14,7 @@ holomap_color = HOLOMAP_AREACOLOR_HALLWAYS /area/shuttle + abstract_type = /area/shuttle requires_power = 0 sound_env = SMALL_ENCLOSED base_turf = /turf/space @@ -19,6 +22,10 @@ holomap_color = HOLOMAP_AREACOLOR_CREW /area/ship + abstract_type = /area/ship name = "\improper Generic Ship" ambience = list('sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg') holomap_color = HOLOMAP_AREACOLOR_CREW + +/area/map_template + abstract_type = /area/map_template \ No newline at end of file diff --git a/code/game/area/area_fishing.dm b/code/game/area/area_fishing.dm deleted file mode 100644 index 8370e39c449..00000000000 --- a/code/game/area/area_fishing.dm +++ /dev/null @@ -1,16 +0,0 @@ -/area - var/fishing_failure_prob = 95 - // Hardcoding the contents of /obj/random/junk to avoid hacks for getting results from /obj/random. - var/list/fishing_results = list( - /obj/item/remains/mouse = 1, - /obj/item/remains/robot = 1, - /obj/item/paper/crumpled = 1, - /obj/item/inflatable/torn = 1, - /obj/item/shard = 1, - /obj/item/hand/missing_card = 1 - ) - -/area/proc/get_fishing_result(turf/origin, obj/item/food/bait) - if(!length(fishing_results) || prob(fishing_failure_prob)) - return null - return pickweight(fishing_results) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index e59bc1aca7a..fd202b51c63 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -98,15 +98,7 @@ var/global/list/areas = list() area_blurb_category = type ..() -/area/proc/get_additional_fishing_results() - return - /area/Initialize() - var/list/additional_fishing_results = get_additional_fishing_results() - if(LAZYLEN(additional_fishing_results)) - LAZYINITLIST(fishing_results) - for(var/fish in additional_fishing_results) - fishing_results[fish] = additional_fishing_results[fish] . = ..() global.areas += src if(!requires_power || !apc) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 628b5880042..47c5406dd73 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -583,9 +583,16 @@ throwing = null /atom/movable/proc/reset_movement_delay() + set_next_move_time(world.time) + +/atom/movable/proc/get_next_move_time() + var/datum/movement_handler/delay/delay = locate() in movement_handlers + return delay?.next_move + +/atom/movable/proc/set_next_move_time(new_time) var/datum/movement_handler/delay/delay = locate() in movement_handlers - if(istype(delay)) - delay.next_move = world.time + if(delay) + delay.next_move = new_time /atom/movable/get_affecting_weather() var/turf/my_turf = get_turf(src) diff --git a/code/game/machinery/_machines_base/machine_construction/_construction.dm b/code/game/machinery/_machines_base/machine_construction/_construction.dm index 3555ee7173f..72679df311c 100644 --- a/code/game/machinery/_machines_base/machine_construction/_construction.dm +++ b/code/game/machinery/_machines_base/machine_construction/_construction.dm @@ -14,6 +14,7 @@ // Called on state transition; can intercept, but must call parent. /obj/machinery/proc/state_transition(var/decl/machine_construction/new_state, var/mob/user) + SHOULD_CALL_PARENT(TRUE) construct_state = new_state // Return a change state define or a fail message to block transition. diff --git a/code/game/machinery/_machines_base/machine_construction/emitter.dm b/code/game/machinery/_machines_base/machine_construction/emitter.dm new file mode 100644 index 00000000000..f1b87c2ad6a --- /dev/null +++ b/code/game/machinery/_machines_base/machine_construction/emitter.dm @@ -0,0 +1,217 @@ +// Emitters are not screwed apart like most machines; they are bolted down with a wrench and then welded in place. +// Some subtypes like gyrotrons also use panel_state to gain the usual maintenance hatch on top of that. +// Yes I hate this, yes it should be done differently, no I do not have it in me to do it any other way. +// Maybe we should just bite the bullet and make gyrotrons not emitters?? +/decl/machine_construction/emitter + visible_components = FALSE + /// The state entered when the emitter is fastened down further, if any. + var/down_state + /// The state entered when the emitter is loosened, if any. + var/up_state + /// Whether the emitter is anchored to the floor in this state. + var/anchored = FALSE + // gyrotron stuff below + /// The state entered when the maintenance hatch is toggled via screwdriver. Null if the emitter has no hatch. + var/panel_state + /// Whether the maintenance hatch is open in this state. + var/panel_open = FALSE + +/decl/machine_construction/emitter/state_is_valid(obj/machinery/machine) + return (machine.anchored == anchored) && (machine.panel_open == panel_open) + +/decl/machine_construction/emitter/validate_state(obj/machinery/machine) + . = ..() + if(!.) + if(machine.panel_open != panel_open) + try_change_state(machine, panel_state) + else + try_change_state(machine, machine.anchored ? down_state : up_state) + +// the panel starts open after construction, again taken from /decl/machine_construction/default/panel_closed +/decl/machine_construction/emitter/post_construct(obj/machinery/machine) + if(!panel_state || panel_open) + return + try_change_state(machine, panel_state) + machine.panel_open = TRUE + machine.queue_icon_update() + +/// Handles a wrench applied to the emitter in this state. Return TRUE if the interaction was handled. +/decl/machine_construction/emitter/proc/wrench_interaction(obj/item/used_item, mob/user, obj/machinery/emitter/machine) + return FALSE + +/// Handles a welding tool applied to the emitter in this state. Return TRUE if the interaction was handled. +/decl/machine_construction/emitter/proc/welder_interaction(obj/item/weldingtool/welder, mob/user, obj/machinery/emitter/machine) + return FALSE + +/decl/machine_construction/emitter/attackby(obj/item/used_item, mob/user, obj/machinery/emitter/machine) + if((. = ..())) + return + if(machine.active) // can't open/close/unweld/etc while operating + to_chat(user, SPAN_WARNING("Turn \the [machine] off first.")) + return TRUE + if(IS_WRENCH(used_item)) + return wrench_interaction(used_item, user, machine) + else if(IS_WELDER(used_item)) + return welder_interaction(used_item, user, machine) + // everything after this is for gyrotrons/etc + if(!panel_state) + return FALSE + // handle this here because otherwise we'd have some nasty code duplication + if(IS_SCREWDRIVER(used_item)) + TRANSFER_STATE(panel_state) + playsound(get_turf(machine), 'sound/items/Screwdriver.ogg', 50, 1) + machine.panel_open = !panel_open + to_chat(user, SPAN_NOTICE("You [machine.panel_open ? "open" : "close"] the maintenance hatch of \the [machine].")) + machine.update_icon() // could be done in a machinery level /state_transition() override but whatever + return TRUE + // sigh. copied from /decl/machine_construction/default/panel_open and /decl/machine_construction/default/panel_closed + // again done this way to avoid duplication because gyrotrons can have any combo of panel + emitter state + if(!panel_open) + // closed panel (taken from panel_closed) + // maybe these should be on the part replacer or something... + // there's so much code duplication between different panel open/closed states and i hate it. + // maybe we just need to separate it out to a separate state machine and let construct state determine if panel state can change + if(istype(used_item, /obj/item/part_replacer)) + var/obj/item/part_replacer/replacer = used_item + if(replacer.remote_interaction) + machine.part_replacement(user, replacer) + for(var/line in machine.get_part_info_strings(user)) + to_chat(user, line) + return TRUE + return FALSE + // open panel (taken from panel_open) + if(IS_CROWBAR(used_item)) + TRANSFER_STATE(/decl/machine_construction/default/deconstructed) + playsound(get_turf(machine), 'sound/items/Crowbar.ogg', 50, 1) + machine.visible_message(SPAN_NOTICE("\The [user] deconstructs \the [machine].")) + machine.dismantle() + return + if(istype(used_item, /obj/item/part_replacer)) + return machine.part_replacement(user, used_item) + if(istype(used_item)) + return machine.part_insertion(user, used_item) + return FALSE + +/decl/machine_construction/emitter/mechanics_info() + . = list() + if(!panel_state) + return + if(panel_open) + . += "Use a screwdriver to close the maintenance hatch." + . += "Use a parts replacer to upgrade some parts." + . += "Use a crowbar to remove the circuit and deconstruct the emitter." + . += "Insert a new part to install it." + else + . += "Use a screwdriver to open the maintenance hatch." + . += "Use a parts replacer to view installed parts." + +/decl/machine_construction/emitter/unsecured + down_state = /decl/machine_construction/emitter/anchored + +/decl/machine_construction/emitter/unsecured/wrench_interaction(obj/item/used_item, mob/user, obj/machinery/emitter/machine) + TRANSFER_STATE(down_state) + playsound(machine.loc, 'sound/items/Ratchet.ogg', 75, 1) + user.visible_message( + "\The [user] secures \the [machine] to the floor.", + "You secure the external reinforcing bolts to the floor.", + "You hear a ratchet.") + machine.anchored = TRUE + return TRUE + +/decl/machine_construction/emitter/unsecured/welder_interaction(obj/item/weldingtool/welder, mob/user, obj/machinery/emitter/machine) + to_chat(user, SPAN_WARNING("\The [machine] needs to be wrenched to the floor.")) + return TRUE + +/decl/machine_construction/emitter/unsecured/mechanics_info() + . = ..() + . += "Use a wrench to anchor the emitter to the floor." + +/decl/machine_construction/emitter/anchored + anchored = TRUE + down_state = /decl/machine_construction/emitter/welded + up_state = /decl/machine_construction/emitter/unsecured + +/decl/machine_construction/emitter/anchored/wrench_interaction(obj/item/used_item, mob/user, obj/machinery/emitter/machine) + TRANSFER_STATE(up_state) + playsound(machine.loc, 'sound/items/Ratchet.ogg', 75, 1) + user.visible_message( + "\The [user] unsecures \the [machine]'s reinforcing bolts from the floor.", + "You undo the external reinforcing bolts.", + "You hear a ratchet.") + machine.anchored = FALSE + return TRUE + +/decl/machine_construction/emitter/anchored/welder_interaction(obj/item/weldingtool/welder, mob/user, obj/machinery/emitter/machine) + if(!welder.do_tool_interaction(TOOL_WELDER, user, machine, 2 SECONDS, \ + "welding", \ + "welding", \ + "You fail to weld \the [machine] to the floor.", \ + fuel_expenditure = 1) \ + ) + return TRUE // failed for whatever reason + TRANSFER_STATE(down_state) + return TRUE + +/decl/machine_construction/emitter/anchored/mechanics_info() + . = ..() + . += "Use a wrench to undo the bolts anchoring the emitter to the floor." + . += "Use a welding tool to weld the emitter to the floor, allowing it to fire." + +/decl/machine_construction/emitter/welded + anchored = TRUE + up_state = /decl/machine_construction/emitter/anchored + +/decl/machine_construction/emitter/welded/wrench_interaction(obj/item/used_item, mob/user, obj/machinery/emitter/machine) + to_chat(user, SPAN_WARNING("\The [machine] needs to be unwelded from the floor.")) + return TRUE + +/decl/machine_construction/emitter/welded/welder_interaction(obj/item/weldingtool/welder, mob/user, obj/machinery/emitter/machine) + if(!welder.do_tool_interaction(TOOL_WELDER, user, machine, 2 SECONDS, \ + "cutting free", \ + "cutting free", \ + "You fail to cut \the [machine] free from the floor.", \ + fuel_expenditure = 1) \ + ) + return TRUE // failed for whatever reason + TRANSFER_STATE(up_state) + return TRUE + +/decl/machine_construction/emitter/welded/mechanics_info() + . = ..() + . += "Use a welding tool to cut the emitter free from the floor." + +// Emitters built from a circuitboard also have a maintenance hatch, giving one state per (bolting, hatch) pair. +/decl/machine_construction/emitter/unsecured/gyrotron + needs_board = "machine" + down_state = /decl/machine_construction/emitter/anchored/gyrotron + panel_state = /decl/machine_construction/emitter/unsecured/gyrotron/panel_open + +/decl/machine_construction/emitter/unsecured/gyrotron/panel_open + panel_open = TRUE + visible_components = TRUE + down_state = /decl/machine_construction/emitter/anchored/gyrotron/panel_open + panel_state = /decl/machine_construction/emitter/unsecured/gyrotron + +/decl/machine_construction/emitter/anchored/gyrotron + needs_board = "machine" + down_state = /decl/machine_construction/emitter/welded/gyrotron + up_state = /decl/machine_construction/emitter/unsecured/gyrotron + panel_state = /decl/machine_construction/emitter/anchored/gyrotron/panel_open + +/decl/machine_construction/emitter/anchored/gyrotron/panel_open + panel_open = TRUE + visible_components = TRUE + down_state = /decl/machine_construction/emitter/welded/gyrotron/panel_open + up_state = /decl/machine_construction/emitter/unsecured/gyrotron/panel_open + panel_state = /decl/machine_construction/emitter/anchored/gyrotron + +/decl/machine_construction/emitter/welded/gyrotron + needs_board = "machine" + up_state = /decl/machine_construction/emitter/anchored/gyrotron + panel_state = /decl/machine_construction/emitter/welded/gyrotron/panel_open + +/decl/machine_construction/emitter/welded/gyrotron/panel_open + panel_open = TRUE + visible_components = TRUE + up_state = /decl/machine_construction/emitter/anchored/gyrotron/panel_open + panel_state = /decl/machine_construction/emitter/welded/gyrotron diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index b788ebfe1e8..1b635ce452c 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -11,6 +11,8 @@ var/start_pressure = ONE_ATMOSPHERE /obj/machinery/portable_atmospherics/get_single_monetary_worth() + if(worthless) + return 0 . = ..() for(var/gas_type, gas_amount in air_contents?.gas) var/decl/material/gas_data = GET_DECL(gas_type) diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index 8128cf6e71d..d33e666c3f1 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -1,4 +1,5 @@ /obj/machinery/computer/upload + abstract_type = /obj/machinery/computer/upload name = "unused upload console" icon_keyboard = "rd_key" icon_screen = "command" diff --git a/code/modules/power/singularity/emitter.dm b/code/game/machinery/emitter.dm similarity index 63% rename from code/modules/power/singularity/emitter.dm rename to code/game/machinery/emitter.dm index 192376cc994..a497a6d2c5f 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/game/machinery/emitter.dm @@ -12,17 +12,17 @@ var/efficiency = 0.3 // Energy efficiency. 30% at this time, so 100kW load means 30kW laser pulses. var/minimum_power = 10 KILOWATTS // The minimum power below which the emitter will turn off; different than the power needed to fire. - var/active = 0 + var/active = FALSE var/fire_delay = 100 var/max_burst_delay = 100 var/min_burst_delay = 20 var/burst_shots = 3 var/last_shot = 0 var/shot_number = 0 - var/state = 0 - var/locked = 0 - var/powered = 0 + var/locked = FALSE + var/powered = FALSE core_skill = SKILL_ENGINES + construct_state = /decl/machine_construction/emitter/unsecured uncreated_component_parts = list( /obj/item/stock_parts/radio/receiver, @@ -39,7 +39,11 @@ /obj/machinery/emitter/anchored anchored = TRUE - state = 2 + construct_state = /decl/machine_construction/emitter/welded + +/// Returns TRUE if the emitter is able to fire based on its construction state (currently checks if welded down). +/obj/machinery/emitter/proc/can_fire() + return istype(construct_state, /decl/machine_construction/emitter/welded) /obj/machinery/emitter/Destroy() log_and_message_admins("deleted \the [src]") @@ -62,29 +66,28 @@ if(!istype(user)) user = null // safety, as the proc is publicly available. - if(state == 2) - if(!locked) - if(active==1) - active = 0 - to_chat(user, "You turn off \the [src].") - log_and_message_admins("turned off \the [src]", user) - investigate_log("turned off by [key_name_admin(user)]","singulo") - else - active = 1 - if(user) - operator_skill = user.get_skill_value(core_skill) - update_efficiency() - to_chat(user, "You turn on \the [src].") - shot_number = 0 - fire_delay = get_initial_fire_delay() - log_and_message_admins("turned on \the [src]", user) - investigate_log("turned on by [key_name_admin(user)]","singulo") - update_icon() + if(!can_fire()) + to_chat(user, SPAN_WARNING("\The [src] needs to be firmly secured to the floor first.")) + return 1 + if(!locked) + if(active) + active = FALSE + to_chat(user, SPAN_NOTICE("You turn off \the [src].")) + log_and_message_admins("turned off \the [src]", user) + investigate_log("turned off by [key_name_admin(user)]","singulo") else - to_chat(user, "The controls are locked!") + active = TRUE + if(user) + operator_skill = user.get_skill_value(core_skill) + update_efficiency() + to_chat(user, SPAN_NOTICE("You turn on \the [src].")) + shot_number = 0 + fire_delay = get_initial_fire_delay() + log_and_message_admins("turned on \the [src]", user) + investigate_log("turned on by [key_name_admin(user)]","singulo") + update_icon() else - to_chat(user, "\The [src] needs to be firmly secured to the floor first.") - return 1 + to_chat(user, SPAN_WARNING("The controls are locked!")) /obj/machinery/emitter/proc/update_efficiency() efficiency = initial(efficiency) @@ -99,11 +102,11 @@ /obj/machinery/emitter/Process() if(stat & (BROKEN)) return - if(state != 2) + if(!can_fire()) active = FALSE update_icon() return - if(((last_shot + fire_delay) <= world.time) && (active == 1)) + if(((last_shot + fire_delay) <= world.time) && active) if(active_power_usage - can_use_power_oneoff(active_power_usage) < minimum_power) powered = FALSE update_icon() @@ -111,10 +114,10 @@ var/drawn_power = min(active_power_usage, active_power_usage - use_power_oneoff(active_power_usage)) last_shot = world.time if(shot_number < burst_shots) - fire_delay = get_burst_delay() + fire_delay = get_shot_delay() shot_number ++ else - fire_delay = get_rand_burst_delay() + fire_delay = get_burst_delay() shot_number = 0 //need to calculate the power per shot as the emitter doesn't fire continuously. @@ -134,66 +137,6 @@ update_icon() /obj/machinery/emitter/attackby(obj/item/used_item, mob/user) - - if(IS_WRENCH(used_item)) - if(active) - to_chat(user, "Turn off [src] first.") - return TRUE - switch(state) - if(0) - state = 1 - playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) - user.visible_message("[user.name] secures [src] to the floor.", \ - "You secure the external reinforcing bolts to the floor.", \ - "You hear a ratchet.") - anchored = TRUE - if(1) - state = 0 - playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) - user.visible_message("[user.name] unsecures [src] reinforcing bolts from the floor.", \ - "You undo the external reinforcing bolts.", \ - "You hear a ratchet.") - anchored = FALSE - if(2) - to_chat(user, "\The [src] needs to be unwelded from the floor.") - return TRUE - - if(IS_WELDER(used_item)) - var/obj/item/weldingtool/welder = used_item - if(active) - to_chat(user, "Turn off [src] first.") - return TRUE - switch(state) - if(0) - to_chat(user, "\The [src] needs to be wrenched to the floor.") - if(1) - if (!welder.weld(0,user)) - to_chat(user, "You need more welding fuel to complete this task.") - return TRUE - playsound(loc, 'sound/items/Welder2.ogg', 50, 1) - user.visible_message("[user.name] starts to weld [src] to the floor.", \ - "You start to weld [src] to the floor.", \ - "You hear welding.") - if (!do_after(user, 2 SECONDS, src)) - return TRUE - if(!src || !welder.isOn()) return TRUE - state = 2 - to_chat(user, "You weld [src] to the floor.") - if(2) - if (welder.weld(0,user)) - playsound(loc, 'sound/items/Welder2.ogg', 50, 1) - user.visible_message("[user.name] starts to cut [src] free from the floor.", \ - "You start to cut [src] free from the floor.", \ - "You hear welding.") - if (!do_after(user, 2 SECONDS, src)) - return TRUE - if(!src || !welder.isOn()) return TRUE - state = 1 - to_chat(user, "You cut [src] free from the floor.") - else - to_chat(user, "You need more welding fuel to complete this task.") - return TRUE - if(istype(used_item, /obj/item/card/id) || istype(used_item, /obj/item/modular_computer)) if(emagged) to_chat(user, "The lock seems to be broken.") @@ -208,8 +151,8 @@ /obj/machinery/emitter/emag_act(var/remaining_charges, var/mob/user) if(!emagged) - locked = 0 - emagged = 1 + locked = FALSE + emagged = TRUE req_access.Cut() user.visible_message("[user.name] emags [src].","You short out the lock.") return 1 @@ -220,13 +163,15 @@ return ..() /obj/machinery/emitter/proc/get_initial_fire_delay() - return 100 + return 10 SECONDS -/obj/machinery/emitter/proc/get_rand_burst_delay() +/// The number of deciseconds between each burst-fire grouping. +/obj/machinery/emitter/proc/get_burst_delay() return rand(min_burst_delay, max_burst_delay) -/obj/machinery/emitter/proc/get_burst_delay() - return 2 +/// The number of deciseconds between each shot in a burst. +/obj/machinery/emitter/proc/get_shot_delay() + return 0.2 SECONDS /obj/machinery/emitter/proc/get_emitter_beam() return new /obj/item/projectile/beam/emitter(get_turf(src)) diff --git a/code/game/machinery/message_server.dm b/code/game/machinery/message_server.dm index 9ddf2a4d9f3..8fbc655edc4 100644 --- a/code/game/machinery/message_server.dm +++ b/code/game/machinery/message_server.dm @@ -48,22 +48,10 @@ var/global/list/message_servers = list() active_power_usage = 100 var/list/datum/data_rc_msg/rc_msgs = list() - var/active = 1 + var/active = TRUE var/power_failure = 0 // Reboot timer after power outage var/decryptkey = "password" - /// Spam filtering stuff. Messages having theese tokens will be rejected by server. Case sensitive. - var/list/spamfilter = list( - "You have won", - "your prize", - "male enhancement", - "shitcurity", - "are happy to inform you", - "account number", - "enter your PIN" - ) - var/spamfilter_limit = MESSAGE_SERVER_DEFAULT_SPAM_LIMIT //Maximal amount of tokens - stat_immune = 0 uncreated_component_parts = null construct_state = /decl/machine_construction/default/panel_closed @@ -80,7 +68,7 @@ var/global/list/message_servers = list() /obj/machinery/network/message_server/Process() ..() if(active && (stat & (BROKEN|NOPOWER))) - active = 0 + active = FALSE power_failure = 10 update_icon() return @@ -88,7 +76,7 @@ var/global/list/message_servers = list() return else if(power_failure > 0) if(!(--power_failure)) - active = 1 + active = TRUE update_icon() /obj/machinery/network/message_server/proc/send_rc_message(var/recipient = "",var/sender = "",var/message = "",var/stamp = "", var/id_auth = "", var/priority = 1) @@ -99,6 +87,8 @@ var/global/list/message_servers = list() if (stamp) authmsg += "[stamp]
" . = FALSE + if(!active) + return // message suppressed but still saved on the message server var/datum/extension/network_device/network_device = get_extension(src, /datum/extension/network_device) var/datum/computer_network/network = network_device?.get_network() @@ -129,22 +119,12 @@ var/global/list/message_servers = list() /obj/machinery/network/message_server/interface_interact(mob/user) if(!CanInteract(user, DefaultTopicState())) return FALSE - to_chat(user, "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") + to_chat(user, "You toggle message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") active = !active power_failure = 0 update_icon() return TRUE -/obj/machinery/network/message_server/attackby(obj/item/used_item, mob/user) - if (active && !(stat & (BROKEN|NOPOWER)) && (spamfilter_limit < MESSAGE_SERVER_DEFAULT_SPAM_LIMIT*2) && \ - istype(used_item,/obj/item/stock_parts/circuitboard/message_monitor)) - spamfilter_limit += round(MESSAGE_SERVER_DEFAULT_SPAM_LIMIT / 2) - qdel(used_item) - to_chat(user, "You install additional memory and processors into \the [src]. Its filtering capabilities been enhanced.") - return TRUE - else - return ..() - /obj/machinery/network/message_server/on_update_icon() icon_state = initial(icon_state) if(panel_open) diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 3452f128f8b..681270aa8cb 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -107,7 +107,7 @@ var/global/req_console_information = list() data["message"] = message data["recipient"] = recipient - data["priortiy"] = priority + data["priority"] = priority data["msgStamped"] = msgStamped data["msgVerified"] = msgVerified data["announceAuth"] = announceAuth diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index feebd929384..6bde88df188 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -21,8 +21,6 @@ var/in_use = FALSE // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! var/armor_penetration = 0 var/anchor_fall = FALSE - /// if the obj is a holographic object spawned by the holodeck - var/holographic = FALSE ///JSON list of directions to x,y offsets to be applied to the object depending on its direction EX: @'{"NORTH":{"x":12,"y":5}, "EAST":{"x":10,"y":50}}' var/directional_offset @@ -75,6 +73,23 @@ /obj/return_air() return loc?.return_air() +/obj/proc/get_internal_pressure_difference() + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + return int_air.return_pressure()-env_air.return_pressure() + +/// Return TRUE if the internal pressure difference is over `limit`. +/obj/proc/check_internal_pressure_difference_over(limit) + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + return (int_air.return_pressure()-env_air.return_pressure()) > limit + +/// Return TRUE if the internal pressure difference is under `limit`. +/obj/proc/check_internal_pressure_difference_under(limit) + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + return (int_air.return_pressure()-env_air.return_pressure()) < limit + /obj/proc/updateUsrDialog() if(in_use) var/is_in_use = 0 diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 1bed5d4cf0f..fd5e22b082c 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -1203,19 +1203,6 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. /obj/item/proc/has_textile_fibers() return FALSE -// Returns a value used as a multiplier in the fishing delay calc. Higher represents a stronger reduction in fishing time. -#define BAIT_VALUE_CONSTANT 0.1 -/obj/item/proc/get_bait_value() - . = 0 - for(var/mat in matter) - var/decl/material/bait_mat = GET_DECL(mat) - if(bait_mat.fishing_bait_value) - . += MATERIAL_UNITS_TO_REAGENTS_UNITS(matter[mat]) * bait_mat.fishing_bait_value * BAIT_VALUE_CONSTANT - for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) - if(reagent.fishing_bait_value) - . += REAGENT_VOLUME(reagents, reagent) * reagent.fishing_bait_value * BAIT_VALUE_CONSTANT -#undef BAIT_VALUE_CONSTANT - /obj/item/proc/get_storage_cost() //If you want to prevent stuff above a certain w_class from being stored, use max_w_class return BASE_STORAGE_COST(w_class) @@ -1353,4 +1340,26 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. qdel(src) /obj/item/proc/pick_attack_verb() - return DEFAULTPICK(attack_verb, attack_verb) || "attacked" // if it's not a list, return itself or just "attacked" \ No newline at end of file + return DEFAULTPICK(attack_verb, attack_verb) || "attacked" // if it's not a list, return itself or just "attacked" + +/obj/item/equipped(mob/user, slot) + if(user?.get_active_held_item() == src) + user.on_mouse_up() + . = ..() + +/obj/item/dropped(mob/user) + if(user?.get_active_held_item() == src) + user.on_mouse_up() + . = ..() + +// Called on initial mouse down event from wielding mob. Return TRUE to begin processing every 1ds. +/obj/item/proc/wielder_mouse_drag_down(mob/user, object, location, control, params) + return FALSE + +// Called every 1ds while mouse is down with an item that returned TRUE to wielder_mouse_drag_down(). Return FALSE to end processing. +/obj/item/proc/wielder_mouse_drag_held(mob/user, atom/target) + return FALSE + +// Called on mouse up event from wielding mob. +/obj/item/proc/wielder_mouse_drag_up(mob/user, atom/target) + return FALSE diff --git a/code/game/objects/items/books/skill/_skill.dm b/code/game/objects/items/books/skill/_skill.dm index 39ed2b3cdfe..7f940ad6367 100644 --- a/code/game/objects/items/books/skill/_skill.dm +++ b/code/game/objects/items/books/skill/_skill.dm @@ -113,6 +113,8 @@ Skill books that increase your skills while you activate and hold them limit = 1 // you can only read one book at a time nerd, therefore you can only get one buff at a time /obj/item/book/skill/get_single_monetary_worth() + if(worthless) + return 0 . = max(..(), 200) + (100 * skill_req) /obj/item/book/skill/proc/check_can_read(mob/user) diff --git a/code/game/objects/items/circuitboards/machinery/power.dm b/code/game/objects/items/circuitboards/machinery/power.dm index d8cdc9faab6..7bb30358f80 100644 --- a/code/game/objects/items/circuitboards/machinery/power.dm +++ b/code/game/objects/items/circuitboards/machinery/power.dm @@ -29,7 +29,7 @@ /obj/item/stock_parts/circuitboard/breaker name = "circuitboard (breaker box)" - build_path = /obj/machinery/power/breakerbox + build_path = /obj/machinery/breakerbox board_type = "machine" origin_tech = @'{"powerstorage":4,"engineering":4}' req_components = list( diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index fc743829cee..83f05a1b7de 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -102,7 +102,7 @@ density = FALSE anchored = TRUE is_spawnable_type = FALSE - movement_handlers = list(/datum/movement_handler/delay/chameleon_projector) + movement_handlers = list(/datum/movement_handler/delay/chameleon_projector = list(2.5 SECONDS)) var/obj/item/chameleon/master = null /obj/effect/dummy/chameleon/Initialize(mapload, var/obj/item/chameleon/projector) @@ -152,9 +152,6 @@ if(!my_turf.get_supporting_platform() && !(locate(/obj/structure/lattice) in loc)) disrupted() -/datum/movement_handler/delay/chameleon_projector - delay = 2.5 SECONDS - /datum/movement_handler/delay/chameleon_projector/MayMove(mob/mover, is_external) return host.loc?.has_gravity() ? ..() : MOVEMENT_STOP diff --git a/code/game/objects/items/paintkit.dm b/code/game/objects/items/paintkit.dm index 5d8602efb26..27857f5ec34 100644 --- a/code/game/objects/items/paintkit.dm +++ b/code/game/objects/items/paintkit.dm @@ -11,6 +11,8 @@ var/custom = FALSE /obj/item/kit/get_single_monetary_worth() + if(worthless) + return 0 . = max(round(..()), (custom ? 100 : 750) * uses) // Luxury good, value is entirely artificial. /obj/item/kit/get_examine_strings(mob/user, distance, infix, suffix) diff --git a/code/game/objects/items/weapons/grenades/prank_grenades.dm b/code/game/objects/items/weapons/grenades/prank_grenades.dm index 0a884f850ec..1a04949c921 100644 --- a/code/game/objects/items/weapons/grenades/prank_grenades.dm +++ b/code/game/objects/items/weapons/grenades/prank_grenades.dm @@ -4,20 +4,3 @@ /obj/item/grenade/fake/detonate() active = 0 playsound(src.loc, get_sfx("explosion"), 50, 1, 30) - -/obj/item/natural_weapon/bite/fake - _base_attack_force = 0 - -/mob/living/simple_animal/hostile/carp/holodeck/fake - faction = null - natural_weapon = /obj/item/natural_weapon/bite/fake - environment_smash = 0 - ai = /datum/mob_controller/aggressive/carp/fake - -/datum/mob_controller/aggressive/carp/fake - try_destroy_surroundings = FALSE - -/obj/item/grenade/spawnergrenade/fake_carp - origin_tech = @'{"materials":2,"magnets":2,"wormholes":5}' - spawner_type = /mob/living/simple_animal/hostile/carp/holodeck/fake - deliveryamt = 4 diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index a636763ac07..4d15f289bd6 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -6,7 +6,7 @@ var/spawner_type = null // must be an object path var/deliveryamt = 1 // amount of type to deliver -/obj/item/grenade/spawnergrenade/fake_carp/detonate() +/obj/item/grenade/spawnergrenade/detonate() if(spawner_type && deliveryamt) var/turf/T = get_turf(src) playsound(T, 'sound/effects/phasein.ogg', 100, 1) diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm index fb7ff59d404..669014d7b9e 100644 --- a/code/game/objects/items/weapons/storage/secure.dm +++ b/code/game/objects/items/weapons/storage/secure.dm @@ -31,12 +31,12 @@ /obj/item/secure_storage/Initialize(ml, material_key) var/datum/extension/lockable/mylock = get_or_create_extension(src, lock_type) - events_repository.register(/decl/observ/lock_state_changed, mylock, src, /obj/item/secure_storage/proc/on_lock_state_changed) + events_repository.register(/decl/observ/lock_state_changed, mylock, src, PROC_REF(on_lock_state_changed)) . = ..() /obj/item/secure_storage/Destroy() var/datum/extension/lockable/mylock = get_extension(src, lock_type) - events_repository.unregister(/decl/observ/lock_state_changed, mylock, src, /obj/item/secure_storage/proc/on_lock_state_changed) + events_repository.unregister(/decl/observ/lock_state_changed, mylock, src, PROC_REF(on_lock_state_changed)) . = ..() /obj/item/secure_storage/proc/on_lock_state_changed(datum/extension/lockable/L, old_locked, new_locked) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index b191b55e56d..cef5d34401a 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -79,6 +79,8 @@ var/global/list/global/tank_gauge_cache = list() . = ..() /obj/item/tank/get_single_monetary_worth() + if(worthless) + return 0 . = ..() for(var/gas_type, gas_amount in air_contents?.gas) var/decl/material/gas_data = GET_DECL(gas_type) diff --git a/code/game/objects/items/weapons/tech_disks.dm b/code/game/objects/items/weapons/tech_disks.dm index c33fceca35b..0e87df82301 100644 --- a/code/game/objects/items/weapons/tech_disks.dm +++ b/code/game/objects/items/weapons/tech_disks.dm @@ -142,4 +142,4 @@ . += "A tiny indicator on \the [src] shows it holds [data] good explorer point\s." /obj/item/disk/survey/get_base_value() - . = holographic ? 0 : (sqrt(data) * 5) + return sqrt(data) * 5 diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index 7184dc4797b..a92ab8414fd 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -64,7 +64,7 @@ . = ..() update_materials() paint_verb ||= "painted" // fallback for the case of no material - if(lock && !istype(loc)) + if(lock && !istype(lock)) lock = new /datum/lock(src, lock) if(!CanFluidPass()) fluid_update(TRUE) diff --git a/code/game/objects/structures/beds/bedroll.dm b/code/game/objects/structures/beds/bedroll.dm index 27971d1b66f..7b64a94f85f 100644 --- a/code/game/objects/structures/beds/bedroll.dm +++ b/code/game/objects/structures/beds/bedroll.dm @@ -59,14 +59,14 @@ /obj/structure/bed/bedroll/show_buckle_message(var/mob/buckled, var/mob/buckling) if(buckled == buckling) - visible_message( + buckled.visible_message( SPAN_NOTICE("\The [buckled] climbs into \the [src]."), SPAN_NOTICE("You climb into \the [src]."), SPAN_NOTICE("You hear a rustling sound.") ) else var/decl/pronouns/pronouns = buckled.get_pronouns() - visible_message( + buckled.visible_message( SPAN_NOTICE("\The [buckled] [pronouns.is] bundled into \the [src] by \the [buckling]."), SPAN_NOTICE("You are bundled into \the [src] by \the [buckling]."), SPAN_NOTICE("You hear a rustling sound.") @@ -74,13 +74,13 @@ /obj/structure/bed/bedroll/show_unbuckle_message(var/mob/buckled, var/mob/buckling) if(buckled == buckling) - visible_message( + buckled.visible_message( SPAN_NOTICE("\The [buckled] climbs out of \the [src]."), SPAN_NOTICE("You climb out of \the [src]."), SPAN_NOTICE("You hear a rustling sound.") ) else - visible_message( + buckled.visible_message( SPAN_NOTICE("\The [buckled] was pulled out of \the [src] by \the [buckling]."), SPAN_NOTICE("You were pulled out of \the [src] by \the [buckling]."), SPAN_NOTICE("You hear a rustling sound.") diff --git a/code/game/objects/structures/beds/rollerbed.dm b/code/game/objects/structures/beds/rollerbed.dm index d7e7f102ec6..739c85b1f23 100644 --- a/code/game/objects/structures/beds/rollerbed.dm +++ b/code/game/objects/structures/beds/rollerbed.dm @@ -136,6 +136,8 @@ var/structure_form_type = /obj/structure/bed/roller //The deployed form path. /obj/item/roller/get_single_monetary_worth() + if(worthless) + return 0 . = structure_form_type ? atom_info_repository.get_combined_worth_for(structure_form_type) : ..() /obj/item/roller/attack_self(mob/user) diff --git a/code/game/objects/structures/fences.dm b/code/game/objects/structures/fences.dm deleted file mode 100644 index 1ed302b304a..00000000000 --- a/code/game/objects/structures/fences.dm +++ /dev/null @@ -1,195 +0,0 @@ -//Chain link fences -//Sprites ported from /VG/ - -#define CUT_TIME 10 SECONDS -#define CLIMB_TIME 5 SECONDS - -///section is intact -#define NO_HOLE 0 -///medium hole in the section - can climb through -#define MEDIUM_HOLE 1 -///large hole in the section - can walk through -#define LARGE_HOLE 2 -#define MAX_HOLE_SIZE LARGE_HOLE - -/obj/structure/fence - name = "fence" - desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out." - density = TRUE - anchored = TRUE - - icon = 'icons/obj/structures/fence.dmi' - icon_state = "straight" - - material = /decl/material/solid/metal/steel - material_alteration = MAT_FLAG_ALTERATION_ALL - tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT - - var/cuttable = TRUE - var/hole_size = NO_HOLE - -/obj/structure/fence/Initialize(mapload) - update_cut_status() - return ..() - -/obj/structure/fence/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - switch(hole_size) - if(MEDIUM_HOLE) - . += SPAN_DANGER("There is a large hole in \the [src].") - if(LARGE_HOLE) - . += SPAN_DANGER("\The [src] has been completely cut through.") - -/obj/structure/fence/get_examine_hints(mob/user, distance, infix, suffix) - . = ..() - if(cuttable && hole_size < MAX_HOLE_SIZE) - LAZYADD(., SPAN_SUBTLE("Use wirecutters to [hole_size > NO_HOLE ? "expand the":"cut a"] hole into the fence, allowing passage.")) - -/obj/structure/fence/end - icon_state = "end" - cuttable = FALSE - -/obj/structure/fence/corner - icon_state = "corner" - cuttable = FALSE - -/obj/structure/fence/post - icon_state = "post" - cuttable = FALSE - -/obj/structure/fence/cut/medium - icon_state = "straight-cut2" - hole_size = MEDIUM_HOLE - -/obj/structure/fence/cut/large - icon_state = "straight-cut3" - hole_size = LARGE_HOLE - -// Projectiles can pass through fences. -/obj/structure/fence/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(mover?.checkpass(PASS_FLAG_TABLE)) - return TRUE - if(hole_size == MEDIUM_HOLE && issmall(mover)) - return TRUE - return ..() - -/obj/structure/fence/can_repair(mob/user) - if(hole_size > NO_HOLE) - return TRUE - return ..() - -/obj/structure/fence/handle_repair(mob/user, obj/item/used_item) - var/obj/item/stack/stack = used_item - if(hole_size > NO_HOLE && istype(stack)) - to_chat(user, SPAN_NOTICE("You fit [stack.get_string_for_amount(1)] to damaged areas of \the [src].")) - stack.use(1) - hole_size = NO_HOLE - update_cut_status() - return TRUE - return ..() - - -/obj/structure/fence/attackby(obj/item/used_item, mob/user) - if(IS_WIRECUTTER(used_item)) - if(!cuttable) - to_chat(user, SPAN_WARNING("This section of the fence can't be cut.")) - return TRUE - var/current_stage = hole_size - if(current_stage >= MAX_HOLE_SIZE) - to_chat(user, SPAN_NOTICE("This fence has too much cut out of it already.")) - return TRUE - - if(used_item.do_tool_interaction(TOOL_WIRECUTTERS, user, src, CUT_TIME, "cutting through", "cutting through", check_skill = FALSE) && current_stage == hole_size) // do_tool_interaction sleeps, so make sure it hasn't been cut more while we waited - switch(++hole_size) - if(MEDIUM_HOLE) - user.visible_message( - SPAN_NOTICE("\The [user] cuts into \the [src] some more."), - SPAN_NOTICE("Someone could probably fit through that hole now, although climbing through would be much faster if it were even bigger.") - ) - if(LARGE_HOLE) - user.visible_message( - SPAN_NOTICE("\The [user] completely cuts through \the [src]."), - SPAN_NOTICE("The hole in \the [src] is now big enough to walk through.") - ) - update_cut_status() - return TRUE - return ..() - -/obj/structure/fence/proc/update_cut_status() - if(!cuttable) - return - density = TRUE - - switch(hole_size) - if(NO_HOLE) - icon_state = initial(icon_state) - if(MEDIUM_HOLE) - icon_state = "[initial(icon_state)]-cut2" - if(LARGE_HOLE) - icon_state = "[initial(icon_state)]-cut3" - density = FALSE - -//FENCE DOORS - -/obj/structure/fence/door - name = "fence door" - desc = "Not very useful without a real lock." - icon_state = "door-closed" - cuttable = FALSE - var/open = FALSE - var/locked = FALSE - -/obj/structure/fence/door/Initialize(mapload) - update_door_status() - return ..() - -/obj/structure/fence/door/opened - icon_state = "door-opened" - open = TRUE - density = TRUE - -/obj/structure/fence/door/locked - desc = "It looks like it has a strong padlock attached." - locked = TRUE - -/obj/structure/fence/door/attack_hand(mob/user, list/params) - SHOULD_CALL_PARENT(FALSE) - if(can_open(user)) - toggle(user) - else - to_chat(user, SPAN_WARNING("\The [src] is [!open ? "locked" : "stuck open"].")) - return TRUE - -/obj/structure/fence/door/proc/toggle(mob/user) - switch(open) - if(FALSE) - visible_message(SPAN_NOTICE("\The [user] opens \the [src].")) - open = TRUE - if(TRUE) - visible_message(SPAN_NOTICE("\The [user] closes \the [src].")) - open = FALSE - - update_door_status() - playsound(src, 'sound/machines/click.ogg', 100, 1) - -/obj/structure/fence/door/proc/update_door_status() - switch(open) - if(FALSE) - density = TRUE - icon_state = "door-closed" - if(TRUE) - density = FALSE - icon_state = "door-opened" - -/obj/structure/fence/door/proc/can_open(mob/user) - if(locked) - return FALSE - return TRUE - -#undef CUT_TIME -#undef CLIMB_TIME - -#undef NO_HOLE -#undef MEDIUM_HOLE -#undef LARGE_HOLE -#undef MAX_HOLE_SIZE \ No newline at end of file diff --git a/code/game/objects/structures/fences/_fences.dm b/code/game/objects/structures/fences/_fences.dm new file mode 100644 index 00000000000..9bc566ec9e8 --- /dev/null +++ b/code/game/objects/structures/fences/_fences.dm @@ -0,0 +1,308 @@ +// Various fences +// Chain link sprites ported from /VG/ +// Stone, stick, plank and palisade sprites by Doe. + + +/obj/structure/fence + name = "fence" + desc = "A fence. Not as effective as a wall, but generally it keeps people out." + density = TRUE + anchored = TRUE + icon = /decl/fence_type::fence_icon + icon_state = /decl/fence_type::straight_state + material = /decl/material/solid/metal/steel + atom_flags = ATOM_FLAG_CLIMBABLE + material_alteration = MAT_FLAG_ALTERATION_ALL + tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT + + var/decl/fence_type/fence_data = /decl/fence_type + var/hole_size = NO_HOLE + var/connected_dirs = 0 + + var/const/CUT_TIME = 10 SECONDS + ///section is intact + var/const/NO_HOLE = 0 + ///medium hole in the section - can climb through + var/const/MEDIUM_HOLE = 1 + ///large hole in the section - can walk through + var/const/LARGE_HOLE = 2 + var/const/MAX_HOLE_SIZE = LARGE_HOLE + +/obj/structure/fence/Destroy() + var/turf/prior_loc = loc + . = ..() + if(istype(prior_loc)) + for(var/check_dir in global.cardinal) + for(var/obj/structure/fence/fence in get_step_resolving_mimic(prior_loc, check_dir)) + fence.update_icon() + +/obj/structure/fence/Initialize(ml, _mat, _reinf_mat) + if(ispath(fence_data)) + fence_data = GET_DECL(fence_data) + set_icon(fence_data.fence_icon) + else if(!istype(fence_data)) + fence_data = null + . = ..() + update_cut_status() + if(ml) + queue_icon_update() + else + return INITIALIZE_HINT_LATELOAD + +/obj/structure/fence/LateInitialize() + . = ..() + update_icon() + for(var/check_dir in global.cardinal) + var/turf/neighbor = get_step_resolving_mimic(get_turf(src), check_dir) + if(istype(neighbor)) + for(var/obj/structure/fence/fence in neighbor) + if(fence_data == RESOLVE_TO_DECL(fence.fence_data)) + fence.update_icon() + +/obj/structure/fence/update_material_name(override_name) + override_name ||= fence_data.name + . = ..() + +/obj/structure/fence/update_material_desc(override_desc) + override_desc ||= fence_data.desc + . = ..() + +/obj/structure/fence/on_update_icon() + . = ..() + if(istype(fence_data)) + update_fence_connections() + update_fence_icon() + +/obj/structure/fence/proc/is_fencepost() + return FALSE // TODO: detect doors and junctions next to us. + +/obj/structure/fence/proc/update_fence_connections() + // Find any adjacent fences. + connected_dirs = 0 + var/turf/my_turf = get_turf(src) + for(var/check_dir in global.cardinal) + var/turf/neighbor = get_step_resolving_mimic(my_turf, check_dir) + if(!istype(neighbor)) + continue + for(var/obj/structure/fence/fence in neighbor) + if(fence_data == RESOLVE_TO_DECL(fence.fence_data)) + connected_dirs |= check_dir + break + +/obj/structure/fence/proc/update_fence_icon() + + // Standalone segment. + if(!connected_dirs) + set_icon_state(fence_data.single_state) + + // Four-way junction. + else if(connected_dirs == (NORTH|SOUTH|EAST|WEST)) + set_icon_state(fence_data.four_way_state) + + // End segments. + else if(connected_dirs == NORTH || connected_dirs == SOUTH || connected_dirs == EAST || connected_dirs == WEST) + set_dir(connected_dirs) + set_icon_state(fence_data.end_state) + + // Straight segments. + else if(connected_dirs == (NORTH | SOUTH) || connected_dirs == (EAST | WEST)) + if(connected_dirs & NORTH) + set_dir(NORTH) + else + set_dir(EAST) + if(hole_size > 0) + set_icon_state("[fence_data.straight_state]-cut[hole_size]") + else if(is_fencepost()) + set_icon_state(fence_data.post_state) + else + set_icon_state(fence_data.straight_state) + + // Corner segments. + else if(connected_dirs in global.cornerdirs) + set_icon_state(fence_data.corner_state) + var/static/list/_corner_fence_to_state_mapping = alist( + (NORTHWEST) = SOUTH, + (NORTHEAST) = NORTH, + (SOUTHWEST) = EAST, + (SOUTHEAST) = WEST + ) + set_dir(_corner_fence_to_state_mapping[connected_dirs]) + + // Junction segments. + else + set_icon_state(fence_data.three_way_state) + for(var/check_dir in global.cardinal) + if(!(connected_dirs & check_dir)) + set_dir(check_dir) + break + +/obj/structure/fence/proc/is_cuttable() + return icon_state == fence_data.straight_state && hole_size < MAX_HOLE_SIZE + +/obj/structure/fence/get_examine_strings(mob/user, distance, infix, suffix) + . = ..() + switch(hole_size) + if(MEDIUM_HOLE) + . += SPAN_DANGER("There is a large hole in \the [src].") + if(LARGE_HOLE) + . += SPAN_DANGER("\The [src] has been completely cut through.") + +/obj/structure/fence/get_examine_hints(mob/user, distance, infix, suffix) + . = ..() + if(is_cuttable()) + LAZYADD(., SPAN_SUBTLE("Use wirecutters to [hole_size > NO_HOLE ? "expand the":"cut a"] hole into the fence, allowing passage.")) + +/obj/structure/fence/cut/medium + icon_state = "straight-cut2" + hole_size = MEDIUM_HOLE + +/obj/structure/fence/cut/large + icon_state = "straight-cut3" + hole_size = LARGE_HOLE + +// Projectiles can pass through fences. +/obj/structure/fence/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(mover?.checkpass(PASS_FLAG_TABLE)) + return TRUE + if(hole_size >= MEDIUM_HOLE && issmall(mover)) + return TRUE + return ..() + +/obj/structure/fence/can_repair(mob/user) + if(hole_size > NO_HOLE) + return TRUE + return ..() + +/obj/structure/fence/handle_repair(mob/user, obj/item/used_item) + var/obj/item/stack/stack = used_item + if(hole_size > NO_HOLE && istype(stack)) + to_chat(user, SPAN_NOTICE("You fit [stack.get_string_for_amount(1)] to damaged areas of \the [src].")) + stack.use(1) + hole_size = NO_HOLE + update_cut_status() + return TRUE + return ..() + +/obj/structure/fence/attackby(obj/item/used_item, mob/user) + if(IS_WIRECUTTER(used_item)) + if(!is_cuttable()) + to_chat(user, SPAN_WARNING("This section of the fence can't be cut.")) + return TRUE + var/current_stage = hole_size + if(current_stage >= MAX_HOLE_SIZE) + to_chat(user, SPAN_NOTICE("This fence has too much cut out of it already.")) + return TRUE + + if(used_item.do_tool_interaction(TOOL_WIRECUTTERS, user, src, CUT_TIME, "cutting through", "cutting through", check_skill = FALSE) && current_stage == hole_size) // do_tool_interaction sleeps, so make sure it hasn't been cut more while we waited + switch(++hole_size) + if(MEDIUM_HOLE) + user.visible_message( + SPAN_NOTICE("\The [user] cuts into \the [src] some more."), + SPAN_NOTICE("Someone could probably fit through that hole now, although climbing through would be much faster if it were even bigger.") + ) + if(LARGE_HOLE) + user.visible_message( + SPAN_NOTICE("\The [user] completely cuts through \the [src]."), + SPAN_NOTICE("The hole in \the [src] is now big enough to walk through.") + ) + update_cut_status() + return TRUE + return ..() + +/obj/structure/fence/proc/update_cut_status() + if(!is_cuttable()) + return + density = TRUE + switch(hole_size) + if(NO_HOLE) + icon_state = initial(icon_state) + if(MEDIUM_HOLE) + icon_state = "[initial(icon_state)]-cut2" + if(LARGE_HOLE) + icon_state = "[initial(icon_state)]-cut3" + density = FALSE + +//FENCE DOORS +/obj/structure/fence/door + name = "fence gate" + desc = "Much like a regular door, but thinner." + icon_state = "door-closed" + +/obj/structure/fence/door/can_install_lock() + return TRUE + +/obj/structure/fence/door/update_material_name(override_name) + override_name ||= fence_data.door_name + . = ..() + +/obj/structure/fence/door/update_material_desc(override_desc) + override_desc ||= fence_data.door_desc + . = ..() + return INITIALIZE_HINT_LATELOAD + +/obj/structure/fence/door/update_fence_icon() + if(!istype(fence_data)) + return + if((connected_dirs & NORTH) || (connected_dirs & SOUTH)) + set_dir(NORTH) + else + set_dir(EAST) + if(density) + set_icon_state(fence_data.door_state_closed) + else + set_icon_state(fence_data.door_state_opened) + +/obj/structure/fence/door/opened + icon_state = "door-opened" + density = TRUE + +/obj/structure/fence/door/locked/Initialize(mapload) + lock ||= "fence key #[random_id(type, 10000, 99999)]" + . = ..() + +/obj/structure/fence/door/attack_hand(mob/user, list/params) + SHOULD_CALL_PARENT(FALSE) + if(!density || can_open(user)) + density = !density + visible_message(SPAN_NOTICE("\The [user] [density ? "opens" : "closes"] \the [src].")) + playsound(src, 'sound/machines/click.ogg', 100, 1) + update_icon() + else + to_chat(user, SPAN_WARNING("\The [src] is locked.")) + return TRUE + +/obj/structure/fence/door/proc/can_open(mob/user) + return !lock || !lock.isLocked() + +// Mapping/crafting helpers. +/obj/structure/fence/brick + icon_state = /decl/fence_type/brick::straight_state + fence_data = /decl/fence_type/brick + +/obj/structure/fence/door/brick + icon_state = /decl/fence_type/brick::door_state_closed + fence_data = /decl/fence_type/brick + +/obj/structure/fence/palisade + icon_state = /decl/fence_type/palisade::straight_state + fence_data = /decl/fence_type/palisade + +/obj/structure/fence/door/palisade + icon_state = /decl/fence_type/palisade::door_state_closed + fence_data = /decl/fence_type/palisade + +/obj/structure/fence/stick + icon_state = /decl/fence_type/stick::straight_state + fence_data = /decl/fence_type/stick + +/obj/structure/fence/door/stick + icon_state = /decl/fence_type/stick::door_state_closed + fence_data = /decl/fence_type/stick + +/obj/structure/fence/plank + icon_state = /decl/fence_type/plank::straight_state + fence_data = /decl/fence_type/plank + +/obj/structure/fence/door/plank + icon_state = /decl/fence_type/plank::door_state_closed + fence_data = /decl/fence_type/plank diff --git a/code/game/objects/structures/fences/fence_types.dm b/code/game/objects/structures/fences/fence_types.dm new file mode 100644 index 00000000000..029df039000 --- /dev/null +++ b/code/game/objects/structures/fences/fence_types.dm @@ -0,0 +1,61 @@ +/decl/fence_type + var/name = "chain link fence" + var/desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out." + var/door_name = "fence gate" + var/door_desc = "Much like a regular door, but thinner." + var/fence_icon = 'icons/obj/structures/fences/chain.dmi' + var/single_state = "single" + var/corner_state = "corner" + var/straight_state = "straight" + var/post_state = "post" + var/end_state = "end" + var/three_way_state = "three_way" + var/four_way_state = "four_way" + var/door_state_closed = "door-opened" + var/door_state_opened = "door-closed" + +/decl/fence_type/validate() + . = ..() + + if(!fence_icon) + . += "missing fence icon" + return + + if(!single_state || !check_state_in_icon(single_state, fence_icon)) + . += "missing or invalid single_state '[single_state]' from '[fence_icon]'" + if(!straight_state || !check_state_in_icon(straight_state, fence_icon)) + . += "missing or invalid straight_state '[straight_state]' from '[fence_icon]'" + if(!corner_state || !check_state_in_icon(corner_state, fence_icon)) + . += "missing or invalid corner_state '[corner_state]' from '[fence_icon]'" + if(!post_state || !check_state_in_icon(post_state, fence_icon)) + . += "missing or invalid post_state '[post_state]' from '[fence_icon]'" + if(!end_state || !check_state_in_icon(end_state, fence_icon)) + . += "missing or invalid end_state '[end_state]' from '[fence_icon]'" + if(!door_state_closed || !check_state_in_icon(door_state_closed, fence_icon)) + . += "missing or invalid door_state_closed '[door_state_closed]' from '[fence_icon]'" + if(!door_state_opened || !check_state_in_icon(door_state_opened, fence_icon)) + . += "missing or invalid door_state_opened '[door_state_opened]' from '[fence_icon]'" + if(!three_way_state || !check_state_in_icon(three_way_state, fence_icon)) + . += "missing or invalid three_way_state '[three_way_state]' from '[fence_icon]'" + if(!four_way_state || !check_state_in_icon(four_way_state, fence_icon)) + . += "missing or invalid four_way_state '[four_way_state]' from '[fence_icon]'" + +/decl/fence_type/brick + name = "brick fence" + desc = "A brick fence. Not as effective as a wall, but generally it keeps people out." + fence_icon = 'icons/obj/structures/fences/brick.dmi' + +/decl/fence_type/palisade + name = "palisade" + desc = "A tall and imposing palisade with sharpened points atop it." + fence_icon = 'icons/obj/structures/fences/palisade.dmi' + +/decl/fence_type/stick + name = "stick fence" + desc = "A stick fence. Not as effective as a wall, but generally it keeps people out." + fence_icon = 'icons/obj/structures/fences/stick.dmi' + +/decl/fence_type/plank + name = "plank fence" + desc = "A plank fence. Not as effective as a wall, but generally it keeps people out." + fence_icon = 'icons/obj/structures/fences/plank.dmi' diff --git a/code/game/objects/structures/flora/plant.dm b/code/game/objects/structures/flora/plant.dm index 9e44f55a2f3..f4dfac197bc 100644 --- a/code/game/objects/structures/flora/plant.dm +++ b/code/game/objects/structures/flora/plant.dm @@ -7,14 +7,20 @@ var/dead = FALSE var/sampled = FALSE var/datum/seed/plant - var/harvestable + var/harvestable = 0 // Note that this is a counter, not a bool. + var/pollen = 0 /obj/structure/flora/plant/large opacity = TRUE density = TRUE +/obj/structure/flora/plant/process_plants() + if(plant?.produces_pollen <= 0) + return PROCESS_KILL + if(pollen < MAX_POLLEN_PER_FLOWER) + pollen += plant.produces_pollen * POLLEN_PRODUCTION_MULT + /* Notes for future work moving logic off hydrotrays onto plants themselves: -/obj/structure/flora/plant/Process() // check our immediate environment // ask our environment for available reagents // process the reagents @@ -61,9 +67,13 @@ var/potency = plant.get_trait(TRAIT_POTENCY) set_light(l_range = max(1, round(potency/10)), l_power = clamp(round(potency/30), 0, 1), l_color = plant.get_trait(TRAIT_BIOLUM_COLOUR)) update_icon() - return ..() + . = ..() + if(plant?.produces_pollen && !is_processing) + START_PROCESSING(SSplants, src) /obj/structure/flora/plant/Destroy() + if(is_processing) + STOP_PROCESSING(SSplants, src) plant = null . = ..() @@ -147,3 +157,24 @@ /obj/structure/flora/plant/random_mushroom/Initialize() plant = pick(get_mushroom_variants()) return ..() + +/obj/structure/flora/plant/random_flower + name = "flower" + color = COLOR_PINK + icon_state = "flower5" + is_spawnable_type = TRUE + +// Only contains roundstart plants, this is meant to be a mapping helper. +/obj/structure/flora/plant/random_flower/proc/get_flower_variants() + var/static/list/flower_variants + if(isnull(flower_variants)) + flower_variants = list() + for(var/plant in SSplants.seeds) + var/datum/seed/seed = SSplants.seeds[plant] + if(!isnull(seed?.name) && seed.produces_pollen) + flower_variants |= seed.name + return flower_variants + +/obj/structure/flora/plant/random_flower/Initialize() + plant = pick(get_flower_variants()) + return ..() diff --git a/code/game/objects/structures/flora/stump.dm b/code/game/objects/structures/flora/stump.dm index 4911cd3a57e..f13415593f7 100644 --- a/code/game/objects/structures/flora/stump.dm +++ b/code/game/objects/structures/flora/stump.dm @@ -4,6 +4,7 @@ /obj/structure/flora/stump name = "stump" hitsound = 'sound/effects/hit_wood.ogg' + storage = /datum/storage/dead_tree var/log_type = /obj/item/stack/material/log /obj/structure/flora/stump/get_material_health_modifier() diff --git a/code/game/objects/structures/flora/tree.dm b/code/game/objects/structures/flora/tree.dm index 57c59edea80..12c7df0a884 100644 --- a/code/game/objects/structures/flora/tree.dm +++ b/code/game/objects/structures/flora/tree.dm @@ -114,6 +114,11 @@ var/global/list/christmas_trees = list() icon_state = "tree_1" protects_against_weather = FALSE stump_type = /obj/structure/flora/stump/tree/dead + storage = /datum/storage/dead_tree + +/datum/storage/dead_tree + max_w_class = ITEM_SIZE_NORMAL + max_storage_space = ITEM_SIZE_SMALL * 5 /obj/structure/flora/tree/dead/random/init_appearance() icon_state = "tree_[rand(1, 6)]" diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index d168182cdcc..4021bac91ea 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -9,7 +9,8 @@ material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_COLOR tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) max_health = 100 - parts_amount = 2 + material = /decl/material/solid/metal/steel + parts_amount = 5 parts_type = /obj/item/stack/material/rods var/cover = 50 diff --git a/code/game/objects/structures/racks.dm b/code/game/objects/structures/racks.dm index 4830025c353..264000c2020 100644 --- a/code/game/objects/structures/racks.dm +++ b/code/game/objects/structures/racks.dm @@ -44,12 +44,6 @@ auto_align(used_item, click_params) return TRUE -/obj/structure/rack/holorack/dismantle_structure(mob/user) - material = null - reinf_material = null - parts_type = null - . = ..() - /obj/structure/rack/dark color = COLOR_GRAY40 diff --git a/code/game/objects/structures/tables.dm b/code/game/objects/structures/tables.dm index fb9dda2bf28..6ce7d95c8dc 100644 --- a/code/game/objects/structures/tables.dm +++ b/code/game/objects/structures/tables.dm @@ -706,26 +706,6 @@ color = "#8f29a3" reinf_material = /decl/material/solid/glass/borosilicate -/obj/structure/table/holotable - icon_state = "holo_preview" - holographic = TRUE - color = COLOR_OFF_WHITE - material = /decl/material/solid/metal/aluminium/holographic - reinf_material = /decl/material/solid/metal/aluminium/holographic - -/obj/structure/table/holo_plastictable - icon_state = "holo_preview" - holographic = TRUE - color = COLOR_OFF_WHITE - material = /decl/material/solid/organic/plastic/holographic - reinf_material = /decl/material/solid/organic/plastic/holographic - -/obj/structure/table/holo_woodentable - holographic = TRUE - icon_state = "holo_preview" - material = /decl/material/solid/organic/wood/holographic - reinf_material = /decl/material/solid/organic/wood/holographic - //wood wood wood /obj/structure/table/wood icon_state = "solid_preview" diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index f30adf01de7..71f162a7fb5 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -89,7 +89,8 @@ var/global/list/flooring_cache = list() var/render_trenches = TRUE var/floor_layer = TURF_LAYER - var/holographic = FALSE + /// If TRUE, this turf cannot be damaged, painted, pried off, etc. + var/visual_only = FALSE var/dirt_color = /decl/material/solid/soil::color var/list/burned_states @@ -105,7 +106,7 @@ var/global/list/flooring_cache = list() if(!istype(force_material)) force_material = null - if(holographic) + if(visual_only) turf_flags = null damage_temperature = INFINITY build_type = null diff --git a/code/game/turfs/flooring/flooring_holowater.dm b/code/game/turfs/flooring/flooring_holowater.dm index 6bfe235d67e..32f36110eef 100644 --- a/code/game/turfs/flooring/flooring_holowater.dm +++ b/code/game/turfs/flooring/flooring_holowater.dm @@ -6,6 +6,6 @@ icon_base = "fakewater" has_base_range = null footstep_type = /decl/footsteps/water - holographic = TRUE + visual_only = TRUE constructed = TRUE uid = "floor_water_fake" diff --git a/code/game/turfs/flooring/flooring_sand.dm b/code/game/turfs/flooring/flooring_sand.dm index 5b4f13501ea..9ee78f27ee0 100644 --- a/code/game/turfs/flooring/flooring_sand.dm +++ b/code/game/turfs/flooring/flooring_sand.dm @@ -5,6 +5,7 @@ icon = 'icons/turf/flooring/sand.dmi' icon_base = "sand" icon_edge_layer = FLOOR_EDGE_SAND + color = null // autoset from material has_base_range = 4 turf_flags = TURF_FLAG_BACKGROUND | TURF_IS_HOLOMAP_PATH | TURF_FLAG_ABSORB_LIQUID force_material = /decl/material/solid/sand @@ -43,7 +44,7 @@ /decl/flooring/sand/fake name = "holosand" desc = "Uncomfortably coarse and gritty for a hologram." - holographic = TRUE + visual_only = TRUE uid = "floor_sand_fake" /decl/flooring/fake_space @@ -52,7 +53,7 @@ icon = 'icons/turf/flooring/fake_space.dmi' icon_base = "space" has_base_range = 25 - holographic = TRUE + visual_only = TRUE gender = NEUTER uid = "floor_space_fake" diff --git a/code/game/turfs/flooring/flooring_snow.dm b/code/game/turfs/flooring/flooring_snow.dm index 72362928c06..d4922e3d30b 100644 --- a/code/game/turfs/flooring/flooring_snow.dm +++ b/code/game/turfs/flooring/flooring_snow.dm @@ -60,7 +60,7 @@ uid = "floor_permafrost" /decl/flooring/permafrost/get_vehicle_transit_delay(obj/vehicle/vehicle) - if(holographic) + if(visual_only) return vehicle::base_speed if(vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE) return 0.8 @@ -69,6 +69,6 @@ /decl/flooring/snow/fake name = "holosnow" desc = "Not quite the same as snow on an entertainment terminal, but close." - holographic = TRUE + visual_only = TRUE uid = "floor_snow_fake" diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 28171b7c239..8af4b0d3b11 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -845,10 +845,6 @@ /turf/get_color() return paint_color || get_material()?.color || color -/turf/proc/get_fishing_result(obj/item/food/bait) - var/area/A = get_area(src) - return A.get_fishing_result(src, bait) - /turf/get_affecting_weather() return weather diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 0dfa901cf98..2b5d18ba136 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -152,7 +152,7 @@ var/new_z_opacity = z_flags & ZM_ALLOW_LIGHTING if (new_z_opacity != old_z_opacity) for (var/datum/lighting_corner/corn in corners) - corn.rebuild_ztraversal(!new_z_opacity) + corn.generate_z_connections() var/tidlu = TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) if ((old_opacity != opacity) || (tidlu != old_dynamic_lighting) || force_lighting_update) diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm index e73bea10dcb..da02c82b244 100644 --- a/code/game/turfs/walls/_wall.dm +++ b/code/game/turfs/walls/_wall.dm @@ -38,7 +38,6 @@ var/global/list/wall_fullblend_objects = list( var/can_open = 0 var/decl/material/girder_material = /decl/material/solid/metal/steel var/construction_stage - var/hitsound = 'sound/weapons/Genhit.ogg' /// A list of connections to walls for each corner, used for icon generation. Can be converted to a list of dirs with corner_states_to_dirs(). var/list/wall_connections /// A list of connections to non-walls for each corner, used for icon generation. Can be converted to a list of dirs with corner_states_to_dirs(). @@ -149,7 +148,7 @@ var/global/list/wall_fullblend_objects = list( . = ..() if(. && density && !ismob(AM)) var/tforce = AM.get_thrown_attack_force() * (TT.speed/THROWFORCE_SPEED_DIVISOR) - playsound(src, hitsound, tforce >= 15 ? 60 : 25, TRUE) + playsound(src, get_hit_sound(), tforce >= 15 ? 60 : 25, TRUE) if(tforce > 0) take_damage(tforce) @@ -322,7 +321,7 @@ var/global/list/wall_fullblend_objects = list( handle_melting() /turf/wall/proc/get_hit_sound() - return 'sound/effects/metalhit.ogg' + return material?.hitsound || 'sound/weapons/Genhit.ogg' // Mapped premade for false walls /turf/wall/false diff --git a/code/game/turfs/walls/wall_attacks.dm b/code/game/turfs/walls/wall_attacks.dm index d736a234400..2da93349cc4 100644 --- a/code/game/turfs/walls/wall_attacks.dm +++ b/code/game/turfs/walls/wall_attacks.dm @@ -94,7 +94,7 @@ if (isnull(construction_stage) || !reinf_material) to_chat(user, "You push \the [src], but nothing happens.") - playsound(src, hitsound, 25, 1) + playsound(src, get_hit_sound(), 25, 1) return TRUE /turf/wall/attack_hand(var/mob/user) @@ -302,25 +302,27 @@ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) user.do_attack_animation(src) + + var/damage_threshold = max(2, max(material.wall_damage_threshold, reinf_material?.wall_damage_threshold)) var/material_divisor = max(material.brute_armor, reinf_material?.brute_armor) if(used_item.atom_damage_type == BURN) material_divisor = max(material.burn_armor, reinf_material?.burn_armor) var/effective_force = round(force / material_divisor) - if(effective_force < 2) - visible_message(SPAN_DANGER("\The [user] [used_item.pick_attack_verb()] \the [src] with \the [used_item], but it had no effect!")) - playsound(src, hitsound, 25, 1) + if(effective_force < damage_threshold) + visible_message(SPAN_DANGER("\The [user] has [used_item.pick_attack_verb()] \the [src] with \the [used_item], but it has no effect!")) + playsound(src, get_hit_sound(), 25, 1) return TRUE // Check for a glancing blow. var/dam_prob = max(0, 100 - material.hardness + effective_force + used_item.armor_penetration) if(!prob(dam_prob)) - visible_message(SPAN_DANGER("\The [user] [used_item.pick_attack_verb()] \the [src] with \the [used_item], but it bounced off!")) - playsound(src, hitsound, 25, 1) + visible_message(SPAN_DANGER("\The [user] has [used_item.pick_attack_verb()] \the [src] with \the [used_item], but it bounced off!")) + playsound(src, get_hit_sound(), 25, 1) if(user.skill_fail_prob(SKILL_HAULING, 40, SKILL_ADEPT)) SET_STATUS_MAX(user, STAT_WEAK, 2) visible_message(SPAN_DANGER("\The [user] is knocked back by the force of the blow!")) return TRUE + visible_message(SPAN_DANGER("\The [user] has [used_item.pick_attack_verb()] \the [src] with \the [used_item]!")) playsound(src, get_hit_sound(), 50, 1) - visible_message(SPAN_DANGER("\The [user] [used_item.pick_attack_verb()] \the [src] with \the [used_item]!")) take_damage(effective_force) return TRUE \ No newline at end of file diff --git a/code/game/turfs/walls/wall_icon.dm b/code/game/turfs/walls/wall_icon.dm index ee87ca41001..856853b0330 100644 --- a/code/game/turfs/walls/wall_icon.dm +++ b/code/game/turfs/walls/wall_icon.dm @@ -11,7 +11,6 @@ material = get_default_material() if(material) explosion_resistance = material.explosion_resistance - hitsound = material.hitsound if(reinf_material) reinf_icon = islist(reinf_material.icon_reinf) ? pick(reinf_material.icon_reinf) : reinf_material.icon_reinf if(reinf_material.explosion_resistance > explosion_resistance) diff --git a/code/game/turfs/walls/wall_natural_subtypes.dm b/code/game/turfs/walls/wall_natural_subtypes.dm index dc072ac2519..549cab71e39 100644 --- a/code/game/turfs/walls/wall_natural_subtypes.dm +++ b/code/game/turfs/walls/wall_natural_subtypes.dm @@ -75,7 +75,6 @@ name = "sand"; \ icon = 'icons/turf/flooring/sand.dmi'; \ icon_state = "sand0"; \ - color = "#ae9e66"; \ _flooring = /decl/flooring/sand; \ } \ /turf/wall/natural/##ID { \ diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index 19efad19a39..4d248d8255d 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -175,6 +175,14 @@ var/global/list/view_variables_no_assoc = list("verbs", "contents","screen","ima else if(istype(value, /client)) var/client/C = value vtext = "\ref[C] - [C] ([C.type])" + else if(istype(value, /alist)) + var/alist/AL = value + vtext = "/alist ([AL.len])" + if(!(varname in view_variables_dont_expand) && AL.len > 0 && AL.len < 100) + extra += "" else if(islist(value)) var/list/L = value vtext = "/list ([L.len])" diff --git a/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm b/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm index 70ccb935f75..f8bbb5b54bc 100644 --- a/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm +++ b/code/modules/atmospherics/components/binary_devices/binary_atmos_base.dm @@ -23,11 +23,7 @@ return air1 /obj/machinery/atmospherics/binary/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + return !check_internal_pressure_difference_over(2 ATM) // Will only be used if you set the anchorable obj flag. /obj/machinery/atmospherics/binary/wrench_floor_bolts(mob/user, delay = 2 SECONDS, obj/item/tool) diff --git a/code/modules/atmospherics/components/portables_connector.dm b/code/modules/atmospherics/components/portables_connector.dm index d6898b12987..61bc98beb27 100644 --- a/code/modules/atmospherics/components/portables_connector.dm +++ b/code/modules/atmospherics/components/portables_connector.dm @@ -60,11 +60,7 @@ return list(connection.merged_mixture) /obj/machinery/atmospherics/portables_connector/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + return !check_internal_pressure_difference_over(2 ATM) /obj/machinery/atmospherics/portables_connector/cannot_transition_to(state_path, mob/user) if(state_path == /decl/machine_construction/default/deconstructed) diff --git a/code/modules/atmospherics/components/tvalve.dm b/code/modules/atmospherics/components/tvalve.dm index eca39ebd937..f24062ba123 100644 --- a/code/modules/atmospherics/components/tvalve.dm +++ b/code/modules/atmospherics/components/tvalve.dm @@ -117,11 +117,7 @@ return null /obj/machinery/atmospherics/tvalve/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + return !check_internal_pressure_difference_over(2 ATM) /decl/public_access/public_variable/tvalve_state expected_type = /obj/machinery/atmospherics/tvalve diff --git a/code/modules/atmospherics/components/unary/heat_exchanger.dm b/code/modules/atmospherics/components/unary/heat_exchanger.dm index f19c4ecb4f7..6ed50ed3f46 100644 --- a/code/modules/atmospherics/components/unary/heat_exchanger.dm +++ b/code/modules/atmospherics/components/unary/heat_exchanger.dm @@ -76,12 +76,7 @@ partner.update_networks() /obj/machinery/atmospherics/unary/heat_exchanger/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + return !check_internal_pressure_difference_over(2 ATM) /obj/machinery/atmospherics/unary/heat_exchanger/cannot_transition_to(state_path, mob/user) if(state_path == /decl/machine_construction/default/deconstructed) diff --git a/code/modules/atmospherics/components/unary/unary_base.dm b/code/modules/atmospherics/components/unary/unary_base.dm index 21734409daa..0e452755498 100644 --- a/code/modules/atmospherics/components/unary/unary_base.dm +++ b/code/modules/atmospherics/components/unary/unary_base.dm @@ -11,6 +11,8 @@ var/controlled = TRUE // if true, report to air alarm, if false, probably in direct contact with something else by radio (e.g. airlocks) /obj/machinery/atmospherics/unary/get_single_monetary_worth() + if(worthless) + return 0 . = ..() for(var/gas_type, gas_amount in air_contents?.gas) var/decl/material/gas_data = GET_DECL(gas_type) diff --git a/code/modules/atmospherics/components/unary/vent_pump.dm b/code/modules/atmospherics/components/unary/vent_pump.dm index 49fcbbbb276..9041a595620 100644 --- a/code/modules/atmospherics/components/unary/vent_pump.dm +++ b/code/modules/atmospherics/components/unary/vent_pump.dm @@ -341,9 +341,7 @@ break if (hidden_pipe_check && isturf(T) && !T.is_plating()) return SPAN_WARNING("You must remove the plating first.") - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) + if (check_internal_pressure_difference_over(2 ATM)) return SPAN_WARNING("You cannot unwrench \the [src], it is too exerted due to internal pressure.") return ..() diff --git a/code/modules/atmospherics/components/unary/vent_scrubber.dm b/code/modules/atmospherics/components/unary/vent_scrubber.dm index 58fdc8f1717..a22a770b326 100644 --- a/code/modules/atmospherics/components/unary/vent_scrubber.dm +++ b/code/modules/atmospherics/components/unary/vent_scrubber.dm @@ -197,9 +197,7 @@ break if (hidden_pipe_check && isturf(T) && !T.is_plating()) return SPAN_WARNING("You must remove the plating first.") - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) + if (check_internal_pressure_difference_over(2 ATM)) return SPAN_WARNING("You cannot take this [src] apart, it too exerted due to internal pressure.") return ..() diff --git a/code/modules/atmospherics/components/valve.dm b/code/modules/atmospherics/components/valve.dm index 8b639036d73..5ffd62ff082 100644 --- a/code/modules/atmospherics/components/valve.dm +++ b/code/modules/atmospherics/components/valve.dm @@ -113,11 +113,7 @@ return null /obj/machinery/atmospherics/valve/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + return !check_internal_pressure_difference_over(2 ATM) /obj/machinery/atmospherics/valve/get_examine_strings(mob/user, distance, infix, suffix) . = ..() diff --git a/code/modules/atmospherics/pipes.dm b/code/modules/atmospherics/pipes.dm index f31cd7a5c53..db37eef0418 100644 --- a/code/modules/atmospherics/pipes.dm +++ b/code/modules/atmospherics/pipes.dm @@ -135,12 +135,8 @@ . = ..() /obj/machinery/atmospherics/pipe/deconstruction_pressure_check() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - - if ((int_air.return_pressure()-env_air.return_pressure()) > (2 ATM)) - return FALSE - return TRUE + // this uses !over instead of under so that it's <= instead of < + return !check_internal_pressure_difference_over(2 ATM) /obj/machinery/atmospherics/pipe/cannot_transition_to(state_path, mob/user) if(state_path == /decl/machine_construction/default/deconstructed) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 5a4b8a07540..5631eb2ae6b 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -430,23 +430,16 @@ var/global/list/localhost_addresses = list( if(world.byond_version >= 511 && byond_version >= 511 && client_fps >= CLIENT_MIN_FPS && client_fps <= CLIENT_MAX_FPS) vars["fps"] = client_fps -/client/MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) - . = ..() - var/mob/living/M = mob - if(istype(M)) - M.OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) - /client/MouseUp(object, location, control, params) . = ..() - var/mob/living/M = mob - if(istype(M)) - M.OnMouseUp(object, location, control, params) + if(mob?.on_mouse_up()) + _block_next_click = TRUE /client/MouseDown(object, location, control, params) . = ..() var/mob/living/M = mob if(istype(M) && !M.in_throw_mode) - M.OnMouseDown(object, location, control, params) + M.on_mouse_down(object, location, control, params) /client/verb/SetWindowIconSize(var/val as num|text) set hidden = 1 @@ -599,6 +592,12 @@ var/global/const/MAX_VIEW = 41 winset(src, "mainwindow.split", "splitter=[pct]") /client/Click(atom/A) + + // Mouse drag safeguard against a trailing Click() called after MouseUp(). + if(_block_next_click) + _block_next_click = FALSE + return + if(!user_acted(src)) return diff --git a/code/modules/client/preference_setup/records/01_character_info.dm b/code/modules/client/preference_setup/records/01_character_info.dm index cd2a6b27b5a..68cc61b5f7d 100644 --- a/code/modules/client/preference_setup/records/01_character_info.dm +++ b/code/modules/client/preference_setup/records/01_character_info.dm @@ -31,11 +31,8 @@ /datum/category_item/player_setup_item/records/character_info/OnTopic(var/href,var/list/href_list, var/mob/user) - if (record_key && href_list["set_record"]) - var/new_record = sanitize(input(user,"Enter new [lowertext(name)] here.", CHARACTER_PREFERENCE_INPUT_TITLE, html_decode(pref.records[record_key])) as message|null, MAX_PAPER_MESSAGE_LEN, extra = 0) - if(!isnull(new_record) && !jobban_isbanned(user, "Records") && !jobban_isbanned(user, name) && CanUseTopic(user)) - pref.records[record_key] = new_record - return TOPIC_REFRESH + if((. = ..())) // does nothing because it has no records_key + return var/datum/character_information/comments = pref.comments_record_id && SScharacter_info.get_record(pref.comments_record_id, TRUE) if(comments) @@ -64,3 +61,9 @@ if(. == TOPIC_REFRESH && istext(pref.comments_record_id) && length(pref.comments_record_id)) SScharacter_info.queue_to_save(pref.comments_record_id) + +/datum/category_item/player_setup_item/records/character_info/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) + if(is_preview_copy) + return + pref.validate_comments_record() // Make sure a record has been generated for this character. + character.comments_record_id = pref.comments_record_id \ No newline at end of file diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index e851c251390..ad33367976e 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -352,12 +352,6 @@ var/global/list/time_prefs_fixed = list() update_setup_window(usr) return TRUE -/datum/category_item/player_setup_item/records/character_info/apply_post_snapshot_preferences(mob/living/human/character, is_preview_copy = FALSE) - if(is_preview_copy) - return - pref.validate_comments_record() // Make sure a record has been generated for this character. - character.comments_record_id = pref.comments_record_id - /datum/preferences/proc/create_character_from_snapshot(spawn_turf) // Sanitizing rather than saving as someone might still be editing. player_setup.sanitize_setup() @@ -368,7 +362,6 @@ var/global/list/time_prefs_fixed = list() apply_post_snapshot_preferences(character, FALSE) return character - /datum/preferences/proc/copy_to(mob/living/human/character, is_preview_copy = FALSE) apply_snapshot_to_mob(character, is_preview_copy) // this is effectively what create_character_from_snapshot does, but on an existing mob apply_post_snapshot_preferences(character, is_preview_copy) // this is the stuff we need to share diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index 6ba6b837d81..c8412202284 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -237,6 +237,15 @@ gun.Fire(target,holder.wearer) return 1 +/obj/item/rig_module/mounted/wielder_mouse_drag_held(mob/user, atom/target) + return istype(gun) ? gun.wielder_mouse_drag_held(user, target) : ..() + +/obj/item/rig_module/mounted/wielder_mouse_drag_up(mob/user, atom/target) + return istype(gun) ? gun.wielder_mouse_drag_up(user, target) : ..() + +/obj/item/rig_module/mounted/wielder_mouse_drag_down(mob/user, object, location, control, params) + return istype(gun) ? gun.wielder_mouse_drag_down(user, object, location, control, params) : ..() + /obj/item/rig_module/mounted/lcannon name = "mounted laser cannon" diff --git a/code/modules/clothing/spacesuits/void/void.dm b/code/modules/clothing/spacesuits/void/void.dm index 6155cf7da0a..e14954b6f97 100644 --- a/code/modules/clothing/spacesuits/void/void.dm +++ b/code/modules/clothing/spacesuits/void/void.dm @@ -34,7 +34,11 @@ ARMOR_BIO = ARMOR_BIO_SHIELDED, ARMOR_RAD = ARMOR_RAD_MINOR ) - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit) + allowed = list( + /obj/item/flashlight, + /obj/item/tank, + /obj/item/suit_cooling_unit + ) heat_protection = SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_LEGS|SLOT_FEET|SLOT_ARMS|SLOT_HANDS|SLOT_TAIL max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE max_pressure_protection = VOIDSUIT_MAX_PRESSURE @@ -274,6 +278,17 @@ else if(##equipment_var) {\ playsound(loc, 'sound/items/Deconstruct.ogg', 50, 1) return TRUE + if(istype(used_item,/obj/item/suit_cooling_unit)) + if(user.get_equipped_slot_for_item(src) == slot_wear_suit_str) + to_chat(user, "You cannot modify \the [src] while it is being worn.") + else if(tank) + to_chat(user, "\The [src] already has an airtank installed.") + else if(user.try_unequip(used_item, src)) + to_chat(user, "You insert \the [used_item] into \the [src]'s storage compartment.") + tank = used_item + playsound(loc, 'sound/items/Deconstruct.ogg', 50, 1) + return TRUE + return ..() /obj/item/clothing/suit/space/void/attack_self() //sole purpose of existence is to toggle the helmet diff --git a/code/modules/crafting/stack_recipes/_recipe_getter.dm b/code/modules/crafting/stack_recipes/_recipe_getter.dm index cf79001263d..7f6896fcb90 100644 --- a/code/modules/crafting/stack_recipes/_recipe_getter.dm +++ b/code/modules/crafting/stack_recipes/_recipe_getter.dm @@ -19,7 +19,7 @@ /proc/get_stack_recipes(decl/material/mat, decl/material/reinf_mat, stack_type, tool_type, flat = FALSE) // No recipes for holograms or fluids. - if(istype(mat) && (mat.holographic || mat.phase_at_temperature() != MAT_PHASE_SOLID)) + if(istype(mat) && (mat.visual_only || mat.phase_at_temperature() != MAT_PHASE_SOLID)) return list() #ifndef UNIT_TEST // key creation is SLOW and in unit testing almost every call to this will be a cache fail diff --git a/code/modules/crafting/stack_recipes/recipes_bricks.dm b/code/modules/crafting/stack_recipes/recipes_bricks.dm index 0c9670fbcd9..a7dcdbc92fd 100644 --- a/code/modules/crafting/stack_recipes/recipes_bricks.dm +++ b/code/modules/crafting/stack_recipes/recipes_bricks.dm @@ -101,6 +101,14 @@ name = "pedestal, round" result_type = /obj/structure/pedestal/round +/decl/stack_recipe/bricks/furniture/fence + result_type = /obj/structure/fence/brick + difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/bricks/furniture/fence_door + result_type = /obj/structure/fence/door/brick + difficulty = MAT_VALUE_NORMAL_DIY + /decl/stack_recipe/bricks/gravestone result_type = /obj/item/gravemarker/gravestone difficulty = MAT_VALUE_NORMAL_DIY diff --git a/code/modules/crafting/stack_recipes/recipes_logs.dm b/code/modules/crafting/stack_recipes/recipes_logs.dm index 02010e3a925..3dadcadb424 100644 --- a/code/modules/crafting/stack_recipes/recipes_logs.dm +++ b/code/modules/crafting/stack_recipes/recipes_logs.dm @@ -22,4 +22,19 @@ /decl/stack_recipe/logs/wall_frame result_type = /obj/structure/wall_frame/log - difficulty = MAT_VALUE_HARD_DIY \ No newline at end of file + difficulty = MAT_VALUE_HARD_DIY + +/decl/stack_recipe/logs/furniture + abstract_type = /decl/stack_recipe/logs/furniture + one_per_turf = TRUE + on_floor = TRUE + difficulty = MAT_VALUE_HARD_DIY + category = "furniture" + +/decl/stack_recipe/logs/furniture/fence + result_type = /obj/structure/fence/palisade + difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/logs/furniture/fence_door + result_type = /obj/structure/fence/door/palisade + difficulty = MAT_VALUE_NORMAL_DIY diff --git a/code/modules/crafting/stack_recipes/recipes_planks.dm b/code/modules/crafting/stack_recipes/recipes_planks.dm index feac5e54b58..a7114078dda 100644 --- a/code/modules/crafting/stack_recipes/recipes_planks.dm +++ b/code/modules/crafting/stack_recipes/recipes_planks.dm @@ -22,9 +22,6 @@ difficulty = MAT_VALUE_VERY_HARD_DIY available_to_map_tech_level = MAP_TECH_LEVEL_SPACE -/decl/stack_recipe/planks/fishing_rod - result_type = /obj/item/fishing_rod - /decl/stack_recipe/planks/stick result_type = /obj/item/stick difficulty = MAT_VALUE_EASY_DIY @@ -251,3 +248,11 @@ /decl/stack_recipe/planks/furniture/target_stake result_type = /obj/structure/target_stake difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/planks/furniture/fence + result_type = /obj/structure/fence/plank + difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/planks/furniture/fence_door + result_type = /obj/structure/fence/door/plank + difficulty = MAT_VALUE_NORMAL_DIY diff --git a/code/modules/crafting/stack_recipes/recipes_rods.dm b/code/modules/crafting/stack_recipes/recipes_rods.dm index 4b506210b57..7458e938929 100644 --- a/code/modules/crafting/stack_recipes/recipes_rods.dm +++ b/code/modules/crafting/stack_recipes/recipes_rods.dm @@ -37,7 +37,6 @@ /decl/stack_recipe/rods/girder result_type = /obj/structure/girder required_wall_support_value = 10 - req_amount = 5 * SHEET_MATERIAL_AMOUNT // Arbitrary value since girders return weird matter values. available_to_map_tech_level = MAP_TECH_LEVEL_SPACE /decl/stack_recipe/rods/wall_frame @@ -77,3 +76,19 @@ result_type = /obj/structure/grille one_per_turf = TRUE difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/rods/furniture + abstract_type = /decl/stack_recipe/rods/furniture + one_per_turf = TRUE + on_floor = TRUE + difficulty = MAT_VALUE_HARD_DIY + category = "furniture" + +/decl/stack_recipe/rods/furniture/fence + result_type = /obj/structure/fence/stick + difficulty = MAT_VALUE_NORMAL_DIY + +/decl/stack_recipe/rods/furniture/fence_door + result_type = /obj/structure/fence/door/stick + difficulty = MAT_VALUE_NORMAL_DIY + diff --git a/code/modules/decoration/decoration_item.dm b/code/modules/decoration/decoration_item.dm index ae628cf68bf..034ebbb7871 100644 --- a/code/modules/decoration/decoration_item.dm +++ b/code/modules/decoration/decoration_item.dm @@ -74,6 +74,8 @@ return TRUE /obj/item/get_single_monetary_worth() + if(worthless) + return 0 . = ..() var/base_value = . for(var/decl/item_decoration/decoration as anything in decorations) diff --git a/code/modules/economy/_worth.dm b/code/modules/economy/_worth.dm index f2f476a776b..cceeb5e0ae7 100644 --- a/code/modules/economy/_worth.dm +++ b/code/modules/economy/_worth.dm @@ -1,4 +1,6 @@ /atom + /// If TRUE, this is worthless. Its contents will still be properly valued by get_contents_monetary_worth(), however. + var/worthless = FALSE var/monetary_worth_multiplier = 1 /atom/proc/get_base_value() @@ -8,6 +10,8 @@ . = monetary_worth_multiplier /atom/proc/get_single_monetary_worth() + if(worthless) + return 0 . = get_base_value() * get_value_multiplier() if(reagents) for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) diff --git a/code/modules/economy/worth_cash.dm b/code/modules/economy/worth_cash.dm index 49d04f24ebd..84db32d7d5e 100644 --- a/code/modules/economy/worth_cash.dm +++ b/code/modules/economy/worth_cash.dm @@ -42,7 +42,7 @@ update_from_worth() /obj/item/cash/get_base_value() - . = holographic ? 0 : absolute_worth + return absolute_worth /obj/item/cash/proc/set_currency(var/new_currency) currency = new_currency @@ -245,7 +245,7 @@ . += SPAN_NOTICE("[capitalize(cur.name)] remaining: [floor(loaded_worth / cur.absolute_value)].") /obj/item/charge_stick/get_base_value() - . = holographic ? 0 : loaded_worth + return loaded_worth /obj/item/charge_stick/attackby(var/obj/item/used_item, var/mob/user) var/datum/extension/lockable/lock = get_extension(src, /datum/extension/lockable) diff --git a/code/modules/economy/worth_clothing.dm b/code/modules/economy/worth_clothing.dm index bf1a039887b..7a0a45548d3 100644 --- a/code/modules/economy/worth_clothing.dm +++ b/code/modules/economy/worth_clothing.dm @@ -1,6 +1,6 @@ /obj/item/clothing/get_base_value() . = max(..(), 10) - if(!holographic && flash_protection > 0) + if(flash_protection > 0) . += flash_protection * 25 /obj/item/clothing/head/collectable/get_value_multiplier() diff --git a/code/modules/economy/worth_items.dm b/code/modules/economy/worth_items.dm index 15998a7521f..bea9943f41e 100644 --- a/code/modules/economy/worth_items.dm +++ b/code/modules/economy/worth_items.dm @@ -3,10 +3,6 @@ #define BASE_ARMOUR_WORTH 50 /obj/item/get_base_value() - - if(holographic) - return 0 - . = ..() if(origin_tech) @@ -75,7 +71,7 @@ #undef MUNDANE_ARMOUR_VALUE #undef BASE_ARMOUR_WORTH -/obj/item/organ/get_single_monetary_worth() +/obj/item/organ/get_value_multiplier() . = ..() if(species) - . = round(. * species.rarity_value) + . *= species.rarity_value diff --git a/code/modules/economy/worth_mob.dm b/code/modules/economy/worth_mob.dm index 7102d7c6809..d4bb79d02ec 100644 --- a/code/modules/economy/worth_mob.dm +++ b/code/modules/economy/worth_mob.dm @@ -5,6 +5,8 @@ . = max(round(.), mob_size) /mob/living/get_single_monetary_worth() + if(worthless) + return 0 . = ..() for(var/atom/movable/organ in get_organs()) . += organ.get_combined_monetary_worth() @@ -14,5 +16,7 @@ . = round(.) /mob/living/get_value_multiplier() + if(worthless) + return 0 var/decl/species/my_species = get_species() . = my_species ? my_species.rarity_value : 1 diff --git a/code/modules/economy/worth_obj.dm b/code/modules/economy/worth_obj.dm index 04b75a13fed..3e89c43e68c 100644 --- a/code/modules/economy/worth_obj.dm +++ b/code/modules/economy/worth_obj.dm @@ -18,8 +18,6 @@ . = length(matter) ? ..() : (material?.value || 1) /obj/get_base_value() - if(holographic) - return 0 if(length(matter)) . = 0 for(var/mat in matter) diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index 48702089f43..0a7a83f4471 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -108,16 +108,6 @@ S.add_ion_law(law) S.show_laws() - for(var/z in affecting_z) - var/obj/machinery/network/message_server/MS = get_message_server_for_z(z) - if(MS) - MS.spamfilter.Cut() - var/i - for (i = 1, i <= MS.spamfilter_limit, i++) - MS.spamfilter += pick("kitty","HONK","rev","malf","liberty","freedom","drugs", "[global.using_map.station_short]", \ - "admin","ponies","heresy","meow","Pun Pun","monkey","Ian","moron","pizza","message","spam",\ - "director", "Hello", "Hi!"," ","nuke","crate","dwarf","xeno") - /datum/event/ionstorm/tick() if(botEmagChance) for(var/mob/living/bot/bot in global.living_mob_list_) diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm index aef3b2e9a27..741fc0c2ded 100644 --- a/code/modules/fabrication/designs/general/designs_general.dm +++ b/code/modules/fabrication/designs/general/designs_general.dm @@ -153,12 +153,6 @@ path = /obj/item/stack/tape_roll/duct_tape pass_multiplier_to_product_new = FALSE // they are printed as single items with 32 uses -/datum/fabricator_recipe/fishing_line - path = /obj/item/fishing_line - -/datum/fabricator_recipe/fishing_line_high_quality - path = /obj/item/fishing_line/high_quality - /datum/fabricator_recipe/chipboard // base type is for oak path = /obj/item/stack/material/sheet/mapped/chipboard_oak category = "Textiles" diff --git a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm index 474b8c3e409..902b95c986f 100644 --- a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm +++ b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm @@ -83,9 +83,6 @@ /datum/fabricator_recipe/imprinter/circuit/accounts path = /obj/item/stock_parts/circuitboard/account_database -/datum/fabricator_recipe/imprinter/circuit/holo - path = /obj/item/stock_parts/circuitboard/holodeck_control - /datum/fabricator_recipe/imprinter/circuit/aiupload path = /obj/item/stock_parts/circuitboard/aiupload diff --git a/code/modules/fusion/gyrotron/gyrotron.dm b/code/modules/fusion/gyrotron/gyrotron.dm index f70c73ffb5d..27fef671223 100644 --- a/code/modules/fusion/gyrotron/gyrotron.dm +++ b/code/modules/fusion/gyrotron/gyrotron.dm @@ -10,10 +10,11 @@ active_power_usage = GYRO_POWER var/initial_id_tag + /// Time between shots, in SECONDS, NOT DECISECONDS var/rate = 3 var/mega_energy = 1 - construct_state = /decl/machine_construction/default/panel_closed + construct_state = /decl/machine_construction/emitter/unsecured/gyrotron uncreated_component_parts = list( /obj/item/stock_parts/radio/receiver ) @@ -21,7 +22,7 @@ /obj/machinery/emitter/gyrotron/anchored anchored = TRUE - state = 2 + construct_state = /decl/machine_construction/emitter/welded/gyrotron /obj/machinery/emitter/gyrotron/Initialize() set_extension(src, /datum/extension/local_network_member) @@ -39,11 +40,11 @@ change_power_consumption(mega_energy * GYRO_POWER, POWER_USE_ACTIVE) . = ..() -/obj/machinery/emitter/gyrotron/get_rand_burst_delay() - return rate*10 - /obj/machinery/emitter/gyrotron/get_burst_delay() - return rate*10 + return rate SECONDS + +/obj/machinery/emitter/gyrotron/get_shot_delay() + return rate SECONDS /obj/machinery/emitter/gyrotron/get_emitter_beam() var/obj/item/projectile/beam/emitter/beam = ..() diff --git a/code/modules/gemstones/_gemstone.dm b/code/modules/gemstones/_gemstone.dm index 02d698d4a13..7bee9c244a8 100644 --- a/code/modules/gemstones/_gemstone.dm +++ b/code/modules/gemstones/_gemstone.dm @@ -35,6 +35,8 @@ var/global/list/_available_gemstone_cuts SetName("[cut.adjective] [material.solid_name]") /obj/item/gemstone/get_single_monetary_worth() + if(worthless) + return 0 . = ..() * cut.worth_multiplier /obj/item/gemstone/attackby(obj/item/used_item, mob/user) diff --git a/code/modules/hydroponics/seed_packets.dm b/code/modules/hydroponics/seed_packets.dm index c86b81ef2e9..e0a70230689 100644 --- a/code/modules/hydroponics/seed_packets.dm +++ b/code/modules/hydroponics/seed_packets.dm @@ -25,6 +25,8 @@ add_to_reagents(/decl/material/liquid/oil/plant, 3) /obj/item/seeds/get_single_monetary_worth() + if(worthless) + return 0 . = seed ? seed.get_monetary_value() : ..() // Used for extracts/seed sampling purposes. diff --git a/code/modules/hydroponics/trays/tray_process.dm b/code/modules/hydroponics/trays/tray_process.dm index d692dc841a5..8491df96a2c 100644 --- a/code/modules/hydroponics/trays/tray_process.dm +++ b/code/modules/hydroponics/trays/tray_process.dm @@ -65,8 +65,8 @@ mutate((rand(100) < 15) ? 2 : 1) mutation_level = 0 - if(pollen < 10) - pollen += seed?.produces_pollen + if(pollen < MAX_POLLEN_PER_FLOWER) + pollen += seed?.produces_pollen * POLLEN_PRODUCTION_MULT // Maintain tray nutrient and water levels. if(seed.get_trait(TRAIT_REQUIRES_NUTRIENTS) && seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) > 0 && nutrilevel > 0 && prob(25)) diff --git a/code/modules/fishing/bait.dm b/code/modules/hydroponics/worm.dm similarity index 100% rename from code/modules/fishing/bait.dm rename to code/modules/hydroponics/worm.dm diff --git a/code/modules/lighting/ambient_turf.dm b/code/modules/lighting/ambient_turf.dm index 60992f1fcb5..aeceed3dbd6 100644 --- a/code/modules/lighting/ambient_turf.dm +++ b/code/modules/lighting/ambient_turf.dm @@ -18,7 +18,11 @@ ambient_light = isnull(color) ? ambient_light : color ambient_light_multiplier = isnull(multiplier) ? ambient_light_multiplier : multiplier - update_ambient_light() + // If we haven't initialized our corners yet, do that instead to avoid ambience double-init. + if (!corners || !lighting_corners_initialised) + generate_missing_corners() + else + update_ambient_light() /// Replace one ambient light with another. This is effectively a delta update, but it can be used to pretend that our one channel is doing color blending. /turf/proc/replace_ambient_light(old_color, new_color, old_multiplier, new_multiplier = 0) @@ -79,13 +83,12 @@ ambient_light_old_g += lg ambient_light_old_b += lb - if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) - if (!corners || !lighting_corners_initialised) - generate_missing_corners() + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && (!corners || !lighting_corners_initialised)) + generate_missing_corners() - // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. - for (var/datum/lighting_corner/C in corners) - C.update_ambient_lumcount(lr, lg, lb, !update) + // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. + for (var/datum/lighting_corner/C in corners) + C.update_ambient_lumcount(lr, lg, lb, !update) if (!ambient_active) SSlighting.total_ambient_turfs += 1 diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index ea6f99e9a91..474b5acd641 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -22,6 +22,11 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/turf/t4 var/t4i + /// If a connection for z-lights exists, the corner above us. + var/datum/lighting_corner/above_corner + /// If a connection for z-lights exists, the corner below us. + var/datum/lighting_corner/below_corner + var/list/datum/light_source/affecting // Light sources affecting us. var/active = FALSE // TRUE if one of our masters has dynamic lighting. @@ -34,7 +39,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/self_g = 0 var/self_b = 0 - // The intensity we're inheriting from the turf below us, if we're a Z-turf. This is a sum of all below turfs in the Z-stack. + // The intensity we're inheriting from the turfs below us, if we're a Z-turf. This is a sum of all below turfs. var/below_r = 0 var/below_g = 0 var/below_b = 0 @@ -44,7 +49,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/ambient_g = 0 var/ambient_b = 0 - // The turf above us' ambient + // The turf above us' ambient values. var/above_ambient_r = 0 var/above_ambient_g = 0 var/above_ambient_b = 0 @@ -61,7 +66,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/cache_b = 0 var/cache_mx = 0 -/datum/lighting_corner/New(turf/new_turf, diagonal, oi) +/datum/lighting_corner/New(turf/new_turf, diagonal, oi, direction = LIGHTING_CORNER_GENERATE_BOTH) SSlighting.total_lighting_corners += 1 var/has_ambience = FALSE @@ -125,9 +130,10 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, if (TURF_IS_AMBIENT_LIT_UNSAFE(T)) has_ambience = TRUE - update_active() if (has_ambience) init_ambient() + generate_z_connections(direction) + update_active() #define OVERLAY_PRESENT(T) (T && T.lighting_overlay) @@ -144,6 +150,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, #define UPDATE_APPARENT(T, CH) T.apparent_##CH = T.self_##CH + T.below_##CH + T.ambient_##CH + T.above_ambient_##CH +// Configure ambient lighting for *just* this corner. This deliberately does not handle Z-propagation, that's managed by generate_z_connections(). /datum/lighting_corner/proc/init_ambient() var/sum_r = 0 var/sum_g = 0 @@ -171,7 +178,187 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, sum_g /= 4 sum_b /= 4 - update_ambient_lumcount(sum_r, sum_g, sum_b) + ambient_r += sum_r + ambient_g += sum_g + ambient_b += sum_b + + UPDATE_APPARENT(src, r) + UPDATE_APPARENT(src, g) + UPDATE_APPARENT(src, b) + + if (!needs_update) + needs_update = TRUE + SSlighting.corner_queue += src + +/datum/lighting_corner/proc/generate_z_connections(direction = LIGHTING_CORNER_GENERATE_BOTH) + ASSERT(z != null) + /* + ZM_ALLOW_LIGHTING means that a z-turf is lighting-connected to the turf below it. + So: + Upward: check if above and above is ALLOW_LIGHTING + Downward: check if self is ALLOW_LIGHTING and below + + This corner will be shared by all four of its turfs, so it doesn't matter which condition passes. + The above/below corners should be created if the master has a Z-connection and is ALLOW_LIGHTING, regardless of if it's actually dynamic. This allows light to shine + through Z-turfs that are themselves not dynamic. + */ + + // BOTH is 0, so it's true for both conditions. + // Sometimes we need to only generate going upwards (or downwards); if this corner was created by another corner using this proc, then generating downward is invalid and causes infinite recursion. + // We still need to scan downward (or upward) to find the new connection in this case though. + #define GOING_UP (direction > LIGHTING_CORNER_GENERATE_DOWN) + #define GOING_DOWN (direction < LIGHTING_CORNER_GENERATE_UP) + + var/turf/T + + var/datum/lighting_corner/old_above_corner = above_corner + var/datum/lighting_corner/old_below_corner = below_corner + + /* + This brick is responsible for finding the corner that's directly above us, and forcibly generating the corner if it doesn't exist yet. + It's just the same block of code repeated four times (for each master), plus the case of there now being no above corner, but previously having had one. + We also only initialize the one corner we need rather than all four since there's no benefit to initializing them all -- if a true light needs them, it'll make them itself. + + Nebula specific: due to lighting on edges of z-levels wrapping around, this logic needs to exclude masters that are on a different Z-level. + This logic assumes that all (up to) four masters of this corner are equivalent, but this is not true of corners found via z-level transition boundaries. + Turfs with transition corners should have at least one non-transition corner, so we just ignore them. + */ + if (t1?.z == z && (T = t1.above || GET_ABOVE(t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + if (!(above_corner = T.corners?[t1i]) && GOING_UP) + if (!T.corners) + T.corners = new(4) + T.corners[t1i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t1i], t1i, LIGHTING_CORNER_GENERATE_UP) + above_corner = T.corners[t1i] + else if (t2?.z == z && (T = t2.above || GET_ABOVE(t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + if (!(above_corner = T.corners?[t2i]) && GOING_UP) + if (!T.corners) + T.corners = new(4) + T.corners[t2i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t2i], t2i, LIGHTING_CORNER_GENERATE_UP) + above_corner = T.corners[t2i] + else if (t3?.z == z && (T = t3.above || GET_ABOVE(t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + if (!(above_corner = T.corners?[t3i]) && GOING_UP) + if (!T.corners) + T.corners = new(4) + T.corners[t3i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t3i], t3i, LIGHTING_CORNER_GENERATE_UP) + above_corner = T.corners[t3i] + else if (t4?.z == z && (T = t4.above || GET_ABOVE(t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) + if (!(above_corner = T.corners?[t4i]) && GOING_UP) + if (!T.corners) + T.corners = new(4) + T.corners[t4i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t4i], t4i, LIGHTING_CORNER_GENERATE_UP) + above_corner = T.corners[t4i] + else if (above_corner) // connected -> disconnected transition + /* + The corner directly above us contains the sum of the light that comes from us and everything below us, which is conveniently everything that we need to remove. + We iterate up through the stack removing only the above_corner's light contribution, as to not disturb light sourced from other turfs higher in the stack. + */ + for (var/datum/lighting_corner/corn = above_corner; corn; corn = corn.above_corner) + corn.below_r -= above_corner.below_r + corn.below_g -= above_corner.below_g + corn.below_b -= above_corner.below_b + + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) + + if (!corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn + + above_corner.below_corner = null + above_corner = null + + if (!old_above_corner && above_corner) // disconnected -> connected transition + if (!(apparent_r == apparent_g == apparent_b == 0)) + for (var/datum/lighting_corner/corn = above_corner; corn; corn = corn.above_corner) + // We can't just steal the precomputed value from the above like we can in the removal case: our effect on the turf above us is our own self-light plus the light below us. + corn.below_r += src.below_r + src.self_r + corn.below_g += src.below_g + src.self_g + corn.below_b += src.below_b + src.self_b + + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) + + if (!corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn + + // As above, so below. The ordering here is a bit different from the above block, check the comment at the top of this proc. + if (t1?.z == z && (t1.z_flags & ZM_ALLOW_LIGHTING) && (T = t1.below || GET_BELOW(t1))) + if (!(below_corner = T.corners?[t1i]) && GOING_DOWN) + if (!T.corners) + T.corners = new(4) + T.corners[t1i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t1i], t1i, LIGHTING_CORNER_GENERATE_DOWN) + below_corner = T.corners[t1i] + else if (t2?.z == z && (t2.z_flags & ZM_ALLOW_LIGHTING) && (T = t2.below || GET_BELOW(t2))) + if (!(below_corner = T.corners?[t2i]) && GOING_DOWN) + if (!T.corners) + T.corners = new(4) + T.corners[t2i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t2i], t2i, LIGHTING_CORNER_GENERATE_DOWN) + below_corner = T.corners[t2i] + else if (t3?.z == z && (t3.z_flags & ZM_ALLOW_LIGHTING) && (T = t3.below || GET_BELOW(t3))) + if (!(below_corner = T.corners?[t3i]) && GOING_DOWN) + if (!T.corners) + T.corners = new(4) + T.corners[t3i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t3i], t3i, LIGHTING_CORNER_GENERATE_DOWN) + below_corner = T.corners[t3i] + else if (t4?.z == z && (t4.z_flags & ZM_ALLOW_LIGHTING) && (T = t4.below || GET_BELOW(t4))) + if (!(below_corner = T.corners?[t4i]) && GOING_DOWN) + if (!T.corners) + T.corners = new(4) + T.corners[t4i] = new/datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[t4i], t4i, LIGHTING_CORNER_GENERATE_DOWN) + below_corner = T.corners[t4i] + else if (below_corner) // connected -> disconnected transition + /* + Similar case to above, but not quite the same. + The corner below us' `above_ambient_*` var contins both our contributed light as well as turfs above us, so just subtract that instead of combining both vars manually. + */ + for (var/datum/lighting_corner/corn = below_corner; corn; corn = corn.below_corner) + corn.above_ambient_r -= below_corner.above_ambient_r + corn.above_ambient_g -= below_corner.above_ambient_g + corn.above_ambient_b -= below_corner.above_ambient_b + + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) + + if (!corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn + + below_corner.above_corner = null + below_corner = null + + if (!old_below_corner && below_corner) // disconnected -> connected transition + // quick and dirty heuristic to avoid checking a bunch of different vars, it's still valid if we needlessly run this + if (!(apparent_r == apparent_g == apparent_b == 0)) + for (var/datum/lighting_corner/corn = below_corner; corn; corn = corn.below_corner) + // As with above, we can't just steal the precomputed value from our neighbor. Our effect will be the sum effect of turfs above us, plus our effect. + corn.above_ambient_r += src.above_ambient_r + src.ambient_r + corn.above_ambient_g += src.above_ambient_g + src.ambient_g + corn.above_ambient_b += src.above_ambient_b + src.ambient_b + + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) + + if (!corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn + + if (above_corner) + ASSERT(x == above_corner.x) + ASSERT(y == above_corner.y) + ASSERT(z == above_corner.z - 1) + + if (below_corner) + ASSERT(x == below_corner.x) + ASSERT(y == below_corner.y) + ASSERT(z == below_corner.z + 1) + +#undef GOING_UP +#undef GOING_DOWN // God that was a mess, now to do the rest of the corner code! Hooray! /datum/lighting_corner/proc/update_lumcount(delta_r, delta_g, delta_b, now = FALSE) @@ -186,29 +373,19 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, UPDATE_APPARENT(src, g) UPDATE_APPARENT(src, b) - var/turf/T - var/Ti - // Grab the first master that's a Z-turf, if one exists. - // The above var cannot be relied on due to init ordering, but we can use it if it is set. - if (t1 && (T = t1.above || GET_ABOVE(t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = t1i - else if (t2 && (T = t2.above || GET_ABOVE(t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = t2i - else if (t3 && (T = t3.above || GET_ABOVE(t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = t3i - else if (t4 && (T = t4.above || GET_ABOVE(t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = t4i - else // Nothing above us that cares about below light. - T = null - - if (TURF_IS_DYNAMICALLY_LIT(T)) - do - if (!T.corners || !T.corners[Ti]) - T.generate_missing_corners() - - // Above corners never get instant updates; they're less important, so better to avoid risk of lag. - T.corners[Ti].update_below_lumcount(delta_r, delta_g, delta_b) - while ((T = T.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) + for (var/datum/lighting_corner/corn = above_corner; corn; corn = corn.above_corner) + corn.below_r += delta_r + corn.below_g += delta_g + corn.below_b += delta_b + + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) + + // These are always queued, players are far less likely to notice these being a little behind. + if (!corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn // This needs to be down here instead of the above if so the lum values are properly updated. if (needs_update) @@ -220,25 +397,6 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, needs_update = TRUE SSlighting.corner_queue += src -/datum/lighting_corner/proc/update_below_lumcount(delta_r, delta_g, delta_b) - if (!(delta_r + delta_g + delta_b)) - return - - below_r += delta_r - below_g += delta_g - below_b += delta_b - - UPDATE_APPARENT(src, r) - UPDATE_APPARENT(src, g) - UPDATE_APPARENT(src, b) - - // This needs to be down here instead of the above if so the lum values are properly updated. - if (needs_update) - return - - needs_update = TRUE - SSlighting.corner_queue += src - /datum/lighting_corner/proc/update_ambient_lumcount(delta_r, delta_g, delta_b, skip_update = FALSE) ambient_r += delta_r @@ -249,46 +407,18 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, UPDATE_APPARENT(src, g) UPDATE_APPARENT(src, b) - var/turf/T - var/Ti - - if (t1) - T = t1 - Ti = t1i - else if (t2) - T = t2 - Ti = t2i - else if (t3) - T = t3 - Ti = t3i - else if (t4) - T = t4 - Ti = t4i - else - // This should be impossible to reach -- how do we exist without at least one master turf? - CRASH("Corner has no masters!") - - var/datum/lighting_corner/below = src - - // 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)) - if (!T.corners || !T.corners[Ti]) - T.generate_missing_corners() + for (var/datum/lighting_corner/corn = below_corner; corn; corn = corn.below_corner) + corn.above_ambient_r += delta_r + corn.above_ambient_g += delta_g + corn.above_ambient_b += delta_b - ASSERT(T.corners?.len) + UPDATE_APPARENT(corn, r) + UPDATE_APPARENT(corn, g) + UPDATE_APPARENT(corn, b) - below = T.corners[Ti] - below.above_ambient_r += delta_r - below.above_ambient_g += delta_g - below.above_ambient_b += delta_b - - UPDATE_APPARENT(below, r) - UPDATE_APPARENT(below, g) - UPDATE_APPARENT(below, b) - - if (!skip_update && !below.needs_update) - below.needs_update = TRUE - SSlighting.corner_queue += below + if (!skip_update && !corn.needs_update) + corn.needs_update = TRUE + SSlighting.corner_queue += corn if (needs_update || skip_update) return @@ -302,7 +432,7 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, var/lg = apparent_g var/lb = apparent_b - // Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually. + // Cache these values ahead of time so 4 individual lighting overlays don't all calculate them individually. var/mx = max(lr, lg, lb) // Scale it so 1 is the strongest lum, if it is above 1. . = 1 // factor if (mx > 1) @@ -331,75 +461,6 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, Ov.needs_update = TRUE SSlighting.overlay_queue += Ov -// This is called when our turf's downward Z-opacity changes. -/datum/lighting_corner/proc/rebuild_ztraversal(new_opacity) - /* - If opacity transitions to off: - - Look down stack, removing below lights contributing to us - - Look up stack, updating turfs with our new below lighting value - If opacity transitions to on: - - Look down stack, add below contributors - - Look up stack, updating turfs with our new below lighting value - */ - below_r = below_g = below_b = 0 - var/turf/T = null - var/datum/lighting_corner/Tcorn = src - var/Ti - - if (!new_opacity) - for (;;) - if (Tcorn.t1 && (T = Tcorn.t1.below || GET_BELOW(Tcorn.t1)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t1i - else if (Tcorn.t2 && (T = Tcorn.t2.below || GET_BELOW(Tcorn.t2)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t2i - else if (Tcorn.t3 && (T = Tcorn.t3.below || GET_BELOW(Tcorn.t3)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t3i - else if (Tcorn.t4 && (T = Tcorn.t4.below || GET_BELOW(Tcorn.t4)) && (T.above?.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t4i - else // Nothing above us that cares about below light. - break - - Tcorn = T.corners[Ti] - below_r += Tcorn.apparent_r - below_g += Tcorn.apparent_g - below_b += Tcorn.apparent_b - - UPDATE_APPARENT(src, r) - UPDATE_APPARENT(src, g) - UPDATE_APPARENT(src, b) - - if (!needs_update) - needs_update = TRUE - SSlighting.corner_queue += src - - T = null - Tcorn = src - - for (;;) - if (Tcorn.t1 && (T = Tcorn.t1.above || GET_ABOVE(Tcorn.t1)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t1i - else if (Tcorn.t2 && (T = Tcorn.t2.above || GET_ABOVE(Tcorn.t2)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t2i - else if (Tcorn.t3 && (T = Tcorn.t3.above || GET_ABOVE(Tcorn.t3)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t3i - else if (Tcorn.t4 && (T = Tcorn.t4.above || GET_ABOVE(Tcorn.t4)) && (T.z_flags & ZM_ALLOW_LIGHTING)) - Ti = Tcorn.t4i - else // Nothing above us that cares about below light. - break - - Tcorn = T.corners[Ti] - Tcorn.below_r += apparent_r - Tcorn.below_g += apparent_g - Tcorn.below_b += apparent_b - - UPDATE_APPARENT(Tcorn, r) - UPDATE_APPARENT(Tcorn, g) - UPDATE_APPARENT(Tcorn, b) - - if (!Tcorn.needs_update) - Tcorn.needs_update = TRUE - SSlighting.corner_queue += Tcorn - /datum/lighting_corner/Destroy(force = FALSE) PRINT_STACK_TRACE("Someone [force ? "force-" : ""]deleted a lighting corner.") if (!force) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 84bccc89757..911e71de89d 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -347,6 +347,7 @@ var/test_y var/should_do_wedge = light_angle && !facing_opaque + var/is_dyn_or_adj FOR_DVIEW(T, NONUNIT_CEILING(actual_range, 1), source_turf, 0) do if (should_do_wedge) // Directional lighting coordinate filter. @@ -357,9 +358,17 @@ 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 we're shining a light from a static lit turf onto a dynamic lit one, we do actually want to create corners to light that turf. + // These checks are inlined from generate_missing_corners. They must be kept (roughly) in sync. This one intentionally does not check for ambient turfs. + is_dyn_or_adj = TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) + if (!is_dyn_or_adj) + for (var/turf/Tneigh as anything in RANGE_TURFS(T, 1)) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(Tneigh)) + is_dyn_or_adj = TRUE + break + 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 (is_dyn_or_adj) if (!T.lighting_corners_initialised) T.lighting_corners_initialised = TRUE diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 5543d1166c1..900edbcc531 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -143,8 +143,14 @@ // This is inlined in lighting_source.dm. // Update it too if you change this. /turf/proc/generate_missing_corners() - // 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)) + var/is_dyn = TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) || ambient_light + if (!is_dyn) + for (var/turf/Tneigh as anything in RANGE_TURFS(src, 1)) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(Tneigh)) + is_dyn = TRUE + break + + if (!is_dyn) return lighting_corners_initialised = TRUE diff --git a/code/modules/maps/reader.dm b/code/modules/maps/reader.dm index 1704be274b2..87b26dd61cb 100644 --- a/code/modules/maps/reader.dm +++ b/code/modules/maps/reader.dm @@ -140,8 +140,10 @@ var/global/dmm_suite/preloader/_preloader = new if(zexpansion && !measureOnly) // don't actually expand the world if we're only measuring bounds if(cropMap) continue - while(world.maxz < zcrd) //create new z_levels if needed. - SSmapping.increment_world_z_size(level_data_type) + var/desired_levels = zcrd - world.maxz + if (desired_levels > 0) //create new z_levels if needed. + SSmapping.bulk_increment_world_z_size(desired_levels, level_data_type) + bounds[MAP_MINX] = min(bounds[MAP_MINX], clamp(xcrdStart, x_lower, x_upper)) bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd) bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd) diff --git a/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm b/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm index d6ecd767659..1c4e55d0dd8 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_types/grass.dm @@ -158,26 +158,3 @@ forced_ambience = list( 'sound/ambience/jungle.ogg' ) - fishing_failure_prob = 10 - // TODO: waterweed? - // Hardcoding the contents of /obj/random/natural_debris to avoid hacks to get results out of /obj/random. - fishing_results = list( - /mob/living/simple_animal/aquatic/fish = 10, - /mob/living/simple_animal/aquatic/fish/grump = 10, - /obj/item/mollusc = 5, - /obj/item/mollusc/barnacle/fished = 5, - /mob/living/simple_animal/aquatic/fish/large = 5, - /mob/living/simple_animal/aquatic/fish/large/bass = 5, - /mob/living/simple_animal/aquatic/fish/large/salmon = 5, - /mob/living/simple_animal/aquatic/fish/large/trout = 5, - /mob/living/simple_animal/aquatic/fish/large/pike = 3, - /mob/living/simple_animal/aquatic/fish/large/javelin = 3, - /obj/item/mollusc/clam/fished/pearl = 3, - /obj/item/trash/mollusc_shell/clam = 2, - /obj/item/trash/mollusc_shell/barnacle = 2, - /obj/item/remains/mouse = 2, - /obj/item/remains/lizard = 2, - /obj/item/stick = 1, - /obj/item/trash/mollusc_shell = 1, - /mob/living/simple_animal/aquatic/fish/large/koi = 1 - ) diff --git a/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm b/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm index 6964c4dbd2f..a287d10ecf7 100644 --- a/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm +++ b/code/modules/maps/template_types/random_exoplanet/random_planet_areas.dm @@ -1,5 +1,6 @@ ///Windy surface /area/exoplanet + // not abstract, this can get instantiated name = "\improper Planetary surface" ambience = list( 'sound/effects/wind/wind_2_1.ogg', @@ -13,14 +14,6 @@ area_flags = AREA_FLAG_IS_BACKGROUND | AREA_FLAG_EXTERNAL | AREA_FLAG_HIDE_FROM_HOLOMAP is_outside = OUTSIDE_YES -// Let's make a token effort at making the fish somewhat alien I guess. -/area/exoplanet/get_fishing_result(turf/origin, obj/item/food/bait) - . = ..() - if(ismob(.)) - var/mob/M = . - M.SetName("xeno-[M.name]") - M.set_color(get_random_colour(simple = TRUE)) - ///Spoopy undergrounds /area/exoplanet/underground name = "\improper Planetary mantle" diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index e515e82710a..64b8e7af1ab 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -278,7 +278,7 @@ var/global/list/materials_by_gas_symbol = list() var/sound_manipulate //Default sound something like a material stack made of this material does when picked up var/sound_dropped //Default sound something like a material stack made of this material does when hitting the ground or placed down - var/holographic // Set to true if this material is fake/visual only. + var/visual_only // Set to true if this material is fake/visual only. Can be used for holograms, placeholders, etc. /// Does high temperature baking change this material into something else? var/bakes_into_material @@ -286,9 +286,7 @@ var/global/list/materials_by_gas_symbol = list() /// If set to a material type, stacks of this material will be able to be tanned on a drying rack after being wetted to convert them to tans_to. var/tans_to - /// A multiplier for this material when used in fishing bait. - var/fishing_bait_value = 0 - /// A relative value used only by fishing line at time of commit. + /// A relative value used only by bowstrings and the fishing modpack at time of writing. var/tensile_strength = 0 /// What form does this take if dug out of the ground, if any? @@ -328,6 +326,9 @@ var/global/list/materials_by_gas_symbol = list() var/forgable = FALSE // Can this material be forged in bar/billet form? + // Physical attacks against walls must beat this threshold to cause damage. + var/wall_damage_threshold = 2 + // Used by walls when qdel()ing to avoid neighbor merging. /decl/material/placeholder name = "placeholder" @@ -335,7 +336,7 @@ var/global/list/materials_by_gas_symbol = list() hidden_from_codex = TRUE exoplanet_rarity_plant = MAT_RARITY_NOWHERE exoplanet_rarity_gas = MAT_RARITY_NOWHERE - holographic = TRUE + visual_only = TRUE // Make sure we have a use name and shard icon even if they aren't explicitly set. /decl/material/Initialize() @@ -353,7 +354,7 @@ var/global/list/materials_by_gas_symbol = list() adjective_name ||= use_name // Null/clear a bunch of physical vars as this material is fake. - if(holographic) + if(visual_only) temperature_burn_milestone_material = null can_boil_to_gas = FALSE shard_name = SHARD_NONE @@ -405,7 +406,7 @@ var/global/list/materials_by_gas_symbol = list() global.materials_by_gas_symbol[gas_symbol] = type generate_armor_values() - if(!holographic) + if(!visual_only) var/list/cocktails = decls_repository.get_decls_of_subtype(/decl/cocktail) for(var/ctype in cocktails) var/decl/cocktail/cocktail = cocktails[ctype] diff --git a/code/modules/materials/definitions/solids/materials_solid_alien.dm b/code/modules/materials/definitions/solids/materials_solid_alien.dm index 8b4c33935d4..382e3b27c48 100644 --- a/code/modules/materials/definitions/solids/materials_solid_alien.dm +++ b/code/modules/materials/definitions/solids/materials_solid_alien.dm @@ -12,6 +12,7 @@ default_solid_form = /obj/item/stack/material/cubes exoplanet_rarity_plant = MAT_RARITY_EXOTIC exoplanet_rarity_gas = MAT_RARITY_NOWHERE + wall_damage_threshold = 20 /decl/material/solid/metal/aliumium/Initialize() icon_base = 'icons/turf/walls/metal.dmi' diff --git a/code/modules/materials/definitions/solids/materials_solid_butchery.dm b/code/modules/materials/definitions/solids/materials_solid_butchery.dm index fb78922a5f6..846edc4f50d 100644 --- a/code/modules/materials/definitions/solids/materials_solid_butchery.dm +++ b/code/modules/materials/definitions/solids/materials_solid_butchery.dm @@ -19,7 +19,6 @@ sound_manipulate = 'sound/foley/meat1.ogg' sound_dropped = 'sound/foley/meat2.ogg' hitsound = 'sound/effects/squelch1.ogg' - fishing_bait_value = 1 reagent_overlay = "soup_chunks" nutriment_factor = 10 allergen_flags = ALLERGEN_MEAT @@ -83,7 +82,6 @@ sound_manipulate = 'sound/foley/meat1.ogg' sound_dropped = 'sound/foley/meat2.ogg' hitsound = "punch" - fishing_bait_value = 0.75 tans_to = /decl/material/solid/organic/leather compost_value = 0.8 allergen_flags = ALLERGEN_MEAT @@ -116,7 +114,6 @@ default_solid_form = /obj/item/stack/material/skin/pelt sound_manipulate = 'sound/foley/paperpickup2.ogg' sound_dropped = 'sound/foley/paperpickup1.ogg' - fishing_bait_value = 0 paint_verb = "dyed" /decl/material/solid/organic/skin/fur/gray @@ -178,7 +175,6 @@ default_solid_form = /obj/item/stack/material/skin/feathers sound_manipulate = 'sound/foley/paperpickup2.ogg' sound_dropped = 'sound/foley/paperpickup1.ogg' - fishing_bait_value = 0 /decl/material/solid/organic/skin/feathers/purple color = COLOR_PALE_PURPLE_GRAY diff --git a/code/modules/materials/definitions/solids/materials_solid_exotic.dm b/code/modules/materials/definitions/solids/materials_solid_exotic.dm index a7ac24e2f33..d9ee4b2b9fc 100644 --- a/code/modules/materials/definitions/solids/materials_solid_exotic.dm +++ b/code/modules/materials/definitions/solids/materials_solid_exotic.dm @@ -30,6 +30,7 @@ default_solid_form = /obj/item/stack/material/segment exoplanet_rarity_plant = MAT_RARITY_EXOTIC exoplanet_rarity_gas = MAT_RARITY_NOWHERE + wall_damage_threshold = 20 /decl/material/solid/exotic_matter name = "exotic matter" @@ -65,3 +66,4 @@ default_solid_form = /obj/item/stack/material/segment exoplanet_rarity_plant = MAT_RARITY_EXOTIC exoplanet_rarity_gas = MAT_RARITY_NOWHERE + wall_damage_threshold = 10 diff --git a/code/modules/materials/definitions/solids/materials_solid_fission.dm b/code/modules/materials/definitions/solids/materials_solid_fission.dm index f68c8c85673..066c788dbc0 100644 --- a/code/modules/materials/definitions/solids/materials_solid_fission.dm +++ b/code/modules/materials/definitions/solids/materials_solid_fission.dm @@ -27,7 +27,8 @@ fission_heat = 35000 fission_energy = 4000 neutron_absorption = 4 - + wall_damage_threshold = 20 + /decl/material/solid/metal/neptunium // Np-237. name = "neptunium" diff --git a/code/modules/materials/definitions/solids/materials_solid_gemstones.dm b/code/modules/materials/definitions/solids/materials_solid_gemstones.dm index d9a0253e53a..f0c2504e759 100644 --- a/code/modules/materials/definitions/solids/materials_solid_gemstones.dm +++ b/code/modules/materials/definitions/solids/materials_solid_gemstones.dm @@ -18,6 +18,7 @@ hardness = MAT_VALUE_VERY_HARD reflectiveness = MAT_VALUE_VERY_SHINY construction_difficulty = MAT_VALUE_VERY_HARD_DIY + wall_damage_threshold = 10 /decl/material/solid/gemstone/diamond name = "diamond" diff --git a/code/modules/materials/definitions/solids/materials_solid_ice.dm b/code/modules/materials/definitions/solids/materials_solid_ice.dm index fc679cc6a9b..6133c3758bb 100644 --- a/code/modules/materials/definitions/solids/materials_solid_ice.dm +++ b/code/modules/materials/definitions/solids/materials_solid_ice.dm @@ -20,6 +20,7 @@ heating_products = list( /decl/material/liquid/water = 1 ) + wall_damage_threshold = 5 /decl/material/solid/ice/Initialize() liquid_name ||= "liquid [name]" // avoiding the 'molten ice' issue diff --git a/code/modules/materials/definitions/solids/materials_solid_metal.dm b/code/modules/materials/definitions/solids/materials_solid_metal.dm index 51f1a2db4bf..19c069e9bc5 100644 --- a/code/modules/materials/definitions/solids/materials_solid_metal.dm +++ b/code/modules/materials/definitions/solids/materials_solid_metal.dm @@ -20,6 +20,8 @@ icon_reinf = 'icons/turf/walls/reinforced_metal.dmi' exoplanet_rarity_gas = MAT_RARITY_NOWHERE tensile_strength = 0.8 // metal wire is probably better than plastic? + wall_damage_threshold = 10 + hitsound = 'sound/effects/metalhit.ogg' forgable = TRUE glows_with_heat = TRUE @@ -220,7 +222,7 @@ /decl/material/solid/metal/steel/holographic name = "holographic steel" uid = "solid_holographic_steel" - holographic = TRUE + visual_only = TRUE /decl/material/solid/metal/stainlesssteel name = "stainless steel" @@ -267,7 +269,7 @@ /decl/material/solid/metal/aluminium/holographic name = "holoaluminium" uid = "solid_holographic_aluminium" - holographic = TRUE + visual_only = TRUE /decl/material/solid/metal/plasteel name = "plasteel" diff --git a/code/modules/materials/definitions/solids/materials_solid_mineral.dm b/code/modules/materials/definitions/solids/materials_solid_mineral.dm index 40396f7316e..73a833e3665 100644 --- a/code/modules/materials/definitions/solids/materials_solid_mineral.dm +++ b/code/modules/materials/definitions/solids/materials_solid_mineral.dm @@ -19,6 +19,7 @@ ) ore_type_value = ORE_NUCLEAR ore_data_value = 3 + wall_damage_threshold = 5 /decl/material/solid/graphite name = "graphite" diff --git a/code/modules/materials/definitions/solids/materials_solid_mundane.dm b/code/modules/materials/definitions/solids/materials_solid_mundane.dm index 039405d9506..eaed6454831 100644 --- a/code/modules/materials/definitions/solids/materials_solid_mundane.dm +++ b/code/modules/materials/definitions/solids/materials_solid_mundane.dm @@ -23,3 +23,4 @@ /decl/material/gas/sulfur_dioxide = 0.05, /decl/material/gas/carbon_dioxide = 0.05 ) + wall_damage_threshold = 10 diff --git a/code/modules/materials/definitions/solids/materials_solid_organic.dm b/code/modules/materials/definitions/solids/materials_solid_organic.dm index 7bfb29fb0ab..2d5507831bc 100644 --- a/code/modules/materials/definitions/solids/materials_solid_organic.dm +++ b/code/modules/materials/definitions/solids/materials_solid_organic.dm @@ -12,6 +12,7 @@ bakes_into_at_temperature = T0C+500 bakes_into_material = /decl/material/solid/carbon */ + wall_damage_threshold = 5 /decl/material/solid/organic/plastic name = "plastic" @@ -75,7 +76,7 @@ /decl/material/solid/organic/plastic/holographic name = "holographic plastic" uid = "solid_holographic_plastic" - holographic = TRUE + visual_only = TRUE /decl/material/solid/organic/cardboard name = "cardboard" @@ -216,7 +217,6 @@ dug_drop_type = /obj/item/stack/material/slab sound_manipulate = 'sound/foley/paperpickup2.ogg' sound_dropped = 'sound/foley/paperpickup1.ogg' - fishing_bait_value = 0.75 allergen_flags = ALLERGEN_VEGETABLE exoplanet_rarity_plant = MAT_RARITY_MUNDANE diff --git a/code/modules/materials/definitions/solids/materials_solid_stone.dm b/code/modules/materials/definitions/solids/materials_solid_stone.dm index 07615998549..de026d67404 100644 --- a/code/modules/materials/definitions/solids/materials_solid_stone.dm +++ b/code/modules/materials/definitions/solids/materials_solid_stone.dm @@ -25,6 +25,7 @@ ore_result_amount = 4 sound_manipulate = 'sound/foley/rockscrape.ogg' sound_dropped = 'sound/foley/rockscrape.ogg' + wall_damage_threshold = 10 var/image/texture /decl/material/solid/stone/Initialize() diff --git a/code/modules/materials/definitions/solids/materials_solid_wood.dm b/code/modules/materials/definitions/solids/materials_solid_wood.dm index a20bd4d5a98..9e44c5f6d15 100644 --- a/code/modules/materials/definitions/solids/materials_solid_wood.dm +++ b/code/modules/materials/definitions/solids/materials_solid_wood.dm @@ -56,6 +56,7 @@ compost_value = 0.2 paint_verb = "stained" liquid_name = "wood pulp" + wall_damage_threshold = 8 /decl/material/solid/organic/wood/oak name = "oak" @@ -81,7 +82,7 @@ uid = "solid_holographic_wood" color = WOOD_COLOR_CHOCOLATE //the very concept of wood should be brown adjective_name = "holowood" - holographic = TRUE + visual_only = TRUE /decl/material/solid/organic/wood/mahogany name = "mahogany" diff --git a/code/modules/materials/stack_types/material_stack_ore.dm b/code/modules/materials/stack_types/material_stack_ore.dm index 391166a8bb1..3bff7ccab9f 100644 --- a/code/modules/materials/stack_types/material_stack_ore.dm +++ b/code/modules/materials/stack_types/material_stack_ore.dm @@ -56,7 +56,7 @@ //Randomize the orientation and position of each ores in the image var/matrix/M = matrix() M.Translate(rand(-6, 6), rand(-6, 6)) - M.Turn(pick(-72, -58, -45, -27.-5, 0, 0, 0, 0, 0, 27.5, 45, 58, 72)) + M.Turn(pick(-72, -58, -45, -27.5, 0, 0, 0, 0, 0, 27.5, 45, 58, 72)) var/image/oreoverlay = image('icons/obj/materials/ore.dmi', IS) oreoverlay.transform = M scrapboard.overlays += oreoverlay diff --git a/code/modules/mechs/_mech.dm b/code/modules/mechs/_mech.dm index 957fe6f236a..c49b2db28d0 100644 --- a/code/modules/mechs/_mech.dm +++ b/code/modules/mechs/_mech.dm @@ -260,3 +260,34 @@ if(current_user) return FALSE return ..() + +// Handling for auto-fire mechanic +/mob/living/exosuit/mob_can_autofire(obj/item/gun/autofiring, atom/autofiring_at) + if(!(autofiring in selected_system)) // Make sure the gun is still selected. + return FALSE + return ..() + +/mob/living/exosuit/proc/relayed_pilot_check(mob/user) + if(!user || incapacitated() || user.incapacitated()) + return FALSE + if(!(user in pilots) && user != src) + return FALSE + if(!selected_system) + return FALSE + return TRUE + +// TODO: make mechs use inventory slots so we can just call on_mouse_foo(). +/mob/living/exosuit/relayed_mouse_down(mob/user, object, location, control, params) + if(!relayed_pilot_check(user)) + return ..() + . = selected_system.wielder_mouse_drag_down(src, object, location, control, params) + +/mob/living/exosuit/relayed_mouse_held(mob/user, atom/target) + if(!relayed_pilot_check(user)) + return ..() + return selected_system.wielder_mouse_drag_held(src, target) + +/mob/living/exosuit/relayed_mouse_up(mob/user, atom/target) + if(!relayed_pilot_check(user)) + return ..() + return selected_system.wielder_mouse_drag_up(src, target) diff --git a/code/modules/mechs/equipment/_equipment.dm b/code/modules/mechs/equipment/_equipment.dm index c98994742c5..7dde002884e 100644 --- a/code/modules/mechs/equipment/_equipment.dm +++ b/code/modules/mechs/equipment/_equipment.dm @@ -72,15 +72,6 @@ /obj/item/mech_equipment/proc/get_effective_obj() return src -/obj/item/mech_equipment/proc/MouseDragInteraction() - return 0 - -/obj/item/mech_equipment/proc/MouseDownInteraction() - return 0 - -/obj/item/mech_equipment/proc/MouseUpInteraction() - return 0 - /obj/item/mech_equipment/mob_can_unequip(mob/user, slot, disable_warning = FALSE, dropping = FALSE) . = ..() if(. && owner) diff --git a/code/modules/mechs/equipment/combat_projectile.dm b/code/modules/mechs/equipment/combat_projectile.dm index a717954a893..fa562fcfe1a 100644 --- a/code/modules/mechs/equipment/combat_projectile.dm +++ b/code/modules/mechs/equipment/combat_projectile.dm @@ -109,56 +109,3 @@ material = /decl/material/solid/metal/steel ammo_type = /obj/item/ammo_casing/rifle max_ammo = 300 - -// Handling for auto-fire mechanic -/mob/living/exosuit/can_autofire(obj/item/gun/autofiring, atom/autofiring_at) - if(autofiring.autofiring_by != src) - return FALSE - var/client/C = current_user ? current_user.client : client - - if(!C || !C.mob || C.mob.incapacitated()) - return FALSE - - if(!(autofiring_at in view(C.view, src))) - return FALSE - if(!(get_dir(src, autofiring_at) & dir)) - return FALSE - if(!(autofiring in selected_system)) // Make sure the gun is still selected. - return FALSE - return TRUE - -/obj/item/mech_equipment/mounted_system/projectile/MouseDownInteraction(atom/object, location, control, params, mob/user) - var/obj/item/gun/gun = holding - if(istype(object) && (isturf(object) || isturf(object.loc)) && istype(gun)) - if(user != src) - if(!user.incapacitated()) - gun.set_autofire(object, owner, FALSE) // Passed gun-firer is still the exosuit since all checks need to be done on the suit. - owner.current_user = user - else - if(!owner.incapacitated()) - gun.set_autofire(object, owner, FALSE) - owner.current_user = null - -/obj/item/mech_equipment/mounted_system/projectile/MouseUpInteraction(atom/object, location, control, params, mob/user) - var/obj/item/gun/gun = holding - if(istype(gun)) - gun.clear_autofire() - if(owner) // In case the owning exosuit has been gibbed etc. - owner.current_user = null - -/obj/item/mech_equipment/mounted_system/projectile/MouseDragInteraction(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params, mob/user) - var/obj/item/gun/gun = holding - if(!owner) - gun?.clear_autofire() - return - if(!istype(gun)) - owner?.current_user = null - return - if(istype(over_object) && (isturf(over_object) || isturf(over_object.loc))) - if(user.incapacitated() || (user != owner && user != owner.current_user)) - gun.clear_autofire() - return - gun.set_autofire(over_object, owner, FALSE) - return - - gun.clear_autofire() diff --git a/code/modules/mechs/equipment/mounted_system.dm b/code/modules/mechs/equipment/mounted_system.dm index d8a62f70a8f..5bf0cce9c3b 100644 --- a/code/modules/mechs/equipment/mounted_system.dm +++ b/code/modules/mechs/equipment/mounted_system.dm @@ -39,3 +39,12 @@ /obj/item/mech_equipment/mounted_system/get_hardpoint_maptext() return (holding ? holding.get_hardpoint_maptext() : null) + +/obj/item/mech_equipment/mounted_system/wielder_mouse_drag_held(mob/user, atom/target) + return (holding ? holding.wielder_mouse_drag_held(user, target) : ..()) + +/obj/item/mech_equipment/mounted_system/wielder_mouse_drag_up(mob/user, atom/target) + return (holding ? holding.wielder_mouse_drag_up(user, target) : ..()) + +/obj/item/mech_equipment/mounted_system/wielder_mouse_drag_down(mob/user, object, location, control, params) + return (holding ? holding.wielder_mouse_drag_down(user, object, location, control, params) : ..()) diff --git a/code/modules/mechs/mech_interaction.dm b/code/modules/mechs/mech_interaction.dm index eb900899d02..abc0708cb39 100644 --- a/code/modules/mechs/mech_interaction.dm +++ b/code/modules/mechs/mech_interaction.dm @@ -9,52 +9,6 @@ return TRUE . = ..() -/mob/living/exosuit/RelayMouseDrag(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params, mob/user) - if(user && (user in pilots) && user.loc == src) - return OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, user) - return ..() - -/mob/living/exosuit/OnMouseDrag(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params, mob/user) - if(!user || incapacitated() || user.incapacitated()) - return FALSE - - if(!(user in pilots) && user != src) - return FALSE - - //This is handled at active module level really, it is the one who has to know if it's supposed to act - if(selected_system) - return selected_system.MouseDragInteraction(src_object, over_object, src_location, over_location, src_control, over_control, params, user) - -/mob/living/exosuit/RelayMouseDown(atom/object, location, control, params, mob/user) - if(user && (user in pilots) && user.loc == src) - return OnMouseDown(object, location, control, params, user) - return ..() - -/mob/living/exosuit/OnMouseDown(atom/object, location, control, params, mob/user) - if(!user || incapacitated() || user.incapacitated()) - return FALSE - - if(!(user in pilots) && user != src) - return FALSE - - if(selected_system) - return selected_system.MouseDownInteraction(object, location, control, params, user) - -/mob/living/exosuit/RelayMouseUp(atom/object, location, control, params, mob/user) - if(user && (user in pilots) && user.loc == src) - return OnMouseUp(object, location, control, params, user) - return ..() - -/mob/living/exosuit/OnMouseUp(atom/object, location, control, params, mob/user) - if(!user || incapacitated() || user.incapacitated()) - return FALSE - - if(!(user in pilots) && user != src) - return FALSE - - if(selected_system) - return selected_system.MouseUpInteraction(object, location, control, params, user) - /datum/click_handler/default/mech/OnClick(var/atom/A, var/params) var/mob/living/exosuit/E = user.loc if(!istype(E)) diff --git a/code/modules/mob/living/human/examine.dm b/code/modules/mob/living/human/examine.dm index 3ea28d2aada..077b4c67eb1 100644 --- a/code/modules/mob/living/human/examine.dm +++ b/code/modules/mob/living/human/examine.dm @@ -168,11 +168,13 @@ shown_objects += embedlist var/parsedembed[0] for(var/obj/embedded in embedlist) - if(!parsedembed.len || (!parsedembed.Find(embedded.name) && !parsedembed.Find("multiple [embedded.name]"))) - parsedembed.Add(embedded.name) - else if(!parsedembed.Find("multiple [embedded.name]")) - parsedembed.Remove(embedded.name) - parsedembed.Add("multiple "+embedded.name) + var/single_embed_string = "\a [embedded.name]" + var/plural_embed_string = "multiple [text_make_plural(embedded.name)]" + if(!parsedembed.len || (!parsedembed.Find(single_embed_string) && !parsedembed.Find(plural_embed_string))) + parsedembed.Add(single_embed_string) + else if(!parsedembed.Find(plural_embed_string)) + parsedembed.Remove(single_embed_string) + parsedembed.Add(plural_embed_string) wound_flavor_text[limb.organ_tag] += SPAN_WARNING("The [wound.desc] on [pronouns.his] [limb.name] has \a [english_list(parsedembed, and_text = " and a ", comma_text = ", a ")] sticking out of it!") if(limb.splinted && limb.splinted.loc == limb) diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index 4e60c2cc91d..5369b167113 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -688,11 +688,11 @@ if(!affecting) to_chat(user, SPAN_WARNING("\The [src] is missing that limb.")) - return 0 + return FALSE if(BP_IS_PROSTHETIC(affecting)) to_chat(user, SPAN_WARNING("That limb is prosthetic.")) - return 0 + return FALSE . = CAN_INJECT for(var/slot in list(slot_head_str, slot_wear_mask_str, slot_wear_suit_str, slot_w_uniform_str, slot_gloves_str, slot_shoes_str)) @@ -701,8 +701,8 @@ if(istype(C, /obj/item/clothing/suit/space)) . = INJECTION_PORT //it was going to block us, but it's a space suit so it doesn't because it has some kind of port else - to_chat(user, "There is no exposed flesh or thin material on [src]'s [affecting.name] to inject into.") - return 0 + to_chat(user, SPAN_WARNING("There is no exposed flesh or thin material on [src]'s [affecting.name] to inject into.")) + return FALSE /mob/living/human/print_flavor_text(var/shrink = 1) diff --git a/code/modules/mob/living/inventory.dm b/code/modules/mob/living/inventory.dm index c4cc217a30e..675b6eb423b 100644 --- a/code/modules/mob/living/inventory.dm +++ b/code/modules/mob/living/inventory.dm @@ -67,13 +67,17 @@ /mob/living/select_held_item_slot(var/slot) . = ..() var/last_slot = get_active_held_item_slot() - if(slot != last_slot && (slot in get_held_item_slots())) - _held_item_slot_selected = slot - if(istype(hud_used)) - hud_used.update_hand_elements() - var/obj/item/I = get_active_held_item() - if(istype(I)) - I.on_active_hand() + if(slot == last_slot) + return + on_mouse_up() + if(!(slot in get_held_item_slots())) + return + _held_item_slot_selected = slot + if(istype(hud_used)) + hud_used.update_hand_elements() + var/obj/item/I = get_active_held_item() + if(istype(I)) + I.on_active_hand() // Defer proc for the sake of delimbing root limbs with multiple graspers (serpentid) /mob/living/proc/queue_hand_rebuild() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 55e94083cb9..14727789f10 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -271,7 +271,7 @@ default behaviour is: gear_tree |= storage_contents /mob/living/proc/can_inject(var/mob/user, var/target_zone) - return 1 + return TRUE /mob/living/proc/get_organ_target() var/mob/shooter = src @@ -2020,3 +2020,6 @@ default behaviour is: //Pixel projectiles need a client, so we need a way to pass who the last user was for view calcs /mob/living/proc/get_effective_gunner() return src + +/mob/living/proc/is_playing_dead() + return stat || current_posture?.prone || (status_flags & FAKEDEATH) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 612a4e888b4..d673b4f6243 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1463,10 +1463,15 @@ var/global/const/ACTION_DANGER_ALL = 2 var/decl/butchery_data/butchery_decl = GET_DECL(butchery_data) . = butchery_decl?.meat_name || name -/mob/reset_movement_delay() +// we change the base type of our delay handler... +/mob/get_next_move_time() var/datum/movement_handler/mob/delay/delay = locate() in movement_handlers - if(istype(delay)) - delay.next_move = world.time + return delay?.next_move + +/mob/set_next_move_time(new_time) + var/datum/movement_handler/mob/delay/delay = locate() in movement_handlers + if(delay) + delay.next_move = new_time /mob/proc/do_attack_windup_checking(atom/target) return TRUE @@ -1613,3 +1618,7 @@ var/global/const/ACTION_DANGER_ALL = 2 /mob/proc/get_background_datum(cat_type) return global.using_map.default_background_info[cat_type] + +// Check if this mob can full-auto fire a gun at a target. +/mob/proc/mob_can_autofire(obj/item/gun/gun, atom/target) + return TRUE // TODO: dexterity check? That will be handled by the item itself probably. diff --git a/code/modules/mob/mob_automove.dm b/code/modules/mob/mob_automove.dm index f552d4f7c1e..543bf5dfd30 100644 --- a/code/modules/mob/mob_automove.dm +++ b/code/modules/mob/mob_automove.dm @@ -25,7 +25,6 @@ /mob/failed_automove() ..() stop_automove() - _automove_target = null return FALSE /mob/start_automove(target, movement_type, datum/automove_metadata/metadata) @@ -63,4 +62,4 @@ // We do some early checking here to avoid doing the same checks repeatedly by calling SelfMove(). /mob/can_do_automated_move(variant_move_delay) - . = MayMove() && !incapacitated() && (!istype(ai) || ai.can_do_automated_move()) + . = ..() && !incapacitated() && (!istype(ai) || ai.can_do_automated_move()) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 0b7f6c091e9..56fdd28e45b 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -228,7 +228,10 @@ INITIALIZE_IMMEDIATE(/mob/new_player) if(!character) return 0 - character = SSjobs.equip_job_title(character, job.title, 1) //equips the human + character = SSjobs.equip_job_title(character, job.title, 1) //equips the human + if(!character) + return 0 + SScustomitems.equip_custom_items(character) if(job.do_spawn_special(character, src, TRUE)) //This replaces the AI spawn logic with a proc stub. Refer to silicon.dm for the spawn logic. diff --git a/code/modules/mob/skills/skillset.dm b/code/modules/mob/skills/skillset.dm index eef15136063..9da79495a21 100644 --- a/code/modules/mob/skills/skillset.dm +++ b/code/modules/mob/skills/skillset.dm @@ -47,7 +47,8 @@ var/global/list/all_skill_verbs QDEL_NULL(NM) //Clean all nano_modules for simplicity. QDEL_NULL(mob.skillset.NM) QDEL_NULL_LIST(nm_viewing) - QDEL_NULL_LIST(mob.skillset.nm_viewing) + if(mob.skillset) + QDEL_NULL_LIST(mob.skillset.nm_viewing) on_levels_change() //Called when a player is added as an antag and the antag datum processes the skillset. diff --git a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm index 224d0cdccc5..ca6513aba20 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm @@ -44,7 +44,7 @@ // BREAKER DATA (simplified view) var/list/breakerlist[0] - for(var/obj/machinery/power/breakerbox/BR in known_breakers) + for(var/obj/machinery/breakerbox/BR in known_breakers) breakerlist.Add(list(list( "RCON_tag" = BR.RCon_tag, "enabled" = BR.on @@ -90,8 +90,8 @@ SMES.set_output(outputset) if(href_list["toggle_breaker"]) - var/obj/machinery/power/breakerbox/toggle = null - for(var/obj/machinery/power/breakerbox/breaker in known_breakers) + var/obj/machinery/breakerbox/toggle = null + for(var/obj/machinery/breakerbox/breaker in known_breakers) if(breaker.RCon_tag == href_list["toggle_breaker"]) toggle = breaker if(toggle) @@ -129,7 +129,7 @@ known_SMESs = sortTim(known_SMESs, /proc/cmp_rcon_tag_asc) known_breakers = new /list() - for(var/obj/machinery/power/breakerbox/breaker in SSmachines.machinery) + for(var/obj/machinery/breakerbox/breaker in SSmachines.machinery) if(can_connect_to(breaker)) known_breakers.Add(breaker) @@ -145,6 +145,6 @@ var/obj/machinery/power/smes/buildable/SMES = M return SMES.RCon_tag && SMES.RCon_tag != "NO_TAG" && SMES.RCon - if(istype(M, /obj/machinery/power/breakerbox)) - var/obj/machinery/power/breakerbox/breaker = M + if(istype(M, /obj/machinery/breakerbox)) + var/obj/machinery/breakerbox/breaker = M return breaker.RCon_tag != "NO_TAG" \ No newline at end of file diff --git a/code/modules/multiz/map_data.dm b/code/modules/multiz/map_data.dm index 83d4f972daa..da960afb031 100644 --- a/code/modules/multiz/map_data.dm +++ b/code/modules/multiz/map_data.dm @@ -18,8 +18,8 @@ INITIALIZE_IMMEDIATE(/obj/abstract/map_data) z_levels.len = i z_levels[i] = src - if (length(SSzcopy.zlev_maximums)) - SSzcopy.calculate_zstack_limits() + SSzcopy.calculate_zstack_limits() + return ..() /obj/abstract/map_data/Destroy(forced) diff --git a/code/modules/overmap/ships/landable.dm b/code/modules/overmap/ships/landable.dm index 93adc809767..452b8f5b89d 100644 --- a/code/modules/overmap/ships/landable.dm +++ b/code/modules/overmap/ships/landable.dm @@ -63,9 +63,10 @@ // We autobuild our z levels. /obj/effect/overmap/visitable/ship/landable/find_z_levels() if(!use_mapped_z_levels) + var/initial_z = world.maxz + SSmapping.bulk_increment_world_z_size(multiz + 1, level_type) for(var/i = 0 to multiz) - SSmapping.increment_world_z_size(level_type) - map_z += world.maxz + map_z += initial_z + i + 1 else ..() diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index 2cd7d48af02..e7eb827fd59 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -3,7 +3,7 @@ // Requires 5 seconds to toggle and can be toggled once a minute // Used for advanced grid control (read: Substations) -/obj/machinery/power/breakerbox +/obj/machinery/breakerbox name = "breaker box" icon = 'icons/obj/power.dmi' icon_state = "bbox_off" @@ -13,7 +13,7 @@ construct_state = /decl/machine_construction/default/panel_closed stat_immune = 0 uncreated_component_parts = null - base_type = /obj/machinery/power/breakerbox + base_type = /obj/machinery/breakerbox var/icon_state_on = "bbox_on" var/icon_state_off = "bbox_off" @@ -23,27 +23,27 @@ /// If world.time < lock_time, system is locked for interactions. var/lock_time = 0 -/obj/machinery/power/breakerbox/activated +/obj/machinery/breakerbox/activated icon_state = parent_type::icon_state_on // Enabled on server startup. Used in substations to keep them in bypass mode. -/obj/machinery/power/breakerbox/activated/Initialize() +/obj/machinery/breakerbox/activated/Initialize() ..() return INITIALIZE_HINT_LATELOAD -/obj/machinery/power/breakerbox/activated/LateInitialize() +/obj/machinery/breakerbox/activated/LateInitialize() set_state(TRUE) . = ..() -/obj/machinery/power/breakerbox/get_examine_strings(mob/user, distance, infix, suffix) +/obj/machinery/breakerbox/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(on) . += SPAN_GOOD("It seems to be online.") else . += SPAN_WARNING("It seems to be offline.") -/obj/machinery/power/breakerbox/proc/try_toggle_state(mob/living/user, digital = FALSE) - if(lock_time < world.time) +/obj/machinery/breakerbox/proc/try_toggle_state(mob/living/user, digital = FALSE) + if(world.time < lock_time) // maybe rename this unlock_time to make it clearer it's the time it unlocks at to_chat(user, SPAN_WARNING("System locked. Please try again later.")) return TRUE @@ -68,13 +68,13 @@ busy = FALSE return TRUE -/obj/machinery/power/breakerbox/attack_ai(mob/living/silicon/ai/user) +/obj/machinery/breakerbox/attack_ai(mob/living/silicon/ai/user) return try_toggle_state(user, digital = TRUE) -/obj/machinery/power/breakerbox/physical_attack_hand(mob/user) +/obj/machinery/breakerbox/physical_attack_hand(mob/user) return try_toggle_state(user, digital = FALSE) -/obj/machinery/power/breakerbox/attackby(obj/item/used_item, mob/user) +/obj/machinery/breakerbox/attackby(obj/item/used_item, mob/user) if(IS_MULTITOOL(used_item)) var/newtag = input(user, "Enter new RCON tag. Use \"NO_TAG\" to disable RCON or leave empty to cancel.", "SMES RCON system") as text if(!CanPhysicallyInteract(user)) @@ -85,11 +85,11 @@ return TRUE return ..() -/obj/machinery/power/breakerbox/on_update_icon() +/obj/machinery/breakerbox/on_update_icon() . = ..() icon_state = on ? icon_state_on : icon_state_off -/obj/machinery/power/breakerbox/proc/set_state(state) +/obj/machinery/breakerbox/proc/set_state(state) on = state update_icon() if(on) @@ -121,7 +121,7 @@ qdel(C) // Used by RCON to toggle the breaker box. -/obj/machinery/power/breakerbox/proc/auto_toggle() +/obj/machinery/breakerbox/proc/auto_toggle() if(lock_time > world.time) return FALSE // still on cooldown set_state(!on) diff --git a/code/modules/power/cable/cable.dm b/code/modules/power/cable/cable.dm index 00cc9a0e2d8..1b39dda6262 100644 --- a/code/modules/power/cable/cable.dm +++ b/code/modules/power/cable/cable.dm @@ -44,7 +44,7 @@ var/global/list/obj/structure/cable/all_cables = list() var/d1 var/d2 var/datum/powernet/powernet - var/obj/machinery/power/breakerbox/breaker_box + var/obj/machinery/breakerbox/breaker_box /obj/structure/cable/drain_power(var/drain_check, var/surge, var/amount = 0) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/_gun.dm similarity index 92% rename from code/modules/projectiles/gun.dm rename to code/modules/projectiles/_gun.dm index 3e61a8a5001..205e1136bcd 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/_gun.dm @@ -23,9 +23,10 @@ else settings[propname] = propvalue -/datum/firemode/proc/apply_to(obj/item/gun/gun) +/datum/firemode/proc/apply_firemode_to(obj/item/gun/gun) for(var/propname in settings) - gun.vars[propname] = settings[propname] + if(propname in gun.vars) + gun.vars[propname] = settings[propname] //Parent gun type. Guns are weapons that can be aimed at mobs and act over a distance /obj/item/gun @@ -83,11 +84,6 @@ var/has_safety = TRUE var/safety_icon //overlay to apply to gun based on safety state, if any - var/autofire_enabled = FALSE - var/atom/autofiring_at - var/mob/autofiring_by - var/autofiring_timer - // Spam prevention var/last_fire_message_type var/last_fire_message_time @@ -104,58 +100,12 @@ if(scope_zoom) verbs += /obj/item/gun/proc/scope -/obj/item/gun/Destroy() - // autofire timer is automatically cleaned up - autofiring_at = null - autofiring_by = null - . = ..() - /obj/item/gun/is_held_twohanded(mob/living/wielder) return one_hand_penalty > 0 && ..() /obj/item/gun/preserve_in_cryopod(var/obj/machinery/cryopod/pod) return TRUE -/obj/item/gun/proc/set_autofire(var/atom/fire_at, var/mob/fire_by, var/autoturn = TRUE) - . = TRUE - if(!istype(fire_at) || !istype(fire_by)) - . = FALSE - else if(QDELETED(fire_at) || QDELETED(fire_by) || QDELETED(src)) - . = FALSE - else if(!autofire_enabled) - . = FALSE - if(.) - autofiring_at = fire_at - autofiring_by = fire_by - if(!autofiring_timer) - autofiring_timer = addtimer(CALLBACK(src, PROC_REF(handle_autofire), autoturn), burst_delay, (TIMER_STOPPABLE | TIMER_LOOP | TIMER_UNIQUE | TIMER_OVERRIDE)) - else - clear_autofire() - -/obj/item/gun/proc/clear_autofire() - autofiring_at = null - autofiring_by = null - if(autofiring_timer) - deltimer(autofiring_timer) - autofiring_timer = null - -/obj/item/gun/proc/handle_autofire(autoturn) - set waitfor = FALSE - . = TRUE - if(QDELETED(autofiring_at) || QDELETED(autofiring_by)) - . = FALSE - else if(!autofiring_by.can_autofire(src, autofiring_at)) - . = FALSE - if(!.) - clear_autofire() - else if(can_autofire()) - try_autofire(autoturn) - -/obj/item/gun/proc/try_autofire(autoturn) - if(autoturn) - autofiring_by.set_dir(get_dir(src, autofiring_at)) - Fire(autofiring_at, autofiring_by, null, (get_dist(autofiring_at, autofiring_by) <= 1), FALSE, FALSE) - /obj/item/gun/update_twohanding() if(one_hand_penalty) update_icon() // In case item_state is set somewhere else. @@ -269,7 +219,6 @@ check_accidents(user) update_icon() . = ..() - clear_autofire() /obj/item/gun/proc/Fire(atom/target, atom/movable/firer, clickparams, pointblank = FALSE, reflex = FALSE, set_click_cooldown = TRUE, target_zone = BP_CHEST) if(!firer || !target) @@ -660,7 +609,7 @@ sel_mode = next_mode var/datum/firemode/new_mode = firemodes[sel_mode] - new_mode.apply_to(src) + new_mode.apply_firemode_to(src) playsound(loc, selector_sound, 50, 1) return new_mode @@ -716,9 +665,6 @@ return TRUE return FALSE -/obj/item/gun/proc/can_autofire() - return (autofire_enabled && world.time >= next_fire_time) - /obj/item/gun/proc/check_accidents(mob/living/user, message = "[user] fumbles with \the [src] and it goes off!",skill_path = SKILL_WEAPONS, fail_chance = 20, no_more_fail = SKILL_EXPERT, factor = 2) if(istype(user) && !safety() && user.skill_fail_prob(skill_path, fail_chance, no_more_fail, factor) && special_check(user)) user.visible_message(SPAN_WARNING(message)) @@ -744,13 +690,6 @@ if(M.aiming) M.aiming.toggle_active(FALSE, TRUE) -/mob/proc/can_autofire(var/obj/item/gun/autofiring, var/atom/autofiring_at) - if(!client || !(autofiring_at in view(client.view,src))) - return FALSE - if(get_active_held_item() != autofiring || incapacitated()) - return FALSE - return TRUE - /obj/item/gun/get_quick_interaction_handler(mob/user) return GET_DECL(/decl/interaction_handler/gun/toggle_safety) diff --git a/code/modules/projectiles/autofire.dm b/code/modules/projectiles/autofire.dm new file mode 100644 index 00000000000..5577984a6d7 --- /dev/null +++ b/code/modules/projectiles/autofire.dm @@ -0,0 +1,37 @@ +/obj/item/gun + var/autofire_enabled = FALSE + var/autofire_delay = 0.1 SECOND + var/next_autofire + +/obj/item/gun/proc/gun_can_autofire() + return (autofire_enabled && world.time >= next_fire_time) + +/obj/item/gun/proc/autofire_check(mob/user, atom/target) + if(!gun_can_autofire()) + return FALSE + if(!istype(user)) + return FALSE + if(!user.check_intent(I_FLAG_HARM)) + return FALSE + if(user.incapacitated()) + return FALSE + if(!user.mob_can_autofire(src, target)) + return FALSE + if(!istype(target) || (!isturf(target) && !isturf(target.loc))) + return FALSE + return TRUE + +/obj/item/gun/wielder_mouse_drag_down(mob/user, object, location, control, params) + if(autofire_check(user, object)) + return TRUE + return FALSE + +/obj/item/gun/wielder_mouse_drag_held(mob/user, atom/target) + next_fire_time = world.time // Reset so we aren't held to a timer. + if(!autofire_check(user, target)) + return FALSE + if(world.time < next_autofire) + return TRUE + next_autofire = world.time + autofire_delay + Fire(target, user, null, (get_dist(target, user) <= 1), FALSE, FALSE) + return TRUE diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index d69f9bdef70..1c2b99a4587 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -144,6 +144,8 @@ use_external_power = 1 max_shots = 4 has_safety = FALSE + autofire_enabled = TRUE + autofire_delay = 0.5 SECONDS /obj/item/gun/energy/plasmacutter/proc/slice(var/mob/M = null) var/obj/item/cell/power_supply = get_cell() diff --git a/code/modules/projectiles/guns/launcher/bows/_bow.dm b/code/modules/projectiles/guns/launcher/bows/_bow.dm index d4182d48fdd..5fee0efa549 100644 --- a/code/modules/projectiles/guns/launcher/bows/_bow.dm +++ b/code/modules/projectiles/guns/launcher/bows/_bow.dm @@ -46,51 +46,6 @@ /// How big is this bow when unstrung? Uses initial w_class if unset. var/unstrung_w_class -/obj/item/gun/launcher/bow/set_autofire(var/atom/fire_at, var/mob/fire_by, var/autoturn = TRUE) - if(!autofire_enabled || autofiring_at) - return ..() - . = ..() - if(ismob(fire_by)) - if(!get_loaded_arrow(fire_by) && fire_by.skill_check(SKILL_WEAPONS, SKILL_ADEPT)) - load_available_ammo(fire_by) - if(check_can_draw(fire_by)) - tension = 0 - next_tension_step = world.time + get_draw_time(fire_by) - fire_by.set_dir(get_dir(fire_by, fire_at)) - show_draw_message(fire_by) - update_icon() - -/obj/item/gun/launcher/bow/try_autofire(autoturn) - if(!autofire_enabled) - return ..() - var/mob/wielder = loc - if(!ismob(wielder) || !check_can_draw(wielder)) - clear_autofire() - else - wielder.set_dir(get_dir(wielder, autofiring_at)) - if(world.time >= next_tension_step && tension < max_tension) - next_tension_step = world.time + get_draw_time(wielder) - tension++ - if(tension == max_tension) - show_max_draw_message(wielder) - else - show_working_draw_message(wielder) - update_icon() - -/obj/item/gun/launcher/bow/clear_autofire() - if(!autofire_enabled) - return ..() - var/mob/living/wielder = loc - if(tension && istype(wielder) && !wielder.incapacitated() && wielder.get_active_held_item() == src && get_loaded_arrow()) - wielder.set_dir(get_dir(wielder, autofiring_at)) - Fire(autofiring_at, autofiring_by, null, (get_dist(autofiring_at, autofiring_by) <= 1), FALSE, FALSE) - . = ..() - if(tension) - if(istype(wielder)) - show_cancel_draw_message(wielder) - tension = 0 - update_icon() - /obj/item/gun/launcher/bow/handle_click_empty(atom/movable/firer) if(check_fire_message_spam("click")) to_chat(firer, SPAN_WARNING("\The [src] has nothing loaded.")) diff --git a/code/modules/projectiles/guns/launcher/bows/bow_drawing.dm b/code/modules/projectiles/guns/launcher/bows/bow_drawing.dm index ccef4f510c1..b3a15416bd0 100644 --- a/code/modules/projectiles/guns/launcher/bows/bow_drawing.dm +++ b/code/modules/projectiles/guns/launcher/bows/bow_drawing.dm @@ -63,3 +63,60 @@ show_working_draw_message(user) continue_drawing(user) update_icon() + +/obj/item/gun/launcher/bow/wielder_mouse_drag_down(mob/user, object, location, control, params) + if(drawing_bow) + return FALSE + . = ..() + +// Nock an arrow, or continue to draw the string back. +// We do this here so we don't instantly nock an arrow even if this is not a proper drag yet. +// DO NOT CALL PARENT, default full auto behavior is to fire while held. +/obj/item/gun/launcher/bow/wielder_mouse_drag_held(mob/user, atom/target) + + if(!autofire_enabled) + return FALSE + + // High skills mean you automatically nock an arrow before you draw. + if(tension <= 0 && !get_loaded_arrow(user) && user.skill_check(SKILL_WEAPONS, SKILL_ADEPT)) + load_available_ammo(user) + + if(!check_can_draw(user)) + return FALSE + + // Start drawing. + if(!drawing_bow) + drawing_bow = TRUE + tension = 0 + next_tension_step = world.time + get_draw_time(user) + if(user && isatom(target)) + user.set_dir(get_dir(user, target)) + show_draw_message(user) + update_icon() + return TRUE + + // Already drawing - keep drawing. + if(world.time >= next_tension_step && tension < max_tension) + next_tension_step = world.time + get_draw_time(user) + tension++ + if(tension == max_tension) + show_max_draw_message(user) + else + show_working_draw_message(user) + update_icon() + return TRUE + +// Fire! +/obj/item/gun/launcher/bow/wielder_mouse_drag_up(mob/user, atom/target) + if(!autofire_enabled || !istype(target)) + return FALSE + if(tension && istype(user) && !user.incapacitated() && user.get_active_held_item() == src && get_loaded_arrow()) + user.set_dir(get_dir(user, target)) + Fire(target, user, null, (get_dist(target, user) <= 1), FALSE, FALSE) + if(tension) + if(istype(user)) + show_cancel_draw_message(user) + tension = 0 + update_icon() + drawing_bow = FALSE + return TRUE diff --git a/code/modules/projectiles/guns/launcher/bows/bow_interaction.dm b/code/modules/projectiles/guns/launcher/bows/bow_interaction.dm index 14eaf835981..ac2434a56e7 100644 --- a/code/modules/projectiles/guns/launcher/bows/bow_interaction.dm +++ b/code/modules/projectiles/guns/launcher/bows/bow_interaction.dm @@ -84,8 +84,9 @@ /obj/item/gun/launcher/bow/proc/relax_tension(mob/user) tension = 0 update_icon() - if(autofire_enabled) - clear_autofire() + // Cancel any drag fire. + if(autofire_enabled && user.get_active_held_item() == src) + user.on_mouse_up() else if(user) show_string_relax_message(user) diff --git a/code/modules/projectiles/guns/launcher/bows/bow_string.dm b/code/modules/projectiles/guns/launcher/bows/bow_string.dm index 72212cde43b..173b3567055 100644 --- a/code/modules/projectiles/guns/launcher/bows/bow_string.dm +++ b/code/modules/projectiles/guns/launcher/bows/bow_string.dm @@ -1,6 +1,6 @@ /obj/item/bowstring name = "bowstring" - icon = 'icons/obj/fishing_line.dmi' // works well enough for the time being + icon = 'icons/obj/bowstring.dmi' icon_state = ICON_STATE_WORLD desc = "A flexible length of material used to string bows." material = /decl/material/solid/organic/meat/gut diff --git a/code/modules/projectiles/guns/launcher/foam_gun.dm b/code/modules/projectiles/guns/launcher/foam_gun.dm index 6a351feeda8..7350c7128ae 100644 --- a/code/modules/projectiles/guns/launcher/foam_gun.dm +++ b/code/modules/projectiles/guns/launcher/foam_gun.dm @@ -86,7 +86,7 @@ icon = 'icons/obj/guns/foam/machine_gun.dmi' w_class = ITEM_SIZE_NORMAL fire_delay = 0 - autofire_enabled = 1 + autofire_enabled = TRUE one_hand_penalty = 3 max_darts = 30 burst_delay = 1 diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index 1614d25961e..9646296d5ff 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -149,9 +149,9 @@ burst = 3 burst_accuracy = list(0,-1,-1) dispersion = list(0.0, 0.6, 1.0) + autofire_enabled = TRUE fire_delay = 0 - autofire_enabled = 1 mag_insert_sound = 'sound/weapons/guns/interaction/batrifle_magin.ogg' mag_remove_sound = 'sound/weapons/guns/interaction/batrifle_magout.ogg' @@ -177,8 +177,8 @@ return FALSE return TRUE -/obj/item/gun/projectile/automatic/machine/set_autofire(atom/fire_at, mob/fire_by, autoturn) - if(!special_check(fire_by)) +/obj/item/gun/projectile/automatic/machine/wielder_mouse_drag_down(mob/user, object, location, control, params) + if(!special_check(user)) return FALSE . = ..() if(. && !spin_up_time) @@ -186,7 +186,7 @@ sound_token = play_looping_sound(src, "machine_gun", 'sound/mecha/hydraulic.ogg', volume = 30) spin_up_time = world.time -/obj/item/gun/projectile/automatic/machine/clear_autofire() +/obj/item/gun/projectile/automatic/machine/wielder_mouse_drag_up(mob/user, atom/target) . = ..() spin_up_time = null - QDEL_NULL(sound_token) \ No newline at end of file + QDEL_NULL(sound_token) diff --git a/code/modules/reagents/chems/chems_nutriment.dm b/code/modules/reagents/chems/chems_nutriment.dm index 929c4307d09..e8f8acf5bbd 100644 --- a/code/modules/reagents/chems/chems_nutriment.dm +++ b/code/modules/reagents/chems/chems_nutriment.dm @@ -9,7 +9,6 @@ fruit_descriptor = "nutritious" uid = "chem_nutriment" exoplanet_rarity_gas = MAT_RARITY_NOWHERE // Please, no more animal protein or glowsap or corn oil atmosphere. - fishing_bait_value = 0.65 compost_value = 1 nutriment_factor = 10 affect_blood_on_ingest = 0 diff --git a/code/modules/reagents/chems/chems_oil.dm b/code/modules/reagents/chems/chems_oil.dm index e540efb9844..4d38583ebe6 100644 --- a/code/modules/reagents/chems/chems_oil.dm +++ b/code/modules/reagents/chems/chems_oil.dm @@ -10,7 +10,6 @@ uid = "chem_oil_lamp" color = "#664330" value = 1.5 - fishing_bait_value = 0 taste_mult = 4 metabolism = REM * 4 exoplanet_rarity_gas = MAT_RARITY_NOWHERE diff --git a/code/modules/reagents/reagent_containers/food/meat/cubes.dm b/code/modules/reagents/reagent_containers/food/meat/cubes.dm index 13f68070877..3dbbd1ab517 100644 --- a/code/modules/reagents/reagent_containers/food/meat/cubes.dm +++ b/code/modules/reagents/reagent_containers/food/meat/cubes.dm @@ -36,6 +36,8 @@ add_to_reagents(/decl/material/solid/organic/meat, 10) /obj/item/food/animal_cube/get_single_monetary_worth() + if(worthless) + return 0 . = (spawn_type ? round(atom_info_repository.get_combined_worth_for((islist(spawn_type) ? spawn_type[1] : spawn_type)) * 1.25) : 5) if(wrapper_type) . += atom_info_repository.get_combined_worth_for(wrapper_type) diff --git a/code/modules/research/design_database_analyzer.dm b/code/modules/research/design_database_analyzer.dm index 6d57b82a063..0c1d18a2ad6 100644 --- a/code/modules/research/design_database_analyzer.dm +++ b/code/modules/research/design_database_analyzer.dm @@ -80,6 +80,10 @@ D.ui_interact(user) return TRUE +/// Returns TRUE if used_item can be deconstructed, assuming it meets other criteria (tech level, etc.) +/obj/machinery/destructive_analyzer/proc/can_deconstruct(var/obj/item/used_item) + return TRUE + /obj/machinery/destructive_analyzer/attackby(var/obj/item/used_item, var/mob/user) if(IS_MULTITOOL(used_item) && !user.check_intent(I_FLAG_HARM)) @@ -106,7 +110,7 @@ return TRUE var/list/techlvls = cached_json_decode(tech) - if(!length(techlvls) || used_item.holographic) + if(!length(techlvls) || !can_deconstruct(used_item)) to_chat(user, SPAN_WARNING("You cannot deconstruct this item.")) return TRUE diff --git a/code/modules/power/singularity/collector.dm b/code/modules/singularity/collector.dm similarity index 100% rename from code/modules/power/singularity/collector.dm rename to code/modules/singularity/collector.dm diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/singularity/containment_field.dm similarity index 100% rename from code/modules/power/singularity/containment_field.dm rename to code/modules/singularity/containment_field.dm diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/singularity/field_generator.dm similarity index 100% rename from code/modules/power/singularity/field_generator.dm rename to code/modules/singularity/field_generator.dm diff --git a/code/modules/power/singularity/generator.dm b/code/modules/singularity/generator.dm similarity index 100% rename from code/modules/power/singularity/generator.dm rename to code/modules/singularity/generator.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/singularity/particle_accelerator/particle.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle.dm rename to code/modules/singularity/particle_accelerator/particle.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/singularity/particle_accelerator/particle_accelerator.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle_accelerator.dm rename to code/modules/singularity/particle_accelerator/particle_accelerator.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle_chamber.dm b/code/modules/singularity/particle_accelerator/particle_chamber.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle_chamber.dm rename to code/modules/singularity/particle_accelerator/particle_chamber.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/singularity/particle_accelerator/particle_control.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle_control.dm rename to code/modules/singularity/particle_accelerator/particle_control.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle_emitter.dm b/code/modules/singularity/particle_accelerator/particle_emitter.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle_emitter.dm rename to code/modules/singularity/particle_accelerator/particle_emitter.dm diff --git a/code/modules/power/singularity/particle_accelerator/particle_power.dm b/code/modules/singularity/particle_accelerator/particle_power.dm similarity index 100% rename from code/modules/power/singularity/particle_accelerator/particle_power.dm rename to code/modules/singularity/particle_accelerator/particle_power.dm diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/singularity/singularity.dm similarity index 100% rename from code/modules/power/singularity/singularity.dm rename to code/modules/singularity/singularity.dm diff --git a/code/modules/power/singularity/singularity_events.dm b/code/modules/singularity/singularity_events.dm similarity index 100% rename from code/modules/power/singularity/singularity_events.dm rename to code/modules/singularity/singularity_events.dm diff --git a/code/modules/power/singularity/singularity_stages.dm b/code/modules/singularity/singularity_stages.dm similarity index 100% rename from code/modules/power/singularity/singularity_stages.dm rename to code/modules/singularity/singularity_stages.dm diff --git a/code/modules/turbolift/turbolift_turfs.dm b/code/modules/turbolift/turbolift_turfs.dm deleted file mode 100644 index 045790c2529..00000000000 --- a/code/modules/turbolift/turbolift_turfs.dm +++ /dev/null @@ -1,2 +0,0 @@ -/turf/wall/elevator/Initialize(var/ml) - . = ..(ml, /decl/material/solid/metal/alienalloy/elevatorium) diff --git a/code/unit_tests/area_tests.dm b/code/unit_tests/area_tests.dm index fafbf4d00d6..fabf408602f 100644 --- a/code/unit_tests/area_tests.dm +++ b/code/unit_tests/area_tests.dm @@ -82,7 +82,9 @@ /datum/unit_test/areas_shall_be_used/start_test() var/unused_areas = 0 - for(var/area_type in subtypesof(/area)) + for(var/area/area_type as anything in subtypesof(/area)) + if(TYPE_IS_ABSTRACT(area_type)) + continue if(area_type in global.using_map.area_usage_test_exempted_areas) continue if(is_path_in_list(area_type, global.using_map.area_usage_test_exempted_root_areas)) diff --git a/code/unit_tests/offset_tests.dm b/code/unit_tests/offset_tests.dm index b4605420adc..fb1537227ff 100644 --- a/code/unit_tests/offset_tests.dm +++ b/code/unit_tests/offset_tests.dm @@ -45,7 +45,6 @@ var/static/list/exception_types = list( /obj/machinery/light, /obj/machinery/camera, - /obj/structure/lift/button/standalone, /obj/structure/hygiene/sink ) diff --git a/code/unit_tests/~unit_test_types.dm b/code/unit_tests/~unit_test_types.dm index 71ba1939b5a..72fa0be9eb7 100644 --- a/code/unit_tests/~unit_test_types.dm +++ b/code/unit_tests/~unit_test_types.dm @@ -45,6 +45,9 @@ /obj/unit_test/transparent opacity = FALSE +/area/test_area + abstract_type = /area/test_area + /area/test_area/general icon_state = "blue" diff --git a/html/changelog.html b/html/changelog.html index 796f54f9fd8..2a482a38ae9 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,10 +52,10 @@ -->
-

27 March 2026

-

Typhin updated:

+

11 July 2026

+

tetra zeta updated:

diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 83328d67f5a..4d94674e39a 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -15070,3 +15070,15 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2026-03-27: Typhin: - tweak: Prevented Garlic Oil from dealing TOX damage +2026-05-22: + MistakeNot4892: + - tweak: Mech plasmacutter is full auto and mech AR full auto should now work. + - tweak: Autofire should be more responsive in general. +2026-05-30: + Penelope Haze: + - tweak: Deactivating message passing on the message server now prevents requests + console messages from being received. They will still be logged for admins and + on the in-game message server console. +2026-07-11: + 'tetra zeta ': + - imageadd: sand resprited and converted to greyscale diff --git a/icons/obj/fishing_line.dmi b/icons/obj/bowstring.dmi similarity index 100% rename from icons/obj/fishing_line.dmi rename to icons/obj/bowstring.dmi diff --git a/icons/obj/structures/fence.dmi b/icons/obj/structures/fence.dmi deleted file mode 100644 index b3d997a940f..00000000000 Binary files a/icons/obj/structures/fence.dmi and /dev/null differ diff --git a/icons/obj/structures/fences/brick.dmi b/icons/obj/structures/fences/brick.dmi new file mode 100644 index 00000000000..a9e2f9c17b7 Binary files /dev/null and b/icons/obj/structures/fences/brick.dmi differ diff --git a/icons/obj/structures/fences/chain.dmi b/icons/obj/structures/fences/chain.dmi new file mode 100644 index 00000000000..109a1aaec4b Binary files /dev/null and b/icons/obj/structures/fences/chain.dmi differ diff --git a/icons/obj/structures/fences/palisade.dmi b/icons/obj/structures/fences/palisade.dmi new file mode 100644 index 00000000000..ad5c81aca43 Binary files /dev/null and b/icons/obj/structures/fences/palisade.dmi differ diff --git a/icons/obj/structures/fences/plank.dmi b/icons/obj/structures/fences/plank.dmi new file mode 100644 index 00000000000..85df01472e3 Binary files /dev/null and b/icons/obj/structures/fences/plank.dmi differ diff --git a/icons/obj/structures/fences/stick.dmi b/icons/obj/structures/fences/stick.dmi new file mode 100644 index 00000000000..c10c06afd29 Binary files /dev/null and b/icons/obj/structures/fences/stick.dmi differ diff --git a/icons/turf/flooring/sand.dmi b/icons/turf/flooring/sand.dmi index 20339e3f1bc..ba1ad8a4c01 100644 Binary files a/icons/turf/flooring/sand.dmi and b/icons/turf/flooring/sand.dmi differ diff --git a/install-byond.sh b/install-byond.sh index 5d60db27712..ae7d1be6973 100755 --- a/install-byond.sh +++ b/install-byond.sh @@ -9,7 +9,7 @@ else cd "$HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}" echo "Installing DreamMaker to $PWD" #curl "http://www.byond.com/download/build/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -H "User-Agent: NebulaSS13/1.0 Continuous Integration" -o byond.zip - curl "https://spacestation13.github.io/byond-builds/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -H "User-Agent: NebulaSS13/1.0 Continuous Integration" -o byond.zip + curl "https://byond-builds.dm-lang.org/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -H "User-Agent: NebulaSS13/1.0 Continuous Integration" -o byond.zip unzip -o byond.zip cd byond make here diff --git a/maps/away/bearcat/bearcat-1.dmm b/maps/away/bearcat/bearcat-1.dmm index 534accc465e..312a07c8266 100644 --- a/maps/away/bearcat/bearcat-1.dmm +++ b/maps/away/bearcat/bearcat-1.dmm @@ -378,7 +378,7 @@ /area/ship/scrap/gambling) "aW" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light/small{ dir = 1; icon_state = "bulb1" @@ -497,7 +497,7 @@ /obj/effect/floor_decal/corner/beige{ dir = 5 }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light/small{ dir = 1; icon_state = "bulb1" @@ -527,7 +527,7 @@ /turf/floor/usedup, /area/ship/scrap/gambling) "bj" = ( -/obj/structure/holostool, +/obj/item/stool/padded, /obj/item/hand/missing_card, /turf/floor/usedup, /area/ship/scrap/gambling) @@ -553,7 +553,7 @@ /obj/structure/cable{ icon_state = "1-4" }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/item/hand/missing_card, /turf/floor/usedup, /area/ship/scrap/gambling) @@ -701,7 +701,7 @@ dir = 1; level = 2 }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/item/hand/missing_card, /turf/floor/usedup, /area/ship/scrap/gambling) @@ -801,7 +801,7 @@ dir = 1; level = 2 }, -/obj/structure/holostool, +/obj/item/stool/padded, /turf/floor/tiled/dark/airless, /area/ship/scrap/crew/dorms1) "bO" = ( @@ -1019,7 +1019,7 @@ /area/ship/scrap/crew/dorms2) "cj" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light/small{ dir = 1; icon_state = "bulb1" @@ -1473,7 +1473,7 @@ /obj/effect/floor_decal/corner/beige{ dir = 5 }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light/small{ dir = 1; icon_state = "bulb1" @@ -1687,7 +1687,7 @@ dir = 1; level = 2 }, -/obj/structure/holostool, +/obj/item/stool/padded, /turf/floor/tiled/dark/airless, /area/ship/scrap/crew/dorms3) "dG" = ( diff --git a/maps/away/bearcat/bearcat-2.dmm b/maps/away/bearcat/bearcat-2.dmm index 64fa7037ab3..d661d6b5134 100644 --- a/maps/away/bearcat/bearcat-2.dmm +++ b/maps/away/bearcat/bearcat-2.dmm @@ -4191,7 +4191,7 @@ /turf/floor/usedup, /area/ship/scrap/maintenance/power) "ib" = ( -/obj/machinery/power/breakerbox/activated, +/obj/machinery/breakerbox/activated, /obj/structure/cable{ icon_state = "1-2" }, diff --git a/maps/away/derelict/derelict-station.dmm b/maps/away/derelict/derelict-station.dmm index 46a25943bbf..327b778a292 100644 --- a/maps/away/derelict/derelict-station.dmm +++ b/maps/away/derelict/derelict-station.dmm @@ -998,7 +998,7 @@ /turf/floor/plating/airless, /area/constructionsite/hallway/fore) "dw" = ( -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/constructionsite/hallway/fore) "dx" = ( /obj/random/junk, @@ -1048,7 +1048,7 @@ /obj/machinery/door/airlock/glass/command{ name = "Bridge" }, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/constructionsite/hallway/fore) "dI" = ( /obj/machinery/door/airlock/glass{ @@ -2916,7 +2916,7 @@ /turf/floor/tiled/dark/airless, /area/constructionsite) "kt" = ( -/obj/machinery/power/breakerbox, +/obj/machinery/breakerbox, /turf/floor/plating/airless, /area/constructionsite) "ku" = ( @@ -3321,10 +3321,8 @@ /turf/floor/plating/airless, /area/AIsattele) "lZ" = ( -/obj/machinery/emitter{ - anchored = 1; - dir = 4; - state = 2 +/obj/machinery/emitter/anchored{ + dir = 4 }, /turf/floor/plating/airless, /area/constructionsite/engineering) @@ -3333,10 +3331,8 @@ /turf/floor/plating/airless, /area/constructionsite/engineering) "mb" = ( -/obj/machinery/emitter{ - anchored = 1; - dir = 8; - state = 2 +/obj/machinery/emitter/anchored{ + dir = 8 }, /turf/floor/plating/airless, /area/constructionsite/engineering) diff --git a/maps/away/derelict/derelict_areas.dm b/maps/away/derelict/derelict_areas.dm index 3ac3f4cceb7..a07f86784dd 100644 --- a/maps/away/derelict/derelict_areas.dm +++ b/maps/away/derelict/derelict_areas.dm @@ -1,3 +1,6 @@ +/area/derelict + abstract_type = /area/derelict + /area/derelict/ship name = "\improper Abandoned Ship" icon_state = "yellow" diff --git a/maps/away/errant_pisces/errant_pisces_areas.dm b/maps/away/errant_pisces/errant_pisces_areas.dm index dedee8fc025..dc6fef3b065 100644 --- a/maps/away/errant_pisces/errant_pisces_areas.dm +++ b/maps/away/errant_pisces/errant_pisces_areas.dm @@ -1,4 +1,5 @@ /area/errant_pisces + abstract_type = /area/errant_pisces icon = 'maps/away/errant_pisces/icons/areas.dmi' /area/errant_pisces/bow_port diff --git a/maps/away/liberia/liberia_areas.dm b/maps/away/liberia/liberia_areas.dm index de18310846c..4e14d0cb731 100644 --- a/maps/away/liberia/liberia_areas.dm +++ b/maps/away/liberia/liberia_areas.dm @@ -1,4 +1,5 @@ /area/liberia + abstract_type = /area/liberia req_access = list(access_merchant) /area/liberia/dockinghall diff --git a/maps/away/lost_supply_base/lost_supply_base_areas.dm b/maps/away/lost_supply_base/lost_supply_base_areas.dm index c33069e5e7a..2f58f748ba0 100644 --- a/maps/away/lost_supply_base/lost_supply_base_areas.dm +++ b/maps/away/lost_supply_base/lost_supply_base_areas.dm @@ -6,19 +6,15 @@ /area/lost_supply_base/solar name = "\improper Abandoned supply station solars control room" icon_state = "lost_supply_base_solar" - icon = 'maps/away/lost_supply_base/lost_supply_base_sprites.dmi' /area/lost_supply_base/office name = "\improper Abandoned supply station office" icon_state = "lost_supply_base_office" - icon = 'maps/away/lost_supply_base/lost_supply_base_sprites.dmi' /area/lost_supply_base/supply name = "\improper Abandoned supply station supplies room" icon_state = "lost_supply_base_supply" - icon = 'maps/away/lost_supply_base/lost_supply_base_sprites.dmi' /area/lost_supply_base/common name = "\improper Abandoned supply station common area" - icon_state = "lost_supply_base_common" - icon = 'maps/away/lost_supply_base/lost_supply_base_sprites.dmi' \ No newline at end of file + icon_state = "lost_supply_base_common" \ No newline at end of file diff --git a/maps/away/magshield/magshield_areas.dm b/maps/away/magshield/magshield_areas.dm index 85a429168f8..0ff5d9ace63 100644 --- a/maps/away/magshield/magshield_areas.dm +++ b/maps/away/magshield/magshield_areas.dm @@ -1,29 +1,27 @@ +/area/magshield + abstract_type = /area/magshield + icon = 'magshield_sprites.dmi' + /area/magshield/south name = "Orbital Station South Wing" icon_state = "south" - icon = 'magshield_sprites.dmi' /area/magshield/north name = "Orbital Station North Wing" icon_state = "north" - icon = 'magshield_sprites.dmi' /area/magshield/east name = "Orbital Station East Wing" icon_state = "east" - icon = 'magshield_sprites.dmi' /area/magshield/west name = "Orbital Station West Wing" icon_state = "west" - icon = 'magshield_sprites.dmi' /area/magshield/engine name = "Orbital Station Engine" icon_state = "engine" - icon = 'magshield_sprites.dmi' /area/magshield/smes_storage name = "Orbital Station SMES Battery Room" icon_state = "smes_storage" - icon = 'magshield_sprites.dmi' diff --git a/maps/away/mining/mining-signal.dmm b/maps/away/mining/mining-signal.dmm index 7badf05b323..95d27ad6286 100644 --- a/maps/away/mining/mining-signal.dmm +++ b/maps/away/mining/mining-signal.dmm @@ -12,28 +12,28 @@ /obj/structure/rack, /obj/random/tech_supply, /obj/random/bomb_supply, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "ah" = ( /obj/structure/rack, /obj/random/tech_supply, /obj/random/loot, /obj/random/loot, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "ai" = ( /obj/structure/table/steel_reinforced, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "aj" = ( /obj/machinery/porta_turret/stationary, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "ak" = ( /obj/structure/rack, /obj/item/cell/hyper, /obj/item/cell/hyper, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "am" = ( /obj/structure/rack, @@ -45,7 +45,7 @@ dir = 4 }, /obj/machinery/door/window/brigdoor/southleft, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "an" = ( /obj/machinery/mech_recharger, @@ -53,10 +53,7 @@ /obj/structure/window/reinforced{ dir = 8 }, -/turf/floor/holofloor/tiled/dark, -/area/outpost/abandoned) -"ao" = ( -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "ap" = ( /obj/structure/table, @@ -125,23 +122,23 @@ dir = 8; icon_state = "bulb1" }, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "aE" = ( /obj/random/trash, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "aF" = ( /obj/random/technology_scanner, /obj/machinery/porta_turret/stationary, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "aG" = ( /obj/machinery/light/small/emergency{ dir = 4; icon_state = "bulb1" }, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "aH" = ( /turf/floor/plating, @@ -257,7 +254,7 @@ /obj/machinery/light/small{ dir = 1 }, -/turf/floor/holofloor/tiled/dark, +/turf/floor/tiled/dark, /area/outpost/abandoned) "be" = ( /obj/effect/decal/cleanable/blood, @@ -345,13 +342,6 @@ }, /turf/floor/tiled/white, /area/outpost/abandoned) -"bu" = ( -/obj/effect/decal/cleanable/dirt/visible, -/obj/effect/floor_decal/corner/purple{ - dir = 5 - }, -/turf/floor/tiled/white, -/area/outpost/abandoned) "bv" = ( /obj/effect/decal/cleanable/dirt/visible, /obj/machinery/light/small{ @@ -429,13 +419,6 @@ /obj/effect/decal/cleanable/dirt/visible, /turf/floor, /area/outpost/abandoned) -"bE" = ( -/obj/effect/floor_decal/corner/purple{ - dir = 5 - }, -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/white, -/area/outpost/abandoned) "bF" = ( /obj/machinery/light/small{ dir = 1 @@ -469,10 +452,6 @@ /obj/effect/gibspawner/human, /turf/floor/tiled/white, /area/outpost/abandoned) -"bL" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/white, -/area/outpost/abandoned) "bM" = ( /obj/effect/decal/cleanable/blood, /turf/floor/tiled/white, @@ -626,10 +605,6 @@ }, /turf/floor/tiled/white, /area/outpost/abandoned) -"ck" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor, -/area/outpost/abandoned) "cl" = ( /obj/structure/hygiene/shower{ dir = 8 @@ -799,10 +774,6 @@ /obj/effect/decal/cleanable/dirt/visible, /turf/floor/tiled/airless, /area/outpost/abandoned) -"cW" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/airless, -/area/outpost/abandoned) "cX" = ( /obj/effect/decal/cleanable/blood, /obj/effect/floor_decal/corner/paleblue{ @@ -900,10 +871,6 @@ /obj/effect/decal/cleanable/dirt/visible, /turf/floor/carpet, /area/outpost/abandoned) -"dk" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/carpet/broken, -/area/outpost/abandoned) "dl" = ( /obj/effect/floor_decal/spline/fancy/wood{ dir = 6 @@ -1231,10 +1198,6 @@ /obj/effect/decal/cleanable/dirt/visible, /turf/floor/tiled/white/airless, /area/outpost/abandoned) -"es" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/white/airless, -/area/outpost/abandoned) "et" = ( /obj/abstract/landmark/mapped_fluid/fuel, /obj/structure/table, @@ -1477,10 +1440,6 @@ /obj/effect/floor_decal/industrial/warning/cee, /turf/floor/plating, /area/outpost/abandoned) -"fh" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/dark, -/area/outpost/abandoned) "fi" = ( /obj/item/pen, /obj/effect/decal/cleanable/dirt/visible, @@ -1860,10 +1819,6 @@ /obj/effect/decal/cleanable/dirt/visible, /turf/floor/barren, /area/mine/explored) -"gt" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/barren, -/area/mine/explored) "gu" = ( /obj/effect/decal/cleanable/dirt/visible, /turf/floor/plating, @@ -2420,10 +2375,6 @@ }, /turf/floor/tiled/white, /area/outpost/abandoned) -"HD" = ( -/obj/effect/decal/cleanable/dirt/visible, -/turf/floor/tiled/white/airless, -/area/outpost/abandoned) "IX" = ( /obj/machinery/door/firedoor, /obj/machinery/door/blast/regular/open, @@ -11822,7 +11773,7 @@ cu cU af dE -cW +cw dD dW fm @@ -12025,9 +11976,9 @@ cV cG dF dV -es -HD -HD +dW +dW +dW fF cG aa @@ -12223,7 +12174,7 @@ aa af cc cw -cW +cw ds dD dW @@ -13845,7 +13796,7 @@ dI ea dH eV -cW +cw fK af aa @@ -14044,10 +13995,10 @@ aa aa af dJ -cW +cw ez eW -cW +cw fL af aa @@ -14246,10 +14197,10 @@ cy af af dK -cW +cw dH eX -cW +cw fM af af @@ -14449,7 +14400,7 @@ cZ cG dL eb -cW +cw eY ft dH @@ -14852,7 +14803,7 @@ sb db af dN -cW +cw cw eZ fv @@ -15054,10 +15005,10 @@ tb bZ cy dH -cW +cw cw fa -cW +cw fO fY gf @@ -15247,7 +15198,7 @@ az aX az bm -bu +bt bK bQ bX @@ -15453,15 +15404,15 @@ bv bJ bQ bX -ck +by vb dd dt dP dH -cW +cw eZ -cW +cw dH af af @@ -15667,7 +15618,7 @@ cw fQ dH dH -cW +cw gy cw gK @@ -15867,8 +15818,8 @@ eC fc fx cw -cW -cW +cw +cw gm dH gE @@ -16068,7 +16019,7 @@ eg eD fd eD -cW +cw ga ga gn @@ -16076,8 +16027,8 @@ gn gF dH gP -cW -cW +cw +cw gL hl ht @@ -16279,7 +16230,7 @@ af af cy gS -cW +cw gL dH hu @@ -16453,9 +16404,9 @@ aa aa af ag -ao +dT aD -ao +dT ba bh af @@ -16655,9 +16606,9 @@ aa aa af ah -ao -ao -ao +dT +dT +dT bb bi af @@ -16857,13 +16808,13 @@ aa aa af ai -ao +dT aj af af af af -bu +bt bJ bQ af @@ -17059,11 +17010,11 @@ aa aa af aj -ao -ao +dT +dT af -ao -ao +dT +dT af bB bJ @@ -17261,11 +17212,11 @@ aa aa af ak -ao +dT aE aN -ao -ao +dT +dT bn bx bH @@ -17463,14 +17414,14 @@ aa aa af aj -ao -ao +dT +dT af bc -ao +dT af bC -bL +bJ bT bZ co @@ -17485,7 +17436,7 @@ ab ab gb ej -gt +ej fe ab gM @@ -17665,7 +17616,7 @@ aa aa af ai -ao +dT aF af af @@ -17867,8 +17818,8 @@ aa aa af am -ao -ao +dT +dT aO pb bj @@ -18069,7 +18020,7 @@ aa aa af an -ao +dT aG aP be @@ -18081,7 +18032,7 @@ bP bZ cr cL -dk +di dA dm ab @@ -19904,7 +19855,7 @@ cQ dS em eK -fh +eL dT fS af @@ -20106,8 +20057,8 @@ cQ cQ af eL -fh -fh +eL +eL eL af aa @@ -20703,7 +20654,7 @@ aH af bx bN -bL +bJ bU af af @@ -20903,7 +20854,7 @@ aH aH aI bq -bE +bC bJ bJ bP diff --git a/maps/away/mining/mining_areas.dm b/maps/away/mining/mining_areas.dm index 3dd6da72529..f786fa4c844 100644 --- a/maps/away/mining/mining_areas.dm +++ b/maps/away/mining/mining_areas.dm @@ -1,5 +1,6 @@ // GENERIC MINING AREAS /area/mine + abstract_type = /area/mine icon_state = "mining" ambience = list('sound/ambience/ambimine.ogg', 'sound/ambience/song_game.ogg') sound_env = ASTEROID @@ -15,9 +16,12 @@ icon_state = "unexplored" // OUTPOSTS +/area/outpost + abstract_type = /area/outpost + icon_state = "dark" + /area/outpost/abandoned name = "Abandoned Outpost" - icon_state = "dark" /area/djstation name = "\improper Listening Post" diff --git a/maps/away/smugglers/smugglers_areas.dm b/maps/away/smugglers/smugglers_areas.dm index 4736cb8a0f0..be6b6277b23 100644 --- a/maps/away/smugglers/smugglers_areas.dm +++ b/maps/away/smugglers/smugglers_areas.dm @@ -1,14 +1,15 @@ +/area/smugglers + abstract_type = /area/smugglers + icon = 'smugglers_sprites.dmi' + /area/smugglers/base name = "\improper Asteroid Base" icon_state = "smgl_base" - icon = 'smugglers_sprites.dmi' /area/smugglers/office name = "\improper Asteroid Base Office" icon_state = "smgl_office" - icon = 'smugglers_sprites.dmi' /area/smugglers/dorms name = "\improper Asteroid Base Rest Area" - icon_state = "smgl_dorms" - icon = 'smugglers_sprites.dmi' \ No newline at end of file + icon_state = "smgl_dorms" \ No newline at end of file diff --git a/maps/away/unishi/unishi_areas.dm b/maps/away/unishi/unishi_areas.dm index b09d7b9fb5e..f8e1c52698d 100644 --- a/maps/away/unishi/unishi_areas.dm +++ b/maps/away/unishi/unishi_areas.dm @@ -1,5 +1,6 @@ -/area/unishi/ - icon = 'unishi.dmi' +/area/unishi + abstract_type = /area/unishi + icon = 'unishi.dmi' /area/unishi/bridge name = "\improper Bridge" diff --git a/maps/away/yacht/yacht_areas.dm b/maps/away/yacht/yacht_areas.dm index 92795b8720b..f85268abb67 100644 --- a/maps/away/yacht/yacht_areas.dm +++ b/maps/away/yacht/yacht_areas.dm @@ -1,12 +1,15 @@ +/area/yacht + abstract_type = /area/yacht + icon = 'yacht_icons.dmi' + /area/yacht/bridge name = "\improper Yacht Bridge" icon_state = "bridge" - icon = 'yacht_icons.dmi' + /area/yacht/living name = "\improper Yacht Living" icon_state = "living" - icon = 'yacht_icons.dmi' + /area/yacht/engine name = "\improper Yacht Engine" - icon_state = "engine" - icon = 'yacht_icons.dmi' \ No newline at end of file + icon_state = "engine" \ No newline at end of file diff --git a/maps/example/example-1.dmm b/maps/example/example-1.dmm index 500a16664c7..77809d5fc12 100644 --- a/maps/example/example-1.dmm +++ b/maps/example/example-1.dmm @@ -141,12 +141,6 @@ "gO" = ( /turf/wall/titanium, /area/shuttle/ferry) -"gT" = ( -/obj/effect/floor_decal/industrial/warning{ - dir = 6 - }, -/turf/floor/tiled/steel_grid, -/area/example/first) "he" = ( /obj/effect/floor_decal/industrial/warning{ dir = 4 @@ -225,9 +219,7 @@ dir = 1 }, /obj/structure/ladder, -/obj/effect/floor_decal/industrial/warning{ - dir = 4 - }, +/obj/effect/floor_decal/industrial/warning/full, /turf/floor/tiled/dark/monotile, /area/example/first) "mo" = ( @@ -427,10 +419,6 @@ /obj/machinery/light, /turf/floor/tiled/steel_grid, /area/example/first) -"tA" = ( -/obj/abstract/turbolift_spawner/example, -/turf/floor, -/area/turbolift/example/first) "uD" = ( /obj/machinery/light, /turf/floor, @@ -652,15 +640,6 @@ /obj/machinery/light, /turf/floor/tiled/steel_grid, /area/example/first) -"FZ" = ( -/obj/effect/floor_decal/industrial/warning/corner{ - dir = 1 - }, -/obj/machinery/light{ - dir = 4 - }, -/turf/floor, -/area/example/first) "Gg" = ( /obj/structure/railing/mapped{ dir = 1 @@ -762,15 +741,9 @@ }, /turf/floor/tiled/steel_grid, /area/example/first) -"LM" = ( -/obj/effect/floor_decal/industrial/warning{ - dir = 1 - }, -/turf/floor, -/area/example/first) "LP" = ( -/turf/floor/plating, -/area/turbolift/example/first) +/turf/floor/tiled/dark/monotile, +/area/example/first) "LW" = ( /obj/machinery/teleport/station, /turf/floor/tiled/dark/monotile, @@ -810,12 +783,6 @@ /obj/effect/floor_decal/industrial/outline/red, /turf/floor/tiled/dark/monotile, /area/example/first) -"Pp" = ( -/obj/effect/floor_decal/industrial/warning/corner{ - dir = 4 - }, -/turf/floor, -/area/example/first) "Pv" = ( /obj/structure/rack, /obj/item/gun/projectile/shotgun/pump, @@ -871,9 +838,6 @@ }, /turf/floor/tiled/steel_grid, /area/example/first) -"RW" = ( -/turf/floor, -/area/turbolift/example/first) "Ss" = ( /obj/structure/tank_rack/oxygen, /obj/effect/floor_decal/corner/orange/half{ @@ -932,10 +896,10 @@ /turf/floor, /area/example/first) "Wi" = ( -/obj/effect/floor_decal/industrial/warning/fulltile, -/obj/effect/floor_decal/industrial/warning{ - dir = 8 +/obj/effect/floor_decal/corner/orange{ + dir = 6 }, +/obj/effect/floor_decal/industrial/warning, /turf/floor/tiled/steel_grid, /area/example/first) "Wj" = ( @@ -2892,9 +2856,9 @@ XZ XZ XZ mc -xT -gT -Pp +CU +WW +Yp VY kw kw @@ -2946,9 +2910,9 @@ KT oz XZ LP -LP -tA -LM +CU +WW +Yp VY kw kw @@ -3000,9 +2964,9 @@ CU oz XZ LP -LP -RW -LM +CU +WW +Yp on he tt @@ -3054,9 +3018,9 @@ CU oz XZ LP -LP -RW -LM +CU +WW +Yp Yp WF WF @@ -3107,10 +3071,10 @@ Mc CU oz XZ +LP +hA Wi -Wi -Wi -FZ +WS Yp jP HA diff --git a/maps/example/example-2.dmm b/maps/example/example-2.dmm index 51b091fc0d0..6e3749b02a9 100644 --- a/maps/example/example-2.dmm +++ b/maps/example/example-2.dmm @@ -130,6 +130,9 @@ /obj/effect/floor_decal/industrial/warning{ dir = 4 }, +/obj/structure/railing/mapped{ + dir = 4 + }, /turf/floor/tiled/dark/monotile, /area/example/second) "kf" = ( @@ -495,6 +498,9 @@ /obj/effect/floor_decal/industrial/warning{ dir = 4 }, +/obj/structure/railing/mapped{ + dir = 4 + }, /turf/floor/tiled/steel_grid, /area/example/second) "Gn" = ( @@ -649,14 +655,24 @@ }, /turf/floor, /area/example/second) +"Vp" = ( +/obj/structure/ladder, +/obj/structure/railing/mapped{ + dir = 4 + }, +/turf/open, +/area/example/second) "VG" = ( /obj/machinery/fabricator/bioprinter, /obj/effect/floor_decal/corner/blue/mono, /turf/floor/tiled/white/monotile, /area/example/second) "VL" = ( +/obj/structure/sign/warning/fall{ + pixel_y = 32 + }, /turf/open, -/area/turbolift/example/second) +/area/example/second) "WW" = ( /obj/effect/floor_decal/corner/mauve/mono, /obj/machinery/recycler, @@ -2588,7 +2604,7 @@ sA XE XE XE -NY +Vp jW FE Gn @@ -2643,8 +2659,8 @@ qV CW XE VL -VL -VL +wP +wP Gn wP wP @@ -2696,9 +2712,9 @@ CW CW CW XE -VL -VL -VL +wP +wP +wP Gn wP wP @@ -2750,9 +2766,9 @@ CW CW CW XE -VL -VL -VL +wP +wP +wP Gn wP wP diff --git a/maps/example/example-3.dmm b/maps/example/example-3.dmm index 018cb124270..37b7b892fb9 100644 --- a/maps/example/example-3.dmm +++ b/maps/example/example-3.dmm @@ -10,9 +10,6 @@ /obj/effect/floor_decal/industrial/warning/dust, /turf/floor, /area/example/third) -"cj" = ( -/turf/open, -/area/turbolift/example/third) "cw" = ( /obj/machinery/atmospherics/portables_connector, /obj/machinery/portable_atmospherics/canister/empty, @@ -273,10 +270,6 @@ }, /turf/floor, /area/example/third) -"TM" = ( -/obj/structure/catwalk, -/turf/open, -/area/example/third) "TO" = ( /obj/machinery/atmospherics/pipe/simple/hidden, /obj/effect/floor_decal/industrial/warning/dust{ @@ -1818,9 +1811,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -1872,9 +1865,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -1926,9 +1919,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -1980,9 +1973,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -2034,9 +2027,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -2088,9 +2081,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -2142,9 +2135,9 @@ Ke Ke Ke Ke -TM -TM -TM +Ke +Ke +Ke Ke Ke Ke @@ -2195,11 +2188,11 @@ Ke Ke Ke Ke -TM -TM -TM -TM -TM +Ke +Ke +Ke +Ke +Ke Ke Ke Ke @@ -2249,11 +2242,11 @@ Ke Ke Ke Ke -TM -cj -cj -cj -TM +Ke +Ke +Ke +Ke +Ke Ke Ke Ke @@ -2303,11 +2296,11 @@ Ke Ke Ke Ke -TM -cj -cj -cj -TM +Ke +Ke +Ke +Ke +Ke Ke Ke Ke @@ -2357,11 +2350,11 @@ Ke Ke Ke Ke -TM -cj -cj -cj -TM +Ke +Ke +Ke +Ke +Ke Ke Ke Ke @@ -2411,11 +2404,11 @@ Ke Ke Ke Ke -TM Ke Ke Ke -TM +Ke +Ke Ke Ke Ke @@ -2465,11 +2458,11 @@ Ke Ke Ke Ke -TM -TM -TM -TM -TM +Ke +Ke +Ke +Ke +Ke Ke Ke Ke diff --git a/maps/example/example_areas.dm b/maps/example/example_areas.dm index 097163dd93f..856a1b9daa2 100644 --- a/maps/example/example_areas.dm +++ b/maps/example/example_areas.dm @@ -1,4 +1,5 @@ /area/example + abstract_type = /area/example holomap_color = HOLOMAP_AREACOLOR_CREW /area/example/first @@ -13,40 +14,6 @@ name = "\improper Testing Site Third Floor" icon_state = "storage" -/area/turbolift/example - name = "\improper Testing Site Elevator" - icon_state = "shuttle" - requires_power = FALSE - dynamic_lighting = TRUE - sound_env = STANDARD_STATION - area_flags = AREA_FLAG_RAD_SHIELDED | AREA_FLAG_ION_SHIELDED - ambience = list( - 'sound/ambience/ambigen3.ogg', - 'sound/ambience/ambigen4.ogg', - 'sound/ambience/ambigen5.ogg', - 'sound/ambience/ambigen6.ogg', - 'sound/ambience/ambigen7.ogg', - 'sound/ambience/ambigen8.ogg', - 'sound/ambience/ambigen9.ogg', - 'sound/ambience/ambigen10.ogg', - 'sound/ambience/ambigen11.ogg', - 'sound/ambience/ambigen12.ogg' - ) - arrival_sound = null - lift_announce_str = null - - base_turf = /turf/open - -/area/turbolift/example/first - name = "Testing Site First Floor Lift" - base_turf = /turf/floor/plating - -/area/turbolift/example/second - name = "Testing Site Second Floor Lift" - -/area/turbolift/example/third - name = "Testing Site Third Floor Lift" - /area/shuttle/ferry name = "\improper Testing Site Ferry" icon_state = "shuttle" diff --git a/maps/example/example_shuttles.dm b/maps/example/example_shuttles.dm index bcc91db25c6..e78882e4162 100644 --- a/maps/example/example_shuttles.dm +++ b/maps/example/example_shuttles.dm @@ -29,23 +29,3 @@ ) ceiling_type = /turf/floor/shuttle_ceiling -/obj/abstract/turbolift_spawner/example - name = "Testing Site elevator placeholder" - icon = 'icons/obj/turbolift_preview_nowalls_3x3.dmi' - depth = 3 - lift_size_x = 2 - lift_size_y = 2 - door_type = null - wall_type = null - firedoor_type = null - light_type = null - floor_type = /turf/floor/tiled/techfloor - button_type = /obj/structure/lift/button/standalone - panel_type = /obj/structure/lift/panel/standalone - areas_to_use = list( - /area/turbolift/example/first, - /area/turbolift/example/second, - /area/turbolift/example/third - ) - floor_departure_sound = 'sound/effects/lift_heavy_start.ogg' - floor_arrival_sound = 'sound/effects/lift_heavy_stop.ogg' diff --git a/maps/example/example_unit_testing.dm b/maps/example/example_unit_testing.dm index df857c78a41..906851bd4fb 100644 --- a/maps/example/example_unit_testing.dm +++ b/maps/example/example_unit_testing.dm @@ -3,7 +3,6 @@ apc_test_exempt_areas = list( /area/space = NO_SCRUBBER|NO_VENT|NO_APC, /area/exoplanet = NO_SCRUBBER|NO_VENT|NO_APC, - /area/turbolift/example = NO_SCRUBBER|NO_VENT|NO_APC, /area/shuttle/ferry = NO_SCRUBBER|NO_VENT|NO_APC ) diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index b7aebcc8585..beecd85bba5 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -6631,7 +6631,7 @@ /turf/wall/prepainted, /area/exodus/maintenance/substation/security) "anR" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Security Substation Bypass" }, /turf/floor/plating, @@ -13766,7 +13766,7 @@ /turf/floor/plating, /area/exodus/maintenance/substation/civilian_east) "aDv" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Civilian East Substation Bypass" }, /turf/floor/plating, @@ -16993,7 +16993,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 9 }, -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Civilian West Substation Bypass" }, /turf/floor/plating, @@ -24402,7 +24402,7 @@ /turf/floor/plating, /area/exodus/maintenance/locker) "bay" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Medical Substation Bypass" }, /turf/floor/plating, @@ -32916,7 +32916,7 @@ /turf/floor/tiled/white, /area/exodus/medical/exam_room) "bsa" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Command Substation Bypass" }, /obj/machinery/light, @@ -51093,7 +51093,7 @@ /turf/floor/plating, /area/exodus/maintenance/cargo) "ccA" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Research Substation Bypass" }, /turf/floor/plating, @@ -53849,7 +53849,7 @@ /turf/floor/tiled/white/monotile, /area/exodus/medical/surgery2) "cib" = ( -/obj/machinery/power/breakerbox/activated{ +/obj/machinery/breakerbox/activated{ RCon_tag = "Engineering Substation Bypass" }, /turf/floor/plating, @@ -61848,10 +61848,8 @@ /turf/floor/plating, /area/exodus/engineering/engine_room) "cIa" = ( -/obj/machinery/emitter{ - anchored = 1; - id_tag = "EngineEmitter"; - state = 2 +/obj/machinery/emitter/anchored{ + id_tag = "EngineEmitter" }, /obj/structure/cable/cyan, /obj/machinery/power/terminal{ diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index 3553f269e87..5f5fa25e9fc 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -24,6 +24,7 @@ #include "../../mods/content/xenobiology/_xenobiology.dme" #include "../../mods/content/exploration/_exploration.dme" #include "../../mods/content/tabloids/_tabloids.dme" + #include "../../mods/content/turbolift/_turbolift.dme" #include "../../mods/gamemodes/cult/_cult.dme" #include "../../mods/gamemodes/heist/_heist.dme" diff --git a/maps/exodus/exodus_areas.dm b/maps/exodus/exodus_areas.dm index 336403c6218..516fb098544 100644 --- a/maps/exodus/exodus_areas.dm +++ b/maps/exodus/exodus_areas.dm @@ -11,6 +11,7 @@ //Do not remove dots after comments /area/exodus + abstract_type = /area/exodus secure = TRUE holomap_color = HOLOMAP_AREACOLOR_CREW diff --git a/maps/exodus/exodus_elevator.dm b/maps/exodus/exodus_elevator.dm index a455e5ad5bd..82176c89dc2 100644 --- a/maps/exodus/exodus_elevator.dm +++ b/maps/exodus/exodus_elevator.dm @@ -23,7 +23,7 @@ /obj/abstract/turbolift_spawner/exodus/engineering name = "Exodus turbolift map placeholder - Engineering" - icon = 'icons/obj/turbolift_preview_3x3.dmi' + icon = 'mods/content/turbolift/icons/turbolift_preview_3x3.dmi' dir = EAST lift_size_x = 4 lift_size_y = 4 diff --git a/maps/karzerfeste/karzerfeste.dm b/maps/karzerfeste/karzerfeste.dm index f656491c44f..b809ce1b646 100644 --- a/maps/karzerfeste/karzerfeste.dm +++ b/maps/karzerfeste/karzerfeste.dm @@ -7,6 +7,8 @@ #include "../../mods/species/drakes/_drakes.dme" // include before _fantasy.dme so overrides work #include "../../mods/species/neoavians/_neoavians.dme" // include before _fantasy.dme so overrides work #include "../../mods/content/fantasy/_fantasy.dme" + #include "../../mods/content/fishing/_fishing.dme" + #include "../../mods/content/undead/_undead.dme" #include "../../mods/content/biomods/_biomods.dme" #include "../../mods/pyrelight/_pyrelight.dme" // include after _fantasy.dme so overrides work diff --git a/maps/ministation/ministation-0.dmm b/maps/ministation/ministation-0.dmm index e3f4b0c453f..5441d9f8411 100644 --- a/maps/ministation/ministation-0.dmm +++ b/maps/ministation/ministation-0.dmm @@ -11078,10 +11078,8 @@ /obj/machinery/power/terminal{ dir = 1 }, -/obj/machinery/emitter{ - anchored = 1; - id_tag = "EngineEmitter"; - state = 2 +/obj/machinery/emitter/anchored{ + id_tag = "EngineEmitter" }, /obj/structure/cable, /turf/floor/plating, diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index fc3ebff786d..54715c60e4b 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -8744,7 +8744,7 @@ /turf/floor/tiled, /area/ministation/hall/e2) "Oo" = ( -/obj/machinery/beehive, +/obj/structure/apiary/mapped, /turf/floor/fake_grass, /area/ministation/hydro) "Op" = ( diff --git a/maps/ministation/ministation-2.dmm b/maps/ministation/ministation-2.dmm index 36d8182f59f..2d036207cdf 100644 --- a/maps/ministation/ministation-2.dmm +++ b/maps/ministation/ministation-2.dmm @@ -3147,7 +3147,7 @@ /obj/machinery/door/firedoor{ dir = 8 }, -/turf/floor/holofloor/lino, +/turf/floor/lino, /area/ministation/telecomms) "op" = ( /obj/machinery/light/small{ diff --git a/maps/ministation/ministation.dm b/maps/ministation/ministation.dm index cf34155e64f..5d97d3401df 100644 --- a/maps/ministation/ministation.dm +++ b/maps/ministation/ministation.dm @@ -32,6 +32,7 @@ Twice... #include "../../mods/content/mouse_highlights/_mouse_highlight.dme" #include "../../mods/content/pheromones/_pheromones.dme" #include "../../mods/content/psionics/_psionics.dme" + #include "../../mods/content/turbolift/_turbolift.dme" #include "../../mods/content/sealant_gun/_sealant_gun.dme" #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" diff --git a/maps/ministation/ministation_shuttles.dm b/maps/ministation/ministation_shuttles.dm index af90fe8838e..a79f5ae6243 100644 --- a/maps/ministation/ministation_shuttles.dm +++ b/maps/ministation/ministation_shuttles.dm @@ -73,7 +73,7 @@ // Essentially a bare platform that moves up and down. /obj/abstract/turbolift_spawner/ministation name = "Tradestation cargo elevator placeholder" -// icon = 'icons/obj/turbolift_preview_nowalls_3x3.dmi' +// icon = 'mods/content/turbolift/icons/turbolift_preview_nowalls_3x3.dmi' depth = 3 lift_size_x = 2 lift_size_y = 2 diff --git a/maps/modpack_testing/modpack_testing.dm b/maps/modpack_testing/modpack_testing.dm index aed6b8e3022..538894b9758 100644 --- a/maps/modpack_testing/modpack_testing.dm +++ b/maps/modpack_testing/modpack_testing.dm @@ -18,8 +18,10 @@ #include "../../mods/content/corporate/_corporate.dme" #include "../../mods/content/dungeon_loot/_dungeon_loot.dme" #include "../../mods/content/fantasy/_fantasy.dme" + #include "../../mods/content/fishing/_fishing.dme" #include "../../mods/content/generic_shuttles/_generic_shuttles.dme" #include "../../mods/content/government/_government.dme" + #include "../../mods/content/holodeck/_holodeck.dme" #include "../../mods/content/inertia/_inertia.dme" #include "../../mods/content/integrated_electronics/_integrated_electronics.dme" #include "../../mods/content/matchmaking/_matchmaking.dme" @@ -34,6 +36,7 @@ #include "../../mods/content/standard_jobs/_standard_jobs.dme" #include "../../mods/content/supermatter/_supermatter.dme" #include "../../mods/content/tabloids/_tabloids.dme" + #include "../../mods/content/turbolift/_turbolift.dme" #include "../../mods/content/undead/_undead.dme" #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" diff --git a/maps/shaded_hills/shaded_hills.dm b/maps/shaded_hills/shaded_hills.dm index 9e43e4d16b0..aa61c38ea5b 100644 --- a/maps/shaded_hills/shaded_hills.dm +++ b/maps/shaded_hills/shaded_hills.dm @@ -9,9 +9,12 @@ #include "../../mods/content/item_sharpening/_item_sharpening.dme" #include "../../mods/content/anima/_anima.dme" // include before _fantasy.dme so skill overrides work #include "../../mods/content/fantasy/_fantasy.dme" + #include "../../mods/content/fishing/_fishing.dme" + #include "../../mods/content/undead/_undead.dme" #include "../../mods/content/biomods/_biomods.dme" #include "../../mods/pyrelight/_pyrelight.dme" // include after _fantasy.dme so overrides work + #include "../../mods/content/blacksmithy/_blacksmithy.dme" #include "areas/_areas.dm" diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm index 34a51a33bfa..73454f89be1 100644 --- a/maps/tradeship/tradeship-1.dmm +++ b/maps/tradeship/tradeship-1.dmm @@ -2580,7 +2580,7 @@ /obj/effect/floor_decal/corner/beige{ dir = 5 }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light{ dir = 1; icon_state = "bulb1" @@ -2720,7 +2720,7 @@ dir = 1; level = 2 }, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/firealarm{ dir = 1; pixel_y = -21 @@ -2847,7 +2847,7 @@ /area/ship/trade/science/fabricaton) "Ib" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, -/obj/structure/holostool, +/obj/item/stool/padded, /obj/machinery/light{ dir = 1; icon_state = "bulb1" diff --git a/maps/tradeship/tradeship-2.dmm b/maps/tradeship/tradeship-2.dmm index 765635bb94f..63c009143c5 100644 --- a/maps/tradeship/tradeship-2.dmm +++ b/maps/tradeship/tradeship-2.dmm @@ -8057,7 +8057,7 @@ /turf/wall/titanium, /area/ship/trade/shuttle/rescue) "Za" = ( -/obj/machinery/power/breakerbox/activated, +/obj/machinery/breakerbox/activated, /obj/structure/cable{ icon_state = "1-2" }, diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index 2101d73bb84..36be422ad7a 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -34,6 +34,7 @@ #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/turbolift/_turbolift.dme" #include "../../mods/content/ventcrawl/_ventcrawl.dme" #include "../../mods/content/xenobiology/_xenobiology.dme" #include "../../mods/content/exploration/_exploration.dme" @@ -85,6 +86,7 @@ #include "tradeship_overrides.dm" #include "tradeship_shuttles.dm" #include "tradeship_spawnpoints.dm" + #include "tradeship_turbolift.dm" #include "tradeship_unit_testing.dm" #include "tradeship-0.dmm" #include "tradeship-1.dmm" diff --git a/maps/tradeship/tradeship_areas.dm b/maps/tradeship/tradeship_areas.dm index 1d46436626c..8451df7b1c1 100644 --- a/maps/tradeship/tradeship_areas.dm +++ b/maps/tradeship/tradeship_areas.dm @@ -10,8 +10,6 @@ /area/ship/trade name = "\improper Tradeship" ambience = list('sound/ambience/ambigen3.ogg','sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg','sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg','sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg','sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg','sound/ambience/ambigen12.ogg') - -/area/ship/trade holomap_color = HOLOMAP_AREACOLOR_CREW /area/ship/trade/crew diff --git a/maps/tradeship/tradeship_shuttles.dm b/maps/tradeship/tradeship_shuttles.dm index c309ad625a2..66b5728ba50 100644 --- a/maps/tradeship/tradeship_shuttles.dm +++ b/maps/tradeship/tradeship_shuttles.dm @@ -43,26 +43,3 @@ /obj/effect/shuttle_landmark/below_deck_starboardastern name = "Near CSV Tradeship Starboard Astern" landmark_tag = "nav_tradeship_below_starboardastern" - -// Essentially a bare platform that moves up and down. -/obj/abstract/turbolift_spawner/tradeship - name = "Tradeship cargo elevator placeholder" - icon = 'icons/obj/turbolift_preview_nowalls_4x4.dmi' - depth = 4 - lift_size_x = 3 - lift_size_y = 3 - door_type = null - wall_type = null - firedoor_type = null - light_type = null - floor_type = /turf/floor/tiled/steel_grid - button_type = /obj/structure/lift/button/standalone - panel_type = /obj/structure/lift/panel/standalone - areas_to_use = list( - /area/turbolift/tradeship_enclave, - /area/turbolift/tradeship_cargo, - /area/turbolift/tradeship_upper, - /area/turbolift/tradeship_roof - ) - floor_departure_sound = 'sound/effects/lift_heavy_start.ogg' - floor_arrival_sound = 'sound/effects/lift_heavy_stop.ogg' diff --git a/maps/tradeship/tradeship_turbolift.dm b/maps/tradeship/tradeship_turbolift.dm new file mode 100644 index 00000000000..2b8ecabee46 --- /dev/null +++ b/maps/tradeship/tradeship_turbolift.dm @@ -0,0 +1,22 @@ +// Essentially a bare platform that moves up and down. +/obj/abstract/turbolift_spawner/tradeship + name = "Tradeship cargo elevator placeholder" + icon = 'mods/content/turbolift/icons/turbolift_preview_nowalls_4x4.dmi' + depth = 4 + lift_size_x = 3 + lift_size_y = 3 + door_type = null + wall_type = null + firedoor_type = null + light_type = null + floor_type = /turf/floor/tiled/steel_grid + button_type = /obj/structure/lift/button/standalone + panel_type = /obj/structure/lift/panel/standalone + areas_to_use = list( + /area/turbolift/tradeship_enclave, + /area/turbolift/tradeship_cargo, + /area/turbolift/tradeship_upper, + /area/turbolift/tradeship_roof + ) + floor_departure_sound = 'sound/effects/lift_heavy_start.ogg' + floor_arrival_sound = 'sound/effects/lift_heavy_stop.ogg' diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index 5ddba61742d..82100776d7e 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -74,15 +74,6 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also toggleable var/emergency_shuttle_recall_message var/emergency_shuttle_arriving_at_dock_message - var/list/holodeck_programs = list() // map of string ids to /datum/holodeck_program instances - var/list/holodeck_supported_programs = list() // map of maps - first level maps from list-of-programs string id (e.g. "BarPrograms") to another map - // this is in order to support multiple holodeck program listings for different holodecks - // second level maps from program friendly display names ("Picnic Area") to program string ids ("picnicarea") - // as defined in holodeck_programs - var/list/holodeck_restricted_programs = list() // as above... but EVIL! - var/list/holodeck_default_program = list() // map of program list string ids to default program string id - var/list/holodeck_off_program = list() // as above... but for being off i guess - var/allowed_latejoin_spawns = list( /decl/spawnpoint/arrivals ) diff --git a/maps/~mapsystem/maps_unit_testing.dm b/maps/~mapsystem/maps_unit_testing.dm index 2ac13dfa9bb..e90ef209923 100644 --- a/maps/~mapsystem/maps_unit_testing.dm +++ b/maps/~mapsystem/maps_unit_testing.dm @@ -23,18 +23,13 @@ // These areas are used specifically by code and need to be broken out somehow var/list/area_usage_test_exempted_areas = list( - /area/ship, - /area/hallway, - /area/maintenance, /area/overmap, - /area/shuttle, /area/template_noop ) var/list/area_usage_test_exempted_root_areas = list( /area/map_template, /area/exoplanet, - /area/turbolift ) var/list/area_purity_test_exempt_areas = list() diff --git a/mods/README.md b/mods/README.md index c6b7a62b11f..3d154e8b1e6 100644 --- a/mods/README.md +++ b/mods/README.md @@ -38,8 +38,103 @@ Modpacks have a defined, user-controlled load order, and cross-modpack compatibi ### Enabling Your Modpack Modpacks are enabled on a per-map basis. To activate a modpack, you `#include` the modpack's .dme in a map's .dme file. -### Overriding Stock Code -TODO: Actually write this section. It's distinct from the "How do I write upstream/core code with extension via modpacks in mind?" part because it's the *opposite,* this section is about overriding core code while the other section is about writing core code to be extended. Maybe do a basic explanation of side-overrides and associated footguns here. +### Overriding Core Code +Sometimes a modpack needs to change how existing non-modpack code behaves, rather than just add new content. Because DM lets you extend any type in any file, a modpack can re-declare an existing type and redefine its vars or procs. This is called a *side-override*: normal overrides are created deeper in the type hierarchy on a subtype, while side-overrides exist 'to the side' of the existing override(s) for a type. (Even though it's not on a parent- or child-type, we still call `..()` the "parent call" even inside a side-override.) + +This is the *opposite* of the approach described in "How do I write upstream/core code with extension via modpacks in mind?" below. That section is about writing core code so modpacks can hook into it without touching it; this section is about the cases where you have to touch it anyway. Prefer the extension approach when stock code already offers a hook (a decl subtype to add, a list to append to, a subtype to iterate over). Reach for a side-override only when there's no such entry point. + +By convention, overrides go in a file named `overrides.dm` (or `_overrides.dm` for a focused group, e.g. `living_overrides.dm`) and are `#include`d from the modpack's `.dme` like any other file. Keeping them in clearly-named files makes it obvious at a glance which stock behavior a modpack changes. Cleverly-designed modpacks will define their core code hooks/overrides separate from per-type value overrides, so they can change as little as possible for each type, making changes less brittle. + +#### Overriding a var +The simplest type of override just extends an existing type and changes variable values: + +```dm +// mods/content/fantasy/items/material_overrides.dm +// FRANCE ISN'T REAL +/obj/item/chems/drinks/bottle/champagne + name = "sparkling wine bottle" + +/decl/material/liquid/alcohol/champagne + name = "sparkling wine" + glass_name = "sparkling wine" + glass_desc = "Sparkling white wine, a favourite at noble and merchant parties." + lore_text = "Sparkling white wine, a favourite at noble and merchant parties." +``` + +This is safe and done entirely at compile-time without adding any new code; it just changes the initial value of vars that the existing type already declares. The only conflict risk is two modpacks setting the same var on the same type to different values, in which case the last one loaded wins. Some modpacks may intend this, while others may want to write a compatibility patch (see below). + +#### Overriding a proc +To change behavior, redefine the proc on the existing type. Most overrides should call `..()` so the stock implementation (and any other modpack's override of it) still runs: + +```dm +// mods/content/augments/passive/armor.dm +// override to add armor augment damage mods +/obj/item/organ/external/get_brute_mod(var/damage_flags) + . = ..() // run the stock proc, keep its result + 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 +``` + +You can call `..()` at the start (to modify the result afterward), at the end (to run your logic first), or conditionally (to sometimes short-circuit and sometimes defer to stock): + +```dm +// mods/content/breath_holding/living_overrides.dm +// override to make a held breath take priority +/mob/living/get_breath(obj/item/organ/internal/lungs/lungs) + if(lungs?.holding_breath && lungs.held_breath) + return lungs.held_breath // intentionally skip the stock proc + return ..() +``` + +#### Overriding a static list getter (the injector pattern) +One common pattern in core code is the *static list getter,* used to avoid creating a new list every time the getter is called. This is much more efficient, but is a little more complex to override. Take this getter, for example: + +```dm +// code/game/objects/items/weapons/secrets_disk.dm +/obj/item/disk/secret_project/proc/get_secret_project_nouns() + var/static/list/nouns = list( + "a superluminal artillery cannon", "a fusion engine", "an atmospheric scrubber",\ + "a human cloning pod", "a microwave oven", "a wormhole generator", "a laser carbine", "an energy pistol",\ + "a wormhole", "a teleporter", "a huge mining drill", "a strange spacecraft", "a space station",\ + "a sleek-looking fighter spacecraft", "a ballistic rifle", "an energy sword", "an inanimate carbon rod" + ) + return nouns +``` + +We want to extend this by adding "a supermatter engine" to the list. A naive approach might be like this: + +```dm +//Example code not actually used +/obj/item/disk/secret_project/get_secret_project_nouns() + . = ..() + . += "a supermatter engine" +``` + +This works at first glance, if you call it once. However, because the getter uses a *static list,* it's saved between calls. That means it will be added every time we use the getter, which will quickly add a lot of duplicate entries to the list. Another naive fix for this would be using `|=` to avoid duplicates, but this is expensive because it checks if the item already exists in the list. Wouldn't it be nice to just add it once? + +For this, we use something called an injector, which uses a static var to track whether or not we've run our override before. If we're running it for the first time, we make all our changes to the static list returned by `..()`, and after that we set our tracking variable to ensure we never modify it again: + +```dm +// mods/content/supermatter/overrides/sm_strings.dm +/obj/item/disk/secret_project/get_secret_project_nouns() + var/static/sm_injected = FALSE + if(sm_injected) + return ..() + sm_injected = TRUE + . = ..() + . += "a supermatter engine" + return . +``` + +This also works for removing items from static lists, and may be useful for run-once code in other contexts as well. Another good example is in mods/content/corporate/items/random.dm. + +#### Footguns +- **Multiple side-overrides chain through `..()` in definition order.** Unlike a normal override, which lives on a new subtype deeper in the type tree, a side-override is defined directly on the existing type. If there are several side-overrides of `/mob/living/some_proc()`, they're all kept and chained: `..()` in the last-defined override calls the previous one, and so on down to the first, which then walks up the type tree to the base implementation. The order in which they run depends on the order they're defined in, so don't write a side-override that assumes it runs first, last, or in any particular position relative to another modpack's. (You can generally assume that it will run after the core definition, though.) +- **Extend, don't copy.** It may be easier to copy an existing proc definition, skip the parent call, and make a change somewhere in the middle. This is (almost) always a horrible idea, because you may not even notice something breaks when an update changes the definition you copied. The correct solution is to add it to an override that runs before or after the parent call, and if you *really* need to run it in the middle, consider adding a proc to the core code that your modpack can override (or split the existing proc into two or more). +- **Always call `..()` unless you really mean to break the chain.** Forgetting `..()` drops the base implementation *and* any earlier modpack's side-override, which can break unrelated core features and any other modpack that expected that proc to do its normal job. Only omit it when you genuinely intend to replace the behavior wholesale. +- **Don't depend on load order between modpacks.** A modpack may depend only on itself and stock code. If your override only makes sense when *another* modpack is also enabled, it's a cross-modpack interaction and belongs in `mods/~compatibility` (see below), which is loaded last and therefore modpack load-order agnostic. +- **Side-overrides are the most fragile thing to maintain across upstream changes.** When upstream code renames a proc, changes its signature, or alters what `..()` returns, your override breaks. The fewer side-overrides a modpack has, the less it breaks when upstream code is refactored. **There being no merge conflicts doesn't mean your code wasn't broken by an update!** ### Cross-modpack interactions Sometimes, a modpack that's enabled might need to do something in response to another modpack also being enabled. Compatibility patches allow for this to happen without the modpacks in question requiring a hard dependency on each other. @@ -62,7 +157,168 @@ Some modpacks extend other modpacks and make no sense to include on their own, i Modular code on a downstream with an upstream that does frequent refactors and rewrites is inevitably going to break when the upstream codebase does anything. Names change, so do assumptions, and even design directions might diverge so far that reconciling them will be hard or even impossible. There's not really getting around that, but we can at least mitigate it by designing stable interfaces and documenting changes. When upstream code is written with modularity in mind, downstreams have a much easier time adding content. ## How do I write upstream/core code with extension via modpacks in mind? -TODO: Actually write this section. Give examples like `/decl/atmos_grief_fix_step`, `/decl/human_examination`, the cocktails system, etc. Iterating over subtypes of a base type makes it easy for modpacks to add new code. Also maybe address some footguns like trying to make something modular before trying to make it actually work? Could also discuss the open-closed principle I guess, e.g. write code that gets *extended* rather than *modified* (so avoiding side-overrides where possible, etc.). +This is the counterpart of "Overriding Core Code" above. There, a modpack reaches into core code and changes it from the side; here, you're the one writing the core code, and your goal is to leave an *entry point* that modpacks can hook into without ever editing your code. The guiding idea is the open/closed principle: code should be open for extension but closed for modification. Every time a modpack can add a feature by writing a new file instead of side-overriding one of yours, that's one fewer thing that silently breaks when you refactor later. + +The single most useful tool for this is **iterating over decl subtypes.** Define an abstract decl as a hook point, write your core logic to enumerate every subtype of it and call into them, and modpacks extend the system simply by defining a new subtype. Nothing in core needs to know the modpack exists. + +### Pattern: action decls +Take the "fix atmospherics grief" admin tool. Core code defines an abstract decl with a small interface, then enumerates every subtype, sorts them, and calls each: + +```dm +// code/modules/admin/verbs/grief_fixers.dm +/decl/atmos_grief_fix_step + abstract_type = /decl/atmos_grief_fix_step + var/name + +/decl/atmos_grief_fix_step/proc/act() + return + +// ...elsewhere, the verb that runs them all: +var/list/steps = decls_repository.get_decls_of_subtype_unassociated(/decl/atmos_grief_fix_step) +steps = sortTim(steps.Copy(), /proc/cmp_decl_sort_value_asc) +for(var/decl/atmos_grief_fix_step/fix_step as anything in steps) + to_chat(usr, "[fix_step.name].") + fix_step.act() +``` + +A modpack can then add a step without touching any of the above. It just defines a new subtype and the core loop runs it in the specified order: + +```dm +// mods/content/supermatter/datums/sm_grief_fix.dm +/decl/atmos_grief_fix_step/supermatter + name = "Supermatter depowered" + sort_order = 0 + +/decl/atmos_grief_fix_step/supermatter/act() + // Depower the supermatter, as it would quickly blow up once we remove all gases from the pipes. + for(var/obj/structure/supermatter/S in SSsupermatter.processing) + S.power = 0 +``` + +Note the two things that make this clean: `abstract_type` marks the base as not-runnable so the enumeration only picks up real steps, and a `sort_order` var (read by the `cmp_decl_sort_value_asc` comparator) lets each subtype declare where it belongs in the sequence rather than relying on definition or load order. When you design a hook like this, give modpacks an explicit ordering knob instead of leaving order undefined. + +### Pattern: output builder decls +Enumerable decls don't have to *do* something; they can be useful just for a calculation or return value used in base-game code. Human examination text works this way. Base human examination code defines a stub decl whose whole purpose is to be subtyped by modpacks: + +```dm +// code/modules/mob/living/human/human_examine_decl.dm +/decl/human_examination //This is essentially a stub-method for modpacks to be able to add onto the human examination stuff + var/priority = 0 + +/decl/human_examination/proc/do_examine(mob/user, distance, mob/living/human/source, hideflags, decl/pronouns/pronouns) + return +``` + +Core's examine code enumerates these decls (sorted by `priority`) and appends whatever each returns. A modpack adds a line to the examine output by defining a subtype of `/decl/human_examination` and implementing `do_examine()`; see `mods/content/matchmaking/matchmaker.dm` for a working example. + +### Pattern: condition/recipe decls +Similarly to output builder decls (see prior section), the cocktails system (`code/modules/reagents/cocktails.dm`), chemical reaction system (`code\modules\reagents\reactions\_reaction.dm`), and stack recipe system (`code\modules\crafting\stack_recipes\_recipe.dm`) follow a similar idea: a base type that gets enumerated, so modpacks add new recipes by adding subtypes rather than editing a central list. By combining this with other principles, modpacks can extend, remove, or modify existing recipes, cocktails, reactions, etc. without needing to edit them directly. + +### Pattern: events (`/decl/observ`) +The decl patterns above allow core and modpack code to request extensible subtypes representing information, behavior, or conditions. Conversely, events allow modular code to request an update when something particular happens, and they're easily the most versatile tool for writing code that doesn't require side-overrides. When something notable happens, core code raises an event, and anything that cares can register to be notified. The code raising the event has no idea who's listening, and never needs a call added for each new listener. This is exactly what makes it good for modular code: a modpack can react to a core event (or even another modpack's event) without the event-issuing code containing a single reference to the consumer of that event. + +An event is a `/decl/observ` subtype. Defining one is just a declaration plus a doc comment describing the arguments listeners will receive: + +```dm +// code/datums/observation/death.dm +// Raised when: A mob dies. +// Arguments the called proc should expect: +// /mob/dying_mob: the mob that died. +/decl/observ/death + name = "Death" + expected_type = /mob +``` + +Events are typically raised with the `RAISE_EVENT` macro, passing the source as the first argument followed by any event-specific arguments. + +```dm +// code/datums/observation/death.dm +/mob/living/add_to_dead_mob_list() + . = ..() + if(.) + RAISE_EVENT(/decl/observ/death, src) +``` + +A modpack (or any object) hooks in by registering a callback through `events_repository`. The arguments are `(event_type, event_source, listener, proc_to_call)`; the listener's proc receives the event source plus whatever extra args the event documents. Crucially, you must **unregister** when you no longer care (and always before the listener is destroyed), or the listener will be forced to clean them up manually on deletion, which can be slow. This augment registers on the item it's holding and tears the registration down when that item goes away (through another event): + +```dm +// mods/content/augments/simple.dm +/obj/item/organ/internal/augment/active/simple/Initialize() + . = ..() + // ... + events_repository.register(/decl/observ/moved, holding, src, PROC_REF(check_holding)) + events_repository.register(/decl/observ/destroyed, holding, src, PROC_REF(check_holding)) + +/obj/item/organ/internal/augment/active/simple/proc/check_holding() + if(QDELETED(holding)) + events_repository.unregister(/decl/observ/moved, holding, src) + events_repository.unregister(/decl/observ/destroyed, holding, src) + holding = null +``` + +Pass `event_source` to register for events from one specific object; use `register_global(event_type, listener, proc_call)` to hear about that event from *every* source. Some high-traffic events forbid this for performance with the `OBSERVATION_NO_GLOBAL_REGISTRATIONS` flag, so check the event's definition; if writing something that may need that level of performance, `raise_event_non_global` can be used instead of `RAISE_EVENT`. + +When you're writing core code, raising an event is the right move whenever you can imagine *someone, someday* wanting to react to something (death, an item moving, a mob examining something) without you knowing who they are or why they want it. It costs one `RAISE_EVENT` line (and the overhead of dispatching events to listeners) and buys almost-unlimited extensibility in modpacks. + +The problem is it's easy to get overeager: every event has a small registration/dispatch cost, so raise them when needed rather than sprinkling them everywhere on the off chance. You can always make an upstream PR to add a new event when it's needed. + +As an aside, those familiar with TGstation's "DCS" system (datum, component, signal) will recognize this as very similar to TG's signals. They do functionally the same thing. + +### Pattern: extensions (`/datum/extension`) +Events let modpacks react to *moments*; extensions let them attach *state and behavior* to an object without subtyping it or piling vars onto its definition. An extension is a separate datum (`/datum/extension`) that hangs off a "holder" datum, keeping a self-contained feature's data and procs encapsulated in its own type instead of smeared across the holder's variable space. This is composition over inheritance: rather than making a new `/obj/item/chems/pill` subtype for "a pill that hides what it contains," you attach an `obfuscated_medication` extension to any pill. + +That separation of concerns is the whole point. The holder doesn't grow a var or a proc for the feature; the feature lives entirely in the extension, can be attached to several unrelated holder types (anything matching its `expected_type`), and can be added or removed at runtime. For a modpack this means adding a self-contained capability to a core object while not touching the core object's *definition* at all. This can even be useful in core code for functionality shared across types whose common ancestor is unacceptably early in the type hierarchy, like `/datum/extension/loaded_cell` (`code\datums\extensions\cell\cell.dm`) or `/datum/extension/padding` (`code\datums\extensions\padding\padding.dm`). + +An extension subtype sets `base_type` (attaching a second extension derived from the same `base_type` replaces the first) and `expected_type` (the holders it's allowed on, enforced at construction): + +```dm +// mods/content/bigpharma/extension.dm +/datum/extension/obfuscated_medication + base_type = /datum/extension/obfuscated_medication + expected_type = /obj/item + flags = EXTENSION_FLAG_IMMEDIATE + var/original_reagent + +/datum/extension/obfuscated_medication/pill + expected_type = /obj/item/chems/pill + +/datum/extension/obfuscated_medication/pill/update_appearance() + var/obj/item/pill = holder // every extension knows its holder + pill.icon_state = get_medication_icon_state_from_reagent_name(original_reagent, "pill", 1, 5) +``` + +You attach an extension to a holder via `set_extension(holder, extension_type, ...)`; any extra arguments are forwarded to the extension's `New()`/`post_construction()`. Then you can retrieve it with `get_extension(holder, base_type)`. By default extensions are lazy-loaded (only instantiated on first `get_extension`); set `EXTENSION_FLAG_IMMEDIATE` if it must exist the moment it's attached. There's also `has_extension()` (a cheap presence check that won't trigger lazy instantiation), `remove_extension()`, and `get_or_create_extension()`: + +```dm +// mods/content/augments/active/cyberbrain.dm +/obj/item/organ/internal/augment/active/cyberbrain/Initialize() + . = ..() + // ... + set_extension(src, /datum/extension/interactive/os/device/implant) + set_extension(src, /datum/extension/assembly/modular_computer/cyberbrain) + // ... + +/obj/item/organ/internal/augment/active/cyberbrain/proc/install_default_hardware() + var/datum/extension/assembly/assembly = get_extension(src, /datum/extension/assembly) + for(var/component_type in default_hardware) + assembly.try_install_component(null, new component_type(src)) +``` + +Note that the cyberbrain above attaches *two* unrelated extensions (a modular-computer assembly extension and the OS extension) to one organ. Each is a distinct concern with its own state, neither knows about the other, and neither required a new organ subtype. + +When you're writing core code, prefer an extension over adding vars/procs to a base type whenever the feature is **optional, self-contained, or only relevant to some instances.** This keeps the base type lean and gives modpacks a clean attachment point. Create a subtype instead when the behavior is intrinsic to what the object *is* rather than an add-on. As with all of these patterns, don't build an extension for something only one type will ever use and that isn't a separable concern; overengineering is the enemy of getting things done. + +As with observation events, those familiar with DCS will note that these are similar to components, with the caveat that explicitly checking for extensions and calling methods on them is perfectly acceptable. You can still avoid it through the use of events, and doing so will often lead to cleaner, more extensible code (after all, if you need to add a hook, chances are something else will too), but it is by no means mandatory or even preferred by all developers. + +### Other hooks worth leaving +- **Append to lists, don't replace them.** If core code builds a list that modpacks might want to add to, expose it (or build it from decl subtypes) so a modpack can contribute an entry. The static-list-getter injector pattern in "Overriding Core Code" above exists precisely *because* a getter didn't leave an easier entry point. Don't make modpacks resort to it if you can offer a cleaner hook. +- **Split a proc to create a hook.** If a modpack would otherwise need to side-override the middle of a long proc (the "Extend, don't copy" footgun), the right fix on the core side is to factor that middle out into its own overridable proc, so modpacks can override the small piece and call `..()`. +- **Add vars to a type for modpacks to fill in.** A core type can carry a var that core logic respects but only modpacks ever set, letting modpacks opt into behavior declaratively. + +### Footguns +- **Don't focus too hard on abstraction before getting something working.** It's tempting to design an elaborate system ahead of time to make implementation easier, but a structure for extension is only useful once you understand what that structure needs to accomplish. Implement a working feature first, *then* focus on the points modpacks might actually want to modify. Time spent abstracting and modularizing a system that's fundamentally broken is time wasted. +- **A stable interface is a promise.** Once modpacks (and downstreams) hook into your decl or proc, renaming it or changing its signature breaks them silently. Treat hook points as a small, deliberate API: keep them narrow, name them clearly, and **document them,** because changing them is more troublesome than changing ordinary internal code. +- **Be aware of subtype ordering.** If the order your subtypes run in matters, give them an explicit ordering var (like `sort_order`/`priority` above). Relying on enumeration or load order makes behavior depend on which modpacks happen to be enabled, which is exactly the kind of fragility this whole approach is meant to avoid. # Contribution Please contribute to this README/guide. It's currently unfinished and doesn't cover a lot of important things. Thanks. \ No newline at end of file diff --git a/mods/_modpack.dm b/mods/_modpack.dm index 0fb1d5d7908..b9bad395444 100644 --- a/mods/_modpack.dm +++ b/mods/_modpack.dm @@ -75,6 +75,14 @@ /decl/modpack/proc/on_roundstart() return +/// This runs before `global.using_map.finalize_map_generation()` in SSmapping initialize. +/decl/modpack/proc/on_mapping_pre_finalize() + return + +/// This runs in SSmisc_late Initialize. +/decl/modpack/proc/on_misc_late_init() + return + /decl/modpack/proc/get_membership_perks() return diff --git a/mods/content/augments/active/polytool.dm b/mods/content/augments/active/polytool.dm index ad72c0f405b..09d7ec605d6 100644 --- a/mods/content/augments/active/polytool.dm +++ b/mods/content/augments/active/polytool.dm @@ -14,9 +14,9 @@ var/obj/item/I = new path (src) I.canremove = FALSE items += I - events_repository.register(/decl/observ/moved, I, src, /obj/item/organ/internal/augment/active/polytool/proc/check_holding) - events_repository.register(/decl/observ/destroyed, I, src, /obj/item/organ/internal/augment/active/polytool/proc/check_holding) - events_repository.register(/decl/observ/item_unequipped, I, src, /obj/item/organ/internal/augment/active/polytool/proc/check_holding) + events_repository.register(/decl/observ/moved, I, src, PROC_REF(check_holding)) + events_repository.register(/decl/observ/destroyed, I, src, PROC_REF(check_holding)) + events_repository.register(/decl/observ/item_unequipped, I, src, PROC_REF(check_holding)) /obj/item/organ/internal/augment/active/polytool/Destroy() for(var/obj/item/item in items) diff --git a/mods/content/augments/simple.dm b/mods/content/augments/simple.dm index 14e48b5ca45..52410b05c91 100644 --- a/mods/content/augments/simple.dm +++ b/mods/content/augments/simple.dm @@ -12,9 +12,9 @@ holding.canremove = FALSE if(!origin_tech) origin_tech = holding.get_origin_tech() - events_repository.register(/decl/observ/moved, holding, src, /obj/item/organ/internal/augment/active/simple/proc/check_holding) - events_repository.register(/decl/observ/destroyed, holding, src, /obj/item/organ/internal/augment/active/simple/proc/check_holding) - events_repository.register(/decl/observ/item_unequipped, holding, src, /obj/item/organ/internal/augment/active/simple/proc/check_holding) + events_repository.register(/decl/observ/moved, holding, src, PROC_REF(check_holding)) + events_repository.register(/decl/observ/destroyed, holding, src, PROC_REF(check_holding)) + events_repository.register(/decl/observ/item_unequipped, holding, src, PROC_REF(check_holding)) /obj/item/organ/internal/augment/active/simple/proc/check_holding() diff --git a/mods/content/beekeeping/_beekeeping.dm b/mods/content/beekeeping/_beekeeping.dm index d3b58be746d..53ca84daf62 100644 --- a/mods/content/beekeeping/_beekeeping.dm +++ b/mods/content/beekeeping/_beekeeping.dm @@ -1,7 +1,13 @@ +#define FRAME_RESERVE_COST 30 +#define SWARM_AGITATION_PER_FRAME 25 +#define FRAME_MATERIAL_COST 20 +#define SWARM_GROWTH_COST 10 +#define FRAME_FILL_MATERIAL_COST 5 +#define HIVE_REPAIR_MATERIAL_COST 5 + /decl/modpack/beekeeping - name = "Beekeeping Content" + name = "Beekeeping and Insects Content" /datum/storage/hopper/industrial/centrifuge/New() ..() can_hold |= /obj/item/hive_frame - diff --git a/mods/content/beekeeping/_beekeeping.dme b/mods/content/beekeeping/_beekeeping.dme index 75778704f36..92ee753943b 100644 --- a/mods/content/beekeeping/_beekeeping.dme +++ b/mods/content/beekeeping/_beekeeping.dme @@ -3,10 +3,17 @@ // BEGIN_INCLUDE #include "_beekeeping.dm" #include "closets.dm" -#include "hive_frame.dm" #include "items.dm" +#include "materials.dm" #include "recipes.dm" #include "trading.dm" -#include "hives\_hive.dm" +#include "hives\hive_extension.dm" +#include "hives\hive_flora.dm" +#include "hives\hive_frame.dm" +#include "hives\hive_queen.dm" +#include "hives\hive_structure.dm" +#include "hives\hive_swarm.dm" +#include "hives\insect_species\_insects.dm" +#include "hives\insect_species\insects_pollinators.dm" // END_INCLUDE #endif diff --git a/mods/content/beekeeping/closets.dm b/mods/content/beekeeping/closets.dm index c39e70dbb45..daf63a942d5 100644 --- a/mods/content/beekeeping/closets.dm +++ b/mods/content/beekeeping/closets.dm @@ -1,11 +1,11 @@ /obj/structure/closet/crate/hydroponics/beekeeping name = "beekeeping crate" - desc = "All you need to set up your own beehive." + desc = "All you need to set up your own beehive, except the beehive." /obj/structure/closet/crate/hydroponics/beekeeping/Initialize() . = ..() - new /obj/item/beehive_assembly(src) - new /obj/item/bee_smoker(src) + new /obj/item/stack/material/plank/mapped/wood/ten + new /obj/item/smoker(src) new /obj/item/hive_frame/crafted(src) new /obj/item/hive_frame/crafted(src) new /obj/item/hive_frame/crafted(src) diff --git a/mods/content/beekeeping/hive_frame.dm b/mods/content/beekeeping/hive_frame.dm deleted file mode 100644 index 35ffd429777..00000000000 --- a/mods/content/beekeeping/hive_frame.dm +++ /dev/null @@ -1,55 +0,0 @@ -/obj/item/hive_frame - abstract_type = /obj/item/hive_frame - icon_state = ICON_STATE_WORLD - w_class = ITEM_SIZE_SMALL - material_alteration = MAT_FLAG_ALTERATION_ALL - chem_volume = 20 - var/destroy_on_centrifuge = FALSE - -/obj/item/hive_frame/on_reagent_change() - . = ..() - if(REAGENT_TOTAL_VOLUME(reagents)) - SetName("filled [initial(name)] ([reagents.get_primary_reagent_name()])") - else - SetName(initial(name)) - queue_icon_update() - -/obj/item/hive_frame/on_update_icon() - . = ..() - var/mesh_state = "[icon_state]-mesh" - if(check_state_in_icon(mesh_state, icon)) - add_overlay(overlay_image(icon, mesh_state, COLOR_WHITE, RESET_COLOR)) - if(REAGENT_TOTAL_VOLUME(reagents)) - var/comb_state = "[icon_state]-comb" - if(check_state_in_icon(comb_state, icon)) - add_overlay(overlay_image(icon, comb_state, reagents.get_color(), RESET_COLOR)) - compile_overlays() - -/obj/item/hive_frame/handle_centrifuge_process(obj/machinery/centrifuge/centrifuge) - if(!(. = ..())) - return - if(REAGENT_TOTAL_VOLUME(reagents)) - reagents.trans_to_holder(centrifuge.loaded_beaker.reagents, REAGENT_TOTAL_VOLUME(reagents)) - for(var/obj/item/thing in contents) - thing.dropInto(centrifuge.loc) - if(destroy_on_centrifuge) - for(var/atom/movable/thing in convert_matter_to_lumps()) - thing.dropInto(centrifuge.loc) - -// Crafted frame used in apiaries. -/obj/item/hive_frame/crafted - name = "hive frame" - desc = "A wooden frame for insect hives that the workers will fill with products like honey." - icon = 'mods/content/beekeeping/icons/frame.dmi' - material = /decl/material/solid/organic/wood/oak - material_alteration = MAT_FLAG_ALTERATION_ALL - -// TEMP until beewrite redoes hives. -/obj/item/hive_frame/crafted/filled/Initialize() - . = ..() - new /obj/item/stack/material/bar/wax(src) - update_icon() - -/obj/item/hive_frame/crafted/filled/populate_reagents() - . = ..() - reagents.add_reagent(/decl/material/liquid/nutriment/honey, REAGENT_MAXIMUM_VOLUME(reagents)) diff --git a/mods/content/beekeeping/hives/_hive.dm b/mods/content/beekeeping/hives/_hive.dm deleted file mode 100644 index 990d148a16f..00000000000 --- a/mods/content/beekeeping/hives/_hive.dm +++ /dev/null @@ -1,168 +0,0 @@ -/obj/machinery/beehive - name = "apiary" - icon = 'mods/content/beekeeping/icons/beekeeping.dmi' - icon_state = "beehive-0" - desc = "A wooden box designed specifically to house our buzzling buddies. Far more efficient than traditional hives. Just insert a frame and a queen, close it up, and you're good to go!" - density = TRUE - anchored = TRUE - layer = BELOW_OBJ_LAYER - - var/closed = 0 - var/bee_count = 0 // Percent - var/smoked = 0 // Timer - var/honeycombs = 0 // Percent - var/frames = 0 - var/maxFrames = 5 - -/obj/machinery/beehive/Initialize() - . = ..() - update_icon() - -/obj/machinery/beehive/on_update_icon() - overlays.Cut() - icon_state = "beehive-[closed]" - if(closed) - overlays += "lid" - if(frames) - overlays += "empty[frames]" - if(honeycombs >= 100) - overlays += "full[round(honeycombs / 100)]" - if(!smoked) - switch(bee_count) - if(1 to 20) - overlays += "bees1" - if(21 to 40) - overlays += "bees2" - if(41 to 60) - overlays += "bees3" - if(61 to 80) - overlays += "bees4" - if(81 to 100) - overlays += "bees5" - -/obj/machinery/beehive/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - if(!closed) - . += "The lid is open." - -/obj/machinery/beehive/attackby(var/obj/item/used_item, var/mob/user) - if(IS_CROWBAR(used_item)) - closed = !closed - user.visible_message("\The [user] [closed ? "closes" : "opens"] \the [src].", "You [closed ? "close" : "open"] \the [src].") - update_icon() - return TRUE - else if(IS_WRENCH(used_item)) - anchored = !anchored - user.visible_message("\The [user] [anchored ? "wrenches" : "unwrenches"] \the [src].", "You [anchored ? "wrench" : "unwrench"] \the [src].") - return TRUE - else if(istype(used_item, /obj/item/bee_smoker)) - if(closed) - to_chat(user, "You need to open \the [src] with a crowbar before smoking the bees.") - return TRUE - user.visible_message("\The [user] smokes the bees in \the [src].", "You smoke the bees in \the [src].") - smoked = 30 - update_icon() - return TRUE - else if(istype(used_item, /obj/item/hive_frame/crafted)) - if(closed) - to_chat(user, "You need to open \the [src] with a crowbar before inserting \the [used_item].") - return TRUE - if(frames >= maxFrames) - to_chat(user, "There is no place for an another frame.") - return TRUE - var/obj/item/hive_frame/crafted/H = used_item - if(REAGENT_TOTAL_VOLUME(H.reagents)) - to_chat(user, "\The [used_item] is full with beeswax and honey, empty it in the extractor first.") - return TRUE - ++frames - user.visible_message("\The [user] loads \the [used_item] into \the [src].", "You load \the [used_item] into \the [src].") - update_icon() - qdel(used_item) - return TRUE - else if(istype(used_item, /obj/item/bee_pack)) - var/obj/item/bee_pack/B = used_item - if(B.full && bee_count) - to_chat(user, "\The [src] already has bees inside.") - return TRUE - if(!B.full && bee_count < 90) - to_chat(user, "\The [src] is not ready to split.") - return TRUE - if(!B.full && !smoked) - to_chat(user, "Smoke \the [src] first!") - return TRUE - if(closed) - to_chat(user, "You need to open \the [src] with a crowbar before moving the bees.") - return TRUE - if(B.full) - user.visible_message("\The [user] puts the queen and the bees from \the [used_item] into \the [src].", "You put the queen and the bees from \the [used_item] into \the [src].") - bee_count = 20 - B.empty() - else - user.visible_message("\The [user] puts bees and larvae from \the [src] into \the [used_item].", "You put bees and larvae from \the [src] into \the [used_item].") - bee_count /= 2 - B.fill() - update_icon() - return TRUE - else if(istype(used_item, /obj/item/scanner/plant)) - to_chat(user, "Scan result of \the [src]...") - to_chat(user, "Beehive is [bee_count ? "[round(bee_count)]% full" : "empty"].[bee_count > 90 ? " Colony is ready to split." : ""]") - if(frames) - to_chat(user, "[frames] frames installed, [round(honeycombs / 100)] filled.") - if(honeycombs < frames * 100) - to_chat(user, "Next frame is [round(honeycombs % 100)]% full.") - else - to_chat(user, "No frames installed.") - if(smoked) - to_chat(user, "The hive is smoked.") - return TRUE - else if(IS_SCREWDRIVER(used_item)) - if(bee_count) - to_chat(user, "You can't dismantle \the [src] with these bees inside.") - return TRUE - to_chat(user, "You start dismantling \the [src]...") - playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1) - if(do_after(user, 30, src)) - user.visible_message("\The [user] dismantles \the [src].", "You dismantle \the [src].") - new /obj/item/beehive_assembly(loc) - qdel(src) - return TRUE - return FALSE // this should probably not be a machine, so don't do any component interactions - -/obj/machinery/beehive/physical_attack_hand(var/mob/user) - if(closed) - return FALSE - . = TRUE - if(honeycombs < 100) - to_chat(user, "There are no filled honeycombs.") - return - if(!smoked && bee_count) - to_chat(user, "The bees won't let you take the honeycombs out like this, smoke them first.") - return - user.visible_message("\The [user] starts taking the honeycombs out of \the [src].", "You start taking the honeycombs out of \the [src]...") - while(honeycombs >= 100 && do_after(user, 30, src)) - new /obj/item/hive_frame/crafted/filled(loc) - honeycombs -= 100 - --frames - update_icon() - if(honeycombs < 100) - to_chat(user, "You take all filled honeycombs out.") - -/obj/machinery/beehive/Process() - if(closed && !smoked && bee_count) - pollinate_flowers() - update_icon() - smoked = max(0, smoked - 1) - if(!smoked && bee_count) - bee_count = min(bee_count * 1.005, 100) - update_icon() - -/obj/machinery/beehive/proc/pollinate_flowers() - var/coef = bee_count / 100 - var/trays = 0 - for(var/obj/machinery/portable_atmospherics/hydroponics/H in view(7, src)) - if(H.seed && !H.dead) - H.plant_health += 0.05 * coef - if(H.pollen >= 1) - H.pollen-- - trays++ - honeycombs = min(honeycombs + 0.1 * coef * min(trays, 5), frames * 100) diff --git a/mods/content/beekeeping/hives/hive_extension.dm b/mods/content/beekeeping/hives/hive_extension.dm new file mode 100644 index 00000000000..5e91d1c1c23 --- /dev/null +++ b/mods/content/beekeeping/hives/hive_extension.dm @@ -0,0 +1,218 @@ +/datum/extension/insect_hive + base_type = /datum/extension/insect_hive + expected_type = /obj/structure + flags = EXTENSION_FLAG_IMMEDIATE + /// The species of insect that made this hive. + var/decl/insect_species/holding_species + /// References to our current swarm effects gathering for the hive. + var/list/swarms + var/current_health = 100 + var/material = 10 + var/raw_reserves = 0 + /// Tracker for the last world.time that a frame was removed. + var/frame_last_removed = 0 + /// Tracker for time that smoke will wear off. + var/smoked_until = 0 + +/datum/extension/insect_hive/New(datum/holder, _species_decl) + ..() + holding_species = istype(_species_decl, /decl/insect_species) ? _species_decl : GET_DECL(_species_decl) + if(!istype(holding_species)) + CRASH("Insect hive extension instantiated with invalid insect species: '[_species_decl]'.") + START_PROCESSING(SSprocessing, src) + +/datum/extension/insect_hive/Destroy() + STOP_PROCESSING(SSprocessing, src) + if(length(swarms)) + for(var/obj/effect/insect_swarm/swarm as anything in swarms) + swarm.owner = null + swarms = null + var/atom/movable/hive = holder + if(istype(hive) && !QDELETED(hive)) + hive.queue_icon_update() + return ..() + +/datum/extension/insect_hive/Process() + if(world.time < smoked_until) + return + holding_species.process_hive(src) + create_hive_products() + +/datum/extension/insect_hive/proc/handle_item_interaction(mob/user, obj/item/item) + return FALSE + +/datum/extension/insect_hive/proc/drop_nest(atom/drop_loc) + if(!isatom(drop_loc)) + return + // handle some kind of physical hive dropping here + remove_extension(holder, /datum/extension/insect_hive) + if(!QDELETED(src)) + qdel(src) + +/datum/extension/insect_hive/proc/get_nest_condition() + switch(current_health) + if(0, 10) + return "dying" + if(10, 30) + return "struggling" + if(30, 60) + return "sickly" + if(60, 90) + return null + return "thriving" + +/datum/extension/insect_hive/proc/get_nest_name() + return holding_species?.nest_name + +/datum/extension/insect_hive/proc/examined(mob/user, show_detail) + var/nest_descriptor = get_nest_condition() + if(nest_descriptor) + to_chat(user, SPAN_NOTICE("It contains \a [nest_descriptor] [get_nest_name()].")) + else + to_chat(user, SPAN_NOTICE("It contains \a [get_nest_name()].")) + +/datum/extension/insect_hive/proc/frame_removed(obj/item/frame) + frame_last_removed = world.time + if(world.time >= smoked_until && length(swarms) > 0) + if(isatom(holder)) + var/atom/hive = holder + hive.visible_message(SPAN_DANGER("The buzzing from \the [holder] intensifies.")) + for(var/obj/effect/insect_swarm/swarm as anything in swarms) + swarm.swarm_agitation = min(100, swarm.swarm_agitation + SWARM_AGITATION_PER_FRAME) + +/datum/extension/insect_hive/proc/try_hand_harvest(mob/user, obj/item/structure) + if(istype(structure) && !structure.storage) + var/obj/item/hive_frame/frame = locate() in structure + if(frame) + frame.dropInto(get_turf(structure)) + if(istype(user)) + user.put_in_hands(frame) + return TRUE + return FALSE + +/datum/extension/insect_hive/proc/try_tool_harvest(mob/user, obj/item/tool) + return FALSE + +/datum/extension/insect_hive/proc/swarm_destroyed(obj/effect/insect_swarm/swarm) + return + +/datum/extension/insect_hive/proc/swarm_at_hive() + for(var/atom/movable/swarm as anything in swarms) + if(get_turf(swarm) == get_turf(holder)) + return swarm + +/datum/extension/insect_hive/proc/has_material(amt) + return amt <= material + +/datum/extension/insect_hive/proc/consume_material(amt) + if(has_material(amt)) + material = clamp(material-amt, 0, 100) + return TRUE + return FALSE + +/datum/extension/insect_hive/proc/add_material(amt) + material = clamp(material+amt, 0, 100) + return TRUE + +/datum/extension/insect_hive/proc/add_reserves(amt) + raw_reserves = clamp(raw_reserves+amt, 0, 100) + return TRUE + +/datum/extension/insect_hive/proc/has_reserves(amt, raw_reserves_only = TRUE) + if(raw_reserves >= amt) + return TRUE + if(raw_reserves_only) + return FALSE + var/reserve = 0 + for(var/obj/item/frame in holder) + reserve += REAGENT_TOTAL_VOLUME(frame.reagents) + if(reserve >= amt) + return TRUE + return FALSE + +/datum/extension/insect_hive/proc/consume_reserves(amt, raw_reserves_only = TRUE) + if(!has_reserves(amt, raw_reserves_only)) + return FALSE + if(raw_reserves >= amt) + raw_reserves -= amt + return TRUE + if(raw_reserves_only) + return FALSE + amt -= raw_reserves + raw_reserves = 0 + for(var/obj/item/frame in holder) + if(!REAGENT_TOTAL_VOLUME(frame.reagents)) + continue + var/consume = min(amt, REAGENT_TOTAL_VOLUME(frame.reagents)) + frame.reagents.remove_any(consume) + amt -= consume + if(amt <= 0) + return TRUE + return FALSE + +/datum/extension/insect_hive/proc/adjust_health(amt) + current_health = clamp(current_health + amt, 0, 100) + if(current_health <= 0) + var/atom/movable/hive = holder + hive.visible_message(SPAN_DANGER("\The [holding_species.nest_name] sags and collapses.")) + remove_extension(holder, base_type) + +/datum/extension/insect_hive/proc/create_hive_products() + + var/atom/movable/hive = holder + if(!istype(hive) || !holding_species) + return TRUE + + if(!swarm_at_hive()) // nobody home to do the work + return TRUE + + // Naturally build up enough material for a new frame (or repairs). + if(!has_material(FRAME_MATERIAL_COST)) + add_material(1) + + // Damaged hives cannot produce combs or honey. + if(current_health < 100) + if(consume_material(HIVE_REPAIR_MATERIAL_COST)) + adjust_health(rand(3,5)) + return TRUE + + if(!has_reserves(FRAME_RESERVE_COST)) + return TRUE + + var/list/holder_contents = hive.get_contained_external_atoms() + for(var/obj/item/hive_frame/frame in holder_contents) + if(!frame.reagents || (REAGENT_TOTAL_VOLUME(frame.reagents) >= REAGENT_MAXIMUM_VOLUME(frame.reagents))) + continue + var/fill_cost = REAGENTS_FREE_SPACE(frame.reagents) + if(has_material(FRAME_FILL_MATERIAL_COST) && has_reserves(fill_cost)) + consume_material(FRAME_FILL_MATERIAL_COST) + consume_reserves(fill_cost) + holding_species.fill_hive_frame(frame) + return TRUE + + var/obj/item/native_frame = holding_species.native_frame_type + var/native_frame_size = initial(native_frame.w_class) + var/space_left = hive.storage.max_storage_space + for(var/obj/item/thing in hive.get_stored_inventory()) + space_left -= thing.w_class + if(space_left < native_frame_size) + return + + // Put a timer check on this to avoid a hive filling up with combs the moment you take 2 frames out. + if(world.time > (frame_last_removed + 2 MINUTES) && space_left >= native_frame_size && consume_material(FRAME_MATERIAL_COST)) + // Frames start empty, and will be filled next run. + // Native 'frames' (combs) are bigger than crafted ones and aren't reusable. + new native_frame(holder, holding_species.produce_material) + hive.storage.update_ui_after_item_insertion() + +/datum/extension/insect_hive/proc/get_total_swarm_intensity() + . = 0 + for(var/obj/effect/insect_swarm/swarm as anything in swarms) + . += swarm.swarm_intensity + +/datum/extension/insect_hive/proc/smoked_by(mob/user, atom/source, smoke_time = 1 MINUTE) + smoked_until = max(smoked_until, world.time + smoke_time) + // this is a little weird due to telekinetic bee smoking but so it goes + for(var/obj/effect/insect_swarm/swarm as anything in swarms) + swarm.was_smoked(max(0, smoked_until-world.time)) + return TRUE diff --git a/mods/content/beekeeping/hives/hive_flora.dm b/mods/content/beekeeping/hives/hive_flora.dm new file mode 100644 index 00000000000..d6bc3a50c34 --- /dev/null +++ b/mods/content/beekeeping/hives/hive_flora.dm @@ -0,0 +1,40 @@ +/obj/structure/flora + /// Percentage chance of trying to spawn an insect hive here, if appropriate. + var/insect_hive_chance = 20 + +/obj/structure/flora/Initialize(ml, _mat, _reinf_mat) + . = ..() + if(insect_hive_chance && length(get_supported_insects())) + return INITIALIZE_HINT_LATELOAD + +/obj/structure/flora/LateInitialize() + ..() + if(prob(insect_hive_chance) && !has_extension(src, /datum/extension/insect_hive)) + var/list/insects = get_supported_insects() + if(length(insects)) + insects = insects.Copy() // don't mutate the static list. + for(var/species_type in insects) + var/decl/insect_species/species = GET_DECL(species_type) + if(!species.can_spawn_in_flora(src)) + insects -= species_type + if(length(insects)) + set_extension(src, /datum/extension/insect_hive, pickweight(insects)) + update_icon() + +// Insect species that can hive in this flora. +/obj/structure/flora/proc/get_supported_insects() + return + +/obj/structure/flora/tree/get_supported_insects() + var/static/list/_insects = list( + /decl/insect_species/honeybees = 10, + ///decl/insect_species/wasps = 1 + ) + return _insects + +/obj/structure/flora/stump/get_supported_insects() + var/static/list/_insects = list( + /decl/insect_species/honeybees = 10, + ///decl/insect_species/wasps = 1 + ) + return _insects diff --git a/mods/content/beekeeping/hives/hive_frame.dm b/mods/content/beekeeping/hives/hive_frame.dm new file mode 100644 index 00000000000..473e667e521 --- /dev/null +++ b/mods/content/beekeeping/hives/hive_frame.dm @@ -0,0 +1,91 @@ +/obj/item/hive_frame + abstract_type = /obj/item/hive_frame + icon_state = ICON_STATE_WORLD + w_class = ITEM_SIZE_SMALL + material_alteration = MAT_FLAG_ALTERATION_ALL + chem_volume = 20 + var/destroy_on_centrifuge = FALSE + +/obj/item/hive_frame/on_reagent_change() + . = ..() + if(REAGENT_TOTAL_VOLUME(reagents)) + SetName("filled [initial(name)] ([reagents.get_primary_reagent_name()])") + else + SetName(initial(name)) + queue_icon_update() + +/obj/item/hive_frame/on_update_icon() + . = ..() + var/mesh_state = "[icon_state]-mesh" + if(check_state_in_icon(mesh_state, icon)) + add_overlay(overlay_image(icon, mesh_state, COLOR_WHITE, RESET_COLOR)) + if(REAGENT_TOTAL_VOLUME(reagents)) + var/comb_state = "[icon_state]-comb" + if(check_state_in_icon(comb_state, icon)) + add_overlay(overlay_image(icon, comb_state, reagents.get_color(), RESET_COLOR)) + compile_overlays() + +/obj/item/hive_frame/handle_centrifuge_process(obj/machinery/centrifuge/centrifuge) + if(!(. = ..())) + return + if(REAGENT_TOTAL_VOLUME(reagents)) + reagents.trans_to_holder(centrifuge.loaded_beaker.reagents, REAGENT_TOTAL_VOLUME(reagents)) + for(var/obj/item/thing in contents) + thing.dropInto(centrifuge.loc) + if(destroy_on_centrifuge) + for(var/atom/movable/thing in convert_matter_to_lumps()) + thing.dropInto(centrifuge.loc) + +/obj/item/hive_frame/honey/populate_reagents() + . = ..() + var/decl/insect_species/bees = GET_DECL(/decl/insect_species/honeybees) + bees.fill_hive_frame(src) + +/obj/item/hive_frame/forceMove(atom/dest) + var/atom/old_loc = loc + . = ..() + if(. && istype(old_loc)) + check_hive_loc(old_loc) + +/obj/item/hive_frame/Move() + var/atom/old_loc = loc + . = ..() + if(. && istype(old_loc)) + check_hive_loc(old_loc) + +/obj/item/hive_frame/proc/check_hive_loc(atom/check_loc) + var/datum/extension/insect_hive/hive = get_extension(check_loc, /datum/extension/insect_hive) + if(istype(hive) && loc != hive.holder) + hive.frame_removed(src) + +// Crafted frame used in apiaries. +/obj/item/hive_frame/crafted + name = "hive frame" + desc = "A wooden frame for insect hives that the workers will fill with products like honey." + icon = 'mods/content/beekeeping/icons/frame.dmi' + material = /decl/material/solid/organic/wood/oak + +// Raw version of honeycomb for wild hives. +/obj/item/hive_frame/comb + name = "comb" + icon = 'mods/content/beekeeping/icons/comb.dmi' + material = /decl/material/solid/organic/wax + destroy_on_centrifuge = TRUE + material_alteration = MAT_FLAG_ALTERATION_COLOR + is_spawnable_type = FALSE + w_class = ITEM_SIZE_NORMAL // Larger than crafted frames, because you should use crafted frames in your hive. + +/obj/item/hive_frame/comb/Initialize(ml, material_key, decl/insect_species/spawning_hive) + . = ..() + if(istype(spawning_hive)) + SetName(spawning_hive.native_frame_name) + desc = spawning_hive.native_frame_desc + spawning_hive.fill_hive_frame(src) + +// Comb subtype for mapping and debugging. +/obj/item/hive_frame/comb/honey + is_spawnable_type = TRUE + color = COLOR_GOLD + +/obj/item/hive_frame/comb/honey/Initialize(ml, material_key) + return ..(ml, material_key, GET_DECL(/decl/insect_species/honeybees)) diff --git a/mods/content/beekeeping/hives/hive_queen.dm b/mods/content/beekeeping/hives/hive_queen.dm new file mode 100644 index 00000000000..599b487d243 --- /dev/null +++ b/mods/content/beekeeping/hives/hive_queen.dm @@ -0,0 +1,23 @@ +/obj/item/bee_pack + name = "bee pack" + desc = "Contains a queen bee and some worker bees. Everything you'll need to start a hive!" + icon = 'mods/content/beekeeping/icons/bee_pack.dmi' + material = /decl/material/solid/organic/plastic + var/contains_insects = /decl/insect_species/honeybees + +/obj/item/bee_pack/Initialize() + . = ..() + update_icon() + +/obj/item/bee_pack/on_update_icon() + . = ..() + if(contains_insects) + add_overlay("[icon_state]-full") + else + add_overlay("[icon_state]-empty") + +/obj/item/bee_pack/proc/empty() + SetName("empty [initial(name)]") + desc = "A stasis pack for moving bees. It's empty." + contains_insects = null + update_icon() diff --git a/mods/content/beekeeping/hives/hive_structure.dm b/mods/content/beekeeping/hives/hive_structure.dm new file mode 100644 index 00000000000..855f9e42c7b --- /dev/null +++ b/mods/content/beekeeping/hives/hive_structure.dm @@ -0,0 +1,86 @@ +/obj/structure/attackby(obj/item/used_item, mob/user) + if((. = ..())) + return + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive) && hive.handle_item_interaction(user, used_item)) + return TRUE + +/obj/structure/attack_hand(mob/user) + if(has_extension(src, /datum/extension/insect_hive)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(hive.try_hand_harvest(user, src)) + return TRUE + return ..() + +/obj/structure/attackby(obj/item/used_item, mob/user) + if(has_extension(src, /datum/extension/insect_hive)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(hive.try_tool_harvest(user, used_item)) + return TRUE + return ..() + +/obj/structure/examined_by(mob/user, distance, infix, suffix) + . = ..() + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive)) + hive.examined(user, (distance <= 1)) + +/atom/physically_destroyed(var/skip_qdel) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + hive?.drop_nest(loc) + return ..() + +/obj/structure/dismantle_structure(mob/user) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + hive?.drop_nest(loc) + return ..() + +// 'proper' nest structure for building and mapping +/obj/structure/apiary + name = "apiary" + desc = "An artificial hive for raising insects, like bees, and harvesting products like honey." + icon = 'mods/content/beekeeping/icons/apiary.dmi' + icon_state = ICON_STATE_WORLD + density = TRUE + anchored = TRUE + storage = /datum/storage/apiary + material_alteration = MAT_FLAG_ALTERATION_ALL + material = /decl/material/solid/organic/wood/oak + color = /decl/material/solid/organic/wood/oak::color + obj_flags = OBJ_FLAG_ANCHORABLE + tool_interaction_flags = (TOOL_INTERACTION_ANCHOR | TOOL_INTERACTION_DECONSTRUCT) + +/obj/structure/apiary/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + return air_group || height == 0 || !density || (istype(mover) && mover.checkpass(PASS_FLAG_TABLE)) + +/obj/structure/apiary/attackby(obj/item/used_item, mob/user) + + if(istype(used_item, /obj/item/bee_pack)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive)) + to_chat(user, SPAN_WARNING("\The [src] already contains \a [hive.holding_species.nest_name].")) + return TRUE + var/obj/item/bee_pack/pack = used_item + if(!pack.contains_insects) + to_chat(user, SPAN_WARNING("\The [pack] is empty!")) + return TRUE + user.visible_message(SPAN_NOTICE("\The [user] transfers the contents of \the [pack] into \the [src].")) + set_extension(src, /datum/extension/insect_hive, pack.contains_insects) + pack.empty() + return TRUE + + . = ..() + +/datum/storage/apiary + can_hold = list(/obj/item/hive_frame) + max_w_class = ITEM_SIZE_NORMAL + max_storage_space = ITEM_SIZE_SMALL * 5 // Five regular frames. + +/obj/structure/apiary/mapped/Initialize(ml, _mat, _reinf_mat) + . = ..() + for(var/_ = 1 to 5) + new /obj/item/hive_frame/crafted(src) + +/obj/structure/apiary/mapped/bees/Initialize(ml, _mat, _reinf_mat) + set_extension(src, /datum/extension/insect_hive, /decl/insect_species/honeybees) + . = ..() diff --git a/mods/content/beekeeping/hives/hive_swarm.dm b/mods/content/beekeeping/hives/hive_swarm.dm new file mode 100644 index 00000000000..c05a7d9972d --- /dev/null +++ b/mods/content/beekeeping/hives/hive_swarm.dm @@ -0,0 +1,371 @@ +/obj/effect/insect_swarm + anchored = TRUE + is_spawnable_type = FALSE + icon_state = "0" + gender = NEUTER + default_pixel_z = 8 + layer = ABOVE_HUMAN_LAYER + pass_flags = PASS_FLAG_TABLE + movement_handlers = list(/datum/movement_handler/delay/insect_swarm = list(1 SECOND)) + + /// Current movement target for automove (ie. hive, flowers or victim) + VAR_PRIVATE/atom/move_target + /// Reference to our owning hive. + var/datum/extension/insect_hive/owner + /// Reference to our insect archetype. + var/decl/insect_species/insect_type + /// A counter for disturbances to the hive or this swarm, causes them to sting people. + var/swarm_agitation = 0 + /// Percentage value; if it drops to 0, the swarm will be destroyed. + var/swarm_intensity = 1 + /// Cooldown timer for next tick. + VAR_PRIVATE/next_work = 0 + /// Time that smoke will wear off. + var/smoked_until = 0 + +/datum/movement_handler/delay/insect_swarm/DoMove(direction, mob/mover, is_external) + ..() + step(host, direction) + return MOVEMENT_HANDLED + +/obj/effect/insect_swarm/debug/Initialize(mapload) + . = ..(mapload, _insect_type = /decl/insect_species/honeybees) + +/obj/effect/insect_swarm/Initialize(mapload, _insect_type, _hive) + . = ..() + insect_type = istype(_insect_type, /decl/insect_species) ? _insect_type : GET_DECL(_insect_type) + owner = _hive + if(!istype(insect_type)) + PRINT_STACK_TRACE("Insect swarm created with invalid insect type: '[_insect_type]'") + return INITIALIZE_HINT_QDEL + if(!istype(owner)) + PRINT_STACK_TRACE("Insect swarm created with invalid hive: '[owner]'") + return INITIALIZE_HINT_QDEL + update_transform() + update_swarm() + LAZYDISTINCTADD(owner.swarms, src) + START_PROCESSING(SSobj, src) + +/obj/effect/insect_swarm/Destroy() + if(owner) + owner.swarm_destroyed(src) + LAZYREMOVE(owner.swarms, src) + owner = null + stop_automove() + STOP_PROCESSING(SSobj, src) + return ..() + +// Resolves the current swarm amount to a coarser value used for icon state selection. +/obj/effect/insect_swarm/proc/get_swarm_state() + return ceil((swarm_intensity / insect_type.max_swarm_intensity) * insect_type.max_swarm_state) + +/obj/effect/insect_swarm/on_update_icon() + . = ..() + color = insect_type.swarm_color + icon = insect_type.swarm_icon + icon_state = num2text(get_swarm_state()) + if(is_smoked()) + icon_state = "[icon_state]_smoked" + +/obj/effect/insect_swarm/update_transform() + . = ..() + // Some icon variation via transform. + if(prob(75)) + var/matrix/swarm_transform = transform || matrix() + swarm_transform.Turn(pick(90, 180, 270)) + transform = swarm_transform + +/obj/effect/insect_swarm/proc/update_swarm() + update_icon() + if(get_swarm_state() == 1) + SetName(insect_type.name_singular) + desc = insect_type.insect_desc + gender = NEUTER + else + SetName(insect_type.name_plural) + desc = insect_type.swarm_desc + gender = PLURAL + +/obj/effect/insect_swarm/proc/is_agitated() + return QDELETED(owner) || (swarm_agitation > 0 && !is_smoked()) + +/obj/effect/insect_swarm/proc/find_sting_target() + for(var/mob/living/victim in view(7, src)) + if(victim.simulated && !victim.is_playing_dead()) + return victim + +/obj/effect/insect_swarm/proc/merge(obj/effect/insect_swarm/other_swarm) + + // If we can fit into one swarm, just merge us together. + var/total_intensity = swarm_intensity + other_swarm.swarm_intensity + if(total_intensity <= insect_type.max_swarm_intensity) + swarm_intensity = total_intensity + swarm_agitation = max(swarm_agitation, other_swarm.swarm_agitation) + update_swarm() + qdel(other_swarm) + return + + // Otherwise equalize between swarms. + swarm_intensity = floor(total_intensity / 2) + other_swarm.swarm_intensity = total_intensity - swarm_intensity + swarm_agitation = max(swarm_agitation, other_swarm.swarm_agitation) + other_swarm.swarm_agitation = max(swarm_agitation, other_swarm.swarm_agitation) + update_swarm() + other_swarm.update_swarm() + +/obj/effect/insect_swarm/Move() + . = ..() + // Swarms from the same hive in the same loc merge together. + if(. && loc && !QDELETED(src)) + try_consolidate_swarms() + +/obj/effect/insect_swarm/Process() + + // Swarms on a loc should try to merge if possible. + try_consolidate_swarms() + if(QDELETED(src)) + return + + // Swarms with no hive gradually decay to nothing. + if(!owner) + adjust_swarm_intensity(-(rand(1,3))) + if(QDELETED(src)) + return + + if(!move_target || !(move_target in view(5, src))) + stop_automove() + + if(is_smoked()) + return + + // Angry swarms move with purpose. + if(is_agitated()) + swarm_agitation = max(0, swarm_agitation-1) + if(!ismob(move_target)) + var/mob/new_move_target = find_sting_target() + if(istype(new_move_target)) + move_target = new_move_target + if(move_target) + start_automove(move_target) + if(insect_type.sting_amount || insect_type.sting_reagent) + insect_type.try_sting(src, loc) + return + + // Large swarms split if they aren't agitated. + if(swarm_can_split() && isturf(loc)) + var/turf/our_turf = loc + for(var/turf/swarm_turf as anything in RANGE_TURFS(our_turf, 1)) + if(swarm_turf == loc || !swarm_turf.CanPass(src)) + continue + var/new_intensity = round(swarm_intensity/2) + var/obj/effect/insect_swarm/new_swarm = new type(swarm_turf, insect_type, owner) + new_swarm.swarm_intensity = new_intensity + new_swarm.swarm_agitation = swarm_agitation + new_swarm.update_swarm() + swarm_intensity -= new_intensity + update_swarm() + break + + // Sting people, if we are so inclined. + if(insect_type.sting_amount || insect_type.sting_reagent) + insect_type.try_sting(src, loc) + + // Hive behavior is dictated by the hive. + if(owner) + handle_hive_behavior() + return + + // If we're not agitated and don't have a hive, we probably shouldn't be pathing somewhere. + stop_automove() + + // Idle swarms with no hive just wander around. + if(prob(5)) + SelfMove(pick(global.alldirs)) + +/obj/effect/insect_swarm/proc/is_first_swarm_at_hive() + var/atom/movable/hive = owner?.holder + if(!isturf(hive?.loc) || loc != hive.loc) + return FALSE + if(length(owner?.swarms) == 1) + return TRUE + for(var/obj/effect/insect_swarm/swarm in hive.loc) + if(swarm == src) + return TRUE + if(swarm in owner.swarms) + break + return FALSE + +/obj/effect/insect_swarm/finished_automove() + ..() + next_work = world.time // we'be a busy bee, check to for new work once you reach your destination + return FALSE + +/obj/effect/insect_swarm/get_automove_target(datum/automove_metadata/metadata) + return move_target + +/obj/effect/insect_swarm/stop_automove() + move_target = null + . = ..() + +/obj/effect/insect_swarm/can_do_automated_move(variant_move_delay) + return ..() && !is_smoked() + +/obj/effect/insect_swarm/start_automove(target, movement_type, datum/automove_metadata/metadata) + move_target = target + . = ..() + +/obj/effect/insect_swarm/get_default_automove_controller_type() + return /decl/automove_controller/stop_on_fail_or_completion + +/obj/effect/insect_swarm/proc/handle_hive_behavior() + + var/atom/movable/hive = owner?.holder + if(!isturf(loc)) + // We've just been created; shunt us out onto the turf. + if(loc == hive) + dropInto(hive.loc) + else + return + + // If we are the first (or only) of our owner swarms in the loc, and we aren't needed, we don't move. Hive needs workers. + if(owner?.has_reserves(FRAME_RESERVE_COST)) + if(is_first_swarm_at_hive()) + stop_automove() + return + if(!hive_has_swarm() && loc != hive.loc) + start_automove(hive) + return + + do_work() + +/obj/effect/insect_swarm/proc/do_work() + stop_automove() + if(prob(25)) + var/step_dir = pick(global.alldirs) + if(get_dist(owner.holder, get_step(loc, step_dir)) <= 2) + SelfMove(step_dir) + +/obj/effect/insect_swarm/proc/hive_has_swarm() + var/atom/movable/hive = owner?.holder + if(!isturf(hive?.loc)) + return FALSE + for(var/obj/item/swarm as anything in owner.swarms) + if(swarm.loc == hive.loc) + return TRUE + return FALSE + +/obj/effect/insect_swarm/proc/adjust_swarm_intensity(amount) + var/old_intensity = swarm_intensity + swarm_intensity = clamp(swarm_intensity + amount, 0, insect_type.max_swarm_intensity) + if(old_intensity != swarm_intensity) + if(swarm_intensity <= 0) + qdel(src) + else + update_swarm() + +/obj/effect/insect_swarm/proc/can_grow() + // higher swarm intensity is only seen during agitated states when they converge on a victim and merge. + return swarm_intensity < insect_type.max_swarm_growth_intensity + +/obj/effect/insect_swarm/proc/can_merge() + return swarm_intensity < (is_agitated() ? insect_type.max_swarm_intensity : insect_type.max_swarm_growth_intensity) + +/obj/effect/insect_swarm/proc/swarm_can_split() + return !is_agitated() && swarm_intensity > insect_type.max_swarm_growth_intensity + +/obj/effect/insect_swarm/proc/try_consolidate_swarms() + if(!can_merge()) + return + for(var/obj/effect/insect_swarm/other_swarm in loc) + if(other_swarm == src || !other_swarm.can_merge() || other_swarm.owner != owner || other_swarm.insect_type != insect_type) + continue + merge(other_swarm) + return + +/obj/effect/insect_swarm/pollinator + var/pollen = 0 + +/obj/effect/insect_swarm/pollinator/do_work() + + // Have a rest/do some work. + if(world.time < next_work) + return + + var/atom/movable/hive = owner?.holder + + // Move to move target (hive or flowers) + if(move_target) + if(!(move_target in view(src, 7))) // no longer able to see our move target + stop_automove() + // don't bail early if we just stopped automove, that would introduce stutter as it'd take one tick to decide what to do next + else + // let us automove, don't restart it + return + + // Unload pollen into hive. + if(pollen) + if(loc == get_turf(hive)) + owner.add_reserves(pollen) + pollen = 0 + next_work = world.time + 5 SECONDS + stop_automove() + else + start_automove(hive) + return + + // Harvest from flowers in our loc. + for(var/obj/machinery/portable_atmospherics/hydroponics/flower in loc) + if(!flower.pollen) + continue + if(flower.seed && !flower.dead) + flower.plant_health += rand(3, 5) + flower.check_plant_health() + pollen += flower.pollen + flower.pollen = 0 + next_work = world.time + 5 SECONDS + stop_automove() + return + + // Same logic for flora. TODO unify these when seeds are rewritten to be less bespoke. + for(var/obj/structure/flora/plant/flower in loc) + if(!flower.pollen) + continue + pollen += flower.pollen + flower.pollen = 0 + next_work = world.time + 5 SECONDS + stop_automove() + return + + // Find a flower. + var/list/all_potential_targets = list() + for(var/thing in view(src, 7)) + if(istype(thing, /obj/machinery/portable_atmospherics/hydroponics)) + var/obj/machinery/portable_atmospherics/hydroponics/flower = thing + if(flower.pollen) + all_potential_targets += flower + else if(istype(thing, /obj/structure/flora/plant)) + var/obj/structure/flora/plant/flower = thing + if(flower.pollen) + all_potential_targets += flower + + var/closest_dist + var/atom/closest_target + for(var/atom/thing as anything in shuffle(all_potential_targets)) + var/next_dist = get_dist(src, thing) + if(isnull(closest_target) || next_dist < closest_dist) + closest_target = thing + closest_dist = next_dist + + if(closest_target) + start_automove(closest_target) + else + start_automove(hive) + +/obj/effect/insect_swarm/proc/was_smoked(smoke_time = 1 MINUTE) + smoked_until = max(smoked_until, world.time + smoke_time) + swarm_agitation = round(swarm_agitation * 0.75) + update_icon() + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon), TRUE), smoke_time, (TIMER_UNIQUE|TIMER_OVERRIDE)) + +/obj/effect/insect_swarm/proc/is_smoked() + return world.time < smoked_until \ No newline at end of file diff --git a/mods/content/beekeeping/hives/insect_species/_insects.dm b/mods/content/beekeeping/hives/insect_species/_insects.dm new file mode 100644 index 00000000000..d05460b9a5e --- /dev/null +++ b/mods/content/beekeeping/hives/insect_species/_insects.dm @@ -0,0 +1,200 @@ +/decl/insect_species + abstract_type = /decl/insect_species + + // Descriptive strings for individual insects and swarms. + var/name_singular + var/name_plural + var/insect_desc + + // Vars for nest description and products. + var/nest_name + var/list/produce_reagents + var/decl/material/produce_material + var/produce_material_amount = 1 + var/native_frame_name = "comb" + var/native_frame_desc = "A wax comb from an insect nest." + var/native_frame_type = /obj/item/hive_frame/comb + + // Visual appearance and behavior of swarms. + var/swarm_desc + var/swarm_color = COLOR_BROWN + var/swarm_icon = 'mods/content/beekeeping/icons/swarm.dmi' + var/swarm_type = /obj/effect/insect_swarm + var/max_swarm_growth_intensity = 50 + var/max_swarm_intensity = 100 + var/max_swarm_state = 6 + + // Venom delivered by swarms whens stinging a victim. + var/sting_reagent + var/sting_amount + var/per_sting_reagents = 0.1 + var/per_sting_pain = 1 + +/decl/insect_species/Initialize() + if(produce_material) + produce_material = GET_DECL(produce_material) + return ..() + +/decl/insect_species/validate() + . = ..() + + if(!name_singular) + . += "no singular name set" + if(!name_plural) + . += "no plural name set" + if(!nest_name) + . += "no nest name set" + if(!insect_desc) + . += "no insect desc set" + + if(swarm_type) + if(!ispath(swarm_type, /obj/effect/insect_swarm)) + . += "invalid swarm path (must be /obj/effect/insect_swarm or subtype): '[swarm_type]'" + if(!swarm_desc) + . += "no swarm description set" + + if(produce_reagents) + if(!length(produce_reagents) || !islist(produce_reagents)) + . += "empty or non-list produce_reagents" + else + var/total = 0 + for(var/reagent in produce_reagents) + if(!ispath(reagent, /decl/material)) + . += "non-material produce_reagents entry '[reagent]'" + continue + var/amt = produce_reagents[reagent] + if(!isnum(amt) || amt <= 0) + . += "non-numerical or 0 produce_reagents value: '[reagent]', '[amt]'" + total += amt + if(total != 1) + . += "produce_reagents weighting does not sum to 1: '[total]'" + + if(produce_material) + if(!isnum(produce_material_amount) || produce_material_amount <= 0) + . += "non-numeric or zero produce amount: '[produce_material_amount]'" + if(!istype(produce_material, /decl/material)) + . += "non-material product material type: '[produce_material]'" + + if(!swarm_icon) + . += "null swarm icon" + else + for(var/i = 0 to max_swarm_state) + var/check_state = num2text(i) + if(!check_state_in_icon(check_state, swarm_icon)) + . += "missing active icon_state '[check_state]'" + check_state = "[check_state]_smoked" + if(!check_state_in_icon(check_state, swarm_icon)) + . += "missing smoked icon_state '[check_state]'" + +/decl/insect_species/proc/fill_hive_frame(obj/item/frame) + + if(!istype(frame) || QDELETED(frame)) + return FALSE + + var/frame_space = REAGENTS_FREE_SPACE(frame.reagents) + if(frame_space <= 0) + return FALSE + + if(REAGENT_MAXIMUM_VOLUME(frame.reagents) && length(produce_reagents)) + var/reagent_split = max(1, floor(min(REAGENTS_FREE_SPACE(frame.reagents), 20) / length(produce_reagents))) + for(var/reagent in produce_reagents) + frame.reagents.add_reagent(reagent, max(1, (reagent_split * produce_reagents[reagent])), defer_update = TRUE) + frame.reagents.handle_update() + if(produce_material && (frame.material != produce_material) && !(locate(/obj/item/stack/material/lump) in frame)) + for(var/atom/movable/thing in produce_material.create_object(frame, produce_material_amount, /obj/item/stack/material/lump)) + thing.forceMove(frame) + return TRUE + +/decl/insect_species/proc/try_sting(obj/effect/insect_swarm/swarm, atom/loc) + if(!istype(swarm) || QDELETED(swarm) || !istype(loc)) + return FALSE + // If we're agitated, always sting. Otherwise, % chance equal to a quarter of our overall swarm intensity. + if(!swarm.is_agitated() && !prob(max(1, round(swarm.swarm_intensity/4)))) + return FALSE + var/base_sting_chance = (sting_amount * clamp(round(swarm.swarm_intensity/10), 1, 10)) + var/sting_mult = swarm.is_agitated() ? max(base_sting_chance, 15) : base_sting_chance + for(var/mob/living/victim in loc) + if(!victim.simulated || victim.stat || victim.current_posture?.prone) + continue + var/datum/reagents/injected_reagents = victim.get_injected_reagents() + var/obj/item/organ/external/affecting = victim.get_organ(pick(global.all_limb_tags)) + if(!affecting || BP_IS_PROSTHETIC(affecting) || BP_IS_CRYSTAL(affecting)) + continue + if(!injected_reagents || !victim.can_inject(null, affecting.organ_tag)) + continue + + to_chat(victim, SPAN_DANGER("\A [swarm] stings you [sting_mult <= sting_amount * 2 ? "" : "multiple times"] on your [affecting.name]!")) + + var/sting_venom = (per_sting_reagents * sting_mult) - REAGENT_VOLUME(injected_reagents, sting_reagent) + if(sting_venom > 0) + injected_reagents.add_reagent(sting_reagent, sting_venom) + + var/sting_pain = (per_sting_pain * sting_mult) - victim.getHalLoss() + if(sting_pain > 0) + affecting.add_pain(sting_pain) + + . = TRUE + +/decl/insect_species/proc/can_spawn_in_flora(var/obj/structure/flora) + + // Territory range. + for(var/obj/structure/flora/plant in view(flora, 7)) + if(has_extension(plant, /datum/extension/insect_hive)) + return FALSE + + // Food source. + for(var/obj/machinery/portable_atmospherics/hydroponics/flower in view(flora, 7)) + if(flower.seed?.produces_pollen) + return TRUE + + for(var/obj/structure/flora/plant/flower in view(flora, 7)) + if(flower.plant?.produces_pollen) + return TRUE + + + return FALSE + +/decl/insect_species/proc/process_hive(datum/extension/insect_hive/hive_metadata) + + // Sanity check. + var/atom/movable/hive = hive_metadata.holder + if(!istype(hive) || !swarm_type || !istype(hive_metadata)) + return + + // Make sure we always have at least one swarm. + if(!length(hive_metadata.swarms)) + new swarm_type(hive, src, hive_metadata) + + // Reduce swarms if we have too many. + var/swarm_intensity = hive_metadata.get_total_swarm_intensity() + if(swarm_intensity > max_swarm_intensity && length(hive_metadata.swarms)) + var/obj/effect/insect_swarm/swarm = hive_metadata.swarms[1] + swarm.adjust_swarm_intensity(-(swarm_intensity-max_swarm_intensity)) + return + + // Try to grow an existing swarm until we're at our max. + if(hive_metadata.has_reserves(SWARM_GROWTH_COST) && length(hive_metadata.swarms)) + for(var/obj/effect/insect_swarm/swarm as anything in hive_metadata.swarms) + if(swarm.can_grow() && hive_metadata.consume_reserves(SWARM_GROWTH_COST)) + swarm.adjust_swarm_intensity(min(max_swarm_growth_intensity-swarm_intensity, rand(3,5))) + return + + // If we have sufficient filled combs, create a new swarm. Otherwise, expand a swarm. + if(hive.loc && hive_metadata.has_reserves(SWARM_GROWTH_COST)) + + var/obj/effect/insect_swarm/swarm + for(var/obj/effect/insect_swarm/check_swarm as anything in hive_metadata.swarms) + if(check_swarm.loc == hive.loc && check_swarm.can_grow()) + swarm = check_swarm + break + + if(!swarm) + var/comb_count = 0 + for(var/obj/item/hive_frame/frame in hive) + if(REAGENT_TOTAL_VOLUME(frame.reagents) >= REAGENT_MAXIMUM_VOLUME(frame.reagents)) + comb_count++ + if(length(hive_metadata.swarms) < comb_count) + swarm = new swarm_type(hive.loc, src, hive_metadata) + + if(!QDELETED(swarm) && istype(swarm) && hive_metadata.consume_reserves(SWARM_GROWTH_COST)) + swarm.adjust_swarm_intensity(min((max_swarm_growth_intensity-swarm_intensity), rand(3,5))) diff --git a/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm b/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm new file mode 100644 index 00000000000..460ca2ef16b --- /dev/null +++ b/mods/content/beekeeping/hives/insect_species/insects_pollinators.dm @@ -0,0 +1,28 @@ +/decl/insect_species/honeybees + name_singular = "honeybee" + name_plural = "honeybees" + nest_name = "beehive" + native_frame_name = "honeycomb" + native_frame_desc = "A lattice of hexagonal wax cells usually filled with honey." + native_frame_type = /obj/item/hive_frame/comb + swarm_desc = "A swarm of buzzing honeybees." + insect_desc = "A single buzzing honeybee." + swarm_color = COLOR_GOLD + swarm_type = /obj/effect/insect_swarm/pollinator + sting_reagent = /decl/material/liquid/bee_venom + sting_amount = 0.2 + produce_reagents = list(/decl/material/liquid/nutriment/honey = 1) + produce_material = /decl/material/solid/organic/wax + +/* +/decl/insect_species/wasps + name_singular = "wasp" + name_plural = "wasps" + nest_name = "wasp hive" + swarm_desc = "A swarm of humming wasps." + insect_desc = "A solitary wasp." + sting_reagent = /decl/material/liquid/cyanide + sting_amount = 1 + swarm_color = COLOR_BRONZE + swarm_type = /obj/effect/insect_swarm/pollinator // tarantula hunter... +*/ \ No newline at end of file diff --git a/mods/content/beekeeping/icons/apiary.dmi b/mods/content/beekeeping/icons/apiary.dmi new file mode 100644 index 00000000000..7ad80a4408f Binary files /dev/null and b/mods/content/beekeeping/icons/apiary.dmi differ diff --git a/mods/content/beekeeping/icons/apiary_bees_etc.dmi b/mods/content/beekeeping/icons/apiary_bees_etc.dmi deleted file mode 100644 index 130120bb08f..00000000000 Binary files a/mods/content/beekeeping/icons/apiary_bees_etc.dmi and /dev/null differ diff --git a/mods/content/beekeeping/icons/bee_pack.dmi b/mods/content/beekeeping/icons/bee_pack.dmi new file mode 100644 index 00000000000..fe68dad8b4b Binary files /dev/null and b/mods/content/beekeeping/icons/bee_pack.dmi differ diff --git a/mods/content/beekeeping/icons/beehive.dmi b/mods/content/beekeeping/icons/beehive.dmi new file mode 100644 index 00000000000..9b58ec19c1b Binary files /dev/null and b/mods/content/beekeeping/icons/beehive.dmi differ diff --git a/mods/content/beekeeping/icons/beekeeping.dmi b/mods/content/beekeeping/icons/beekeeping.dmi deleted file mode 100644 index 569122d4a58..00000000000 Binary files a/mods/content/beekeeping/icons/beekeeping.dmi and /dev/null differ diff --git a/mods/content/beekeeping/icons/comb.dmi b/mods/content/beekeeping/icons/comb.dmi new file mode 100644 index 00000000000..edb611c93ae Binary files /dev/null and b/mods/content/beekeeping/icons/comb.dmi differ diff --git a/mods/content/beekeeping/icons/smoker.dmi b/mods/content/beekeeping/icons/smoker.dmi index 5109ca468e5..e6b14a8576c 100644 Binary files a/mods/content/beekeeping/icons/smoker.dmi and b/mods/content/beekeeping/icons/smoker.dmi differ diff --git a/mods/content/beekeeping/icons/swarm.dmi b/mods/content/beekeeping/icons/swarm.dmi new file mode 100644 index 00000000000..e55d936c1f1 Binary files /dev/null and b/mods/content/beekeeping/icons/swarm.dmi differ diff --git a/mods/content/beekeeping/items.dm b/mods/content/beekeeping/items.dm index 2c15fa75cd0..0c7774e6f1f 100644 --- a/mods/content/beekeeping/items.dm +++ b/mods/content/beekeeping/items.dm @@ -1,47 +1,34 @@ -/obj/item/beehive_assembly - name = "beehive assembly" - desc = "Contains everything you need to build a beehive." - icon = 'mods/content/beekeeping/icons/apiary_bees_etc.dmi' - icon_state = "apiary" - material = /decl/material/solid/organic/wood/oak - -/obj/item/beehive_assembly/attack_self(var/mob/user) - to_chat(user, "You start assembling \the [src]...") - if(do_after(user, 30, src)) - user.visible_message("\The [user] constructs a beehive.", "You construct a beehive.") - new /obj/machinery/beehive(get_turf(user)) - qdel(src) - -/obj/item/bee_smoker - name = "bee smoker" - desc = "A device used to calm down bees before harvesting honey." +/obj/item/smoker + name = "smoker" + desc = "A device used to calm insects down before harvesting from a hive." icon = 'mods/content/beekeeping/icons/smoker.dmi' icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_SMALL material = /decl/material/solid/metal/steel -/obj/item/bee_pack - name = "bee pack" - desc = "Contains a queen bee and some worker bees. Everything you'll need to start a hive!" - icon = 'mods/content/beekeeping/icons/beekeeping.dmi' - icon_state = "beepack" - material = /decl/material/solid/organic/plastic - var/full = 1 +// TODO: consume reagents or charges? Unnecessary complexity? +/obj/item/smoker/resolve_attackby(atom/A, mob/user, click_params) + + if(!user.check_dexterity(get_required_attack_dexterity(user, A))) + return TRUE + + var/smoked = FALSE + if(has_extension(A, /datum/extension/insect_hive)) + var/datum/extension/insect_hive/hive = get_extension(A, /datum/extension/insect_hive) + if(hive.smoked_by(user, A)) + smoked = TRUE -/obj/item/bee_pack/Initialize() - . = ..() - overlays += "beepack-full" + if(!smoked && isturf(A)) + for(var/obj/effect/insect_swarm/swarm in A) + swarm.was_smoked(smoke_time = 1 MINUTE) + smoked = TRUE -/obj/item/bee_pack/proc/empty() - full = 0 - name = "empty bee pack" - desc = "A stasis pack for moving bees. It's empty." - overlays.Cut() - overlays += "beepack-empty" + if(smoked) + var/turf/smoked_turf = get_turf(A) + if(smoked_turf) + playsound(smoked_turf, 'sound/effects/refill.ogg', 25, 1) + user.visible_message(SPAN_NOTICE("\The [user] douses \the [A] in smoke from \the [src].")) + new /obj/effect/effect/smoke(smoked_turf, 2 SECONDS) + return TRUE -/obj/item/bee_pack/proc/fill() - full = initial(full) - SetName(initial(name)) - desc = initial(desc) - overlays.Cut() - overlays += "beepack-full" + return ..() diff --git a/mods/content/beekeeping/materials.dm b/mods/content/beekeeping/materials.dm new file mode 100644 index 00000000000..d5fab8c75d2 --- /dev/null +++ b/mods/content/beekeeping/materials.dm @@ -0,0 +1,22 @@ +/decl/material/liquid/bee_venom + name = "bee venom" + uid = "liquid_venom_bee" + lore_text = "An irritant used by bees to drive off predators." + taste_description = "noxious bitterness" + color = "#d7d891" + heating_products = list( + /decl/material/liquid/denatured_toxin = 1 + ) + heating_point = 100 CELSIUS + heating_message = "becomes clear." + taste_mult = 1.2 + metabolism = REM * 0.25 + exoplanet_rarity_plant = MAT_RARITY_UNCOMMON + exoplanet_rarity_gas = MAT_RARITY_EXOTIC + var/pain_mult = 10 + var/pain_threshold = 100 + +/decl/material/liquid/bee_venom/affect_blood(mob/living/M, removed, datum/reagents/holder) + . = ..() + if(istype(M) && M.getHalLoss() < pain_threshold) + M.adjustHalLoss(max(1, ceil(removed * pain_mult))) diff --git a/mods/content/beekeeping/recipes.dm b/mods/content/beekeeping/recipes.dm index 8494617d347..3d851511382 100644 --- a/mods/content/beekeeping/recipes.dm +++ b/mods/content/beekeeping/recipes.dm @@ -1,6 +1,5 @@ -/decl/stack_recipe/planks/beehive_assembly - result_type = /obj/item/beehive_assembly - category = "furniture" +/decl/stack_recipe/planks/furniture/apiary + result_type = /obj/structure/apiary /decl/stack_recipe/planks/beehive_frame - result_type = /obj/item/hive_frame/crafted + result_type = /obj/item/hive_frame/crafted diff --git a/mods/content/beekeeping/trading.dm b/mods/content/beekeeping/trading.dm index 45009265f1b..5e8a8dc18b1 100644 --- a/mods/content/beekeeping/trading.dm +++ b/mods/content/beekeeping/trading.dm @@ -1,15 +1,15 @@ /datum/trader/trading_beacon/manufacturing/New() - LAZYSET(possible_trading_items, /obj/item/bee_pack, TRADER_THIS_TYPE) - LAZYSET(possible_trading_items, /obj/item/bee_smoker, TRADER_THIS_TYPE) - LAZYSET(possible_trading_items, /obj/item/beehive_assembly, TRADER_THIS_TYPE) - LAZYSET(possible_trading_items, /obj/item/hive_frame/crafted, TRADER_THIS_TYPE) + LAZYSET(possible_trading_items, /obj/item/bee_pack, TRADER_THIS_TYPE) + LAZYSET(possible_trading_items, /obj/item/smoker, TRADER_THIS_TYPE) + LAZYSET(possible_trading_items, /obj/item/hive_frame/crafted, TRADER_THIS_TYPE) + LAZYSET(possible_trading_items, /obj/item/stack/material/plank/mapped/wood/ten, TRADER_THIS_TYPE) ..() /decl/hierarchy/supply_pack/hydroponics/bee_keeper name = "Equipment - Beekeeping" contains = list( - /obj/item/beehive_assembly, - /obj/item/bee_smoker, + /obj/item/stack/material/plank/mapped/wood/ten, + /obj/item/smoker, /obj/item/hive_frame/crafted = 5, /obj/item/bee_pack ) diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria_areas.dm b/mods/content/corporate/away_sites/lar_maria/lar_maria_areas.dm index 65115dbaf27..7aa337a145e 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria_areas.dm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria_areas.dm @@ -1,4 +1,5 @@ /area/lar_maria + abstract_type = /area/lar_maria icon = 'mods/content/corporate/away_sites/lar_maria/lar_maria_sprites.dmi' /////////////////////////////Upper level areas diff --git a/mods/content/fantasy/submaps/_submaps.dm b/mods/content/fantasy/submaps/_submaps.dm index da60d375fd6..b92c691ab14 100644 --- a/mods/content/fantasy/submaps/_submaps.dm +++ b/mods/content/fantasy/submaps/_submaps.dm @@ -47,6 +47,7 @@ area_flags = AREA_FLAG_EXTERNAL | AREA_FLAG_IS_BACKGROUND /area/fantasy/outside/point_of_interest + abstract_type = /area/fantasy/outside/point_of_interest name = "Point Of Interest" description = null area_blurb_category = /area/fantasy/outside/point_of_interest diff --git a/mods/content/fishing/_fishing.dm b/mods/content/fishing/_fishing.dm new file mode 100644 index 00000000000..f2f9bc00d27 --- /dev/null +++ b/mods/content/fishing/_fishing.dm @@ -0,0 +1,2 @@ +/decl/modpack/fishing + name = "Fishing Modpack" \ No newline at end of file diff --git a/mods/content/fishing/_fishing.dme b/mods/content/fishing/_fishing.dme new file mode 100644 index 00000000000..c68d52fd9e9 --- /dev/null +++ b/mods/content/fishing/_fishing.dme @@ -0,0 +1,13 @@ +#ifndef CONTENT_PACK_FISHING +#define CONTENT_PACK_FISHING +// BEGIN_INCLUDE +#include "_fishing.dm" +#include "area_fishing.dm" +#include "fishing_bait.dm" +#include "fishing_designs.dm" +#include "fishing_line.dm" +#include "fishing_recipes.dm" +#include "fishing_rod.dm" +#include "turf_fishing.dm" +// END_INCLUDE +#endif diff --git a/mods/content/fishing/area_fishing.dm b/mods/content/fishing/area_fishing.dm new file mode 100644 index 00000000000..5e046d58fbc --- /dev/null +++ b/mods/content/fishing/area_fishing.dm @@ -0,0 +1,63 @@ +/area + var/fishing_failure_prob = 95 + // Hardcoding the contents of /obj/random/junk to avoid hacks for getting results from /obj/random. + var/list/fishing_results = list( + /obj/item/remains/mouse = 1, + /obj/item/remains/robot = 1, + /obj/item/paper/crumpled = 1, + /obj/item/inflatable/torn = 1, + /obj/item/shard = 1, + /obj/item/hand/missing_card = 1 + ) + +/area/Initialize() + var/list/additional_fishing_results = get_additional_fishing_results() + if(LAZYLEN(additional_fishing_results)) + LAZYINITLIST(fishing_results) + for(var/fish in additional_fishing_results) + fishing_results[fish] = additional_fishing_results[fish] + . = ..() + +/area/proc/get_additional_fishing_results() + return + +/area/proc/get_fishing_result(turf/origin, obj/item/food/bait) + if(!length(fishing_results) || prob(fishing_failure_prob)) + return null + return pickweight(fishing_results) + +// overrides down here + +// Let's make a token effort at making the fish somewhat alien I guess. +/area/exoplanet/get_fishing_result(turf/origin, obj/item/food/bait) + . = ..() + if(ismob(.)) + var/mob/M = . + M.SetName("xeno-[M.name]") + M.set_color(get_random_colour(simple = TRUE)) + +//Fishing results for the grass exoplanet surface +/area/exoplanet/grass + fishing_failure_prob = 10 + // TODO: waterweed? + // Hardcoding the contents of /obj/random/natural_debris to avoid hacks to get results out of /obj/random. + fishing_results = list( + /mob/living/simple_animal/aquatic/fish = 10, + /mob/living/simple_animal/aquatic/fish/grump = 10, + /obj/item/mollusc = 5, + /obj/item/mollusc/barnacle/fished = 5, + /mob/living/simple_animal/aquatic/fish/large = 5, + /mob/living/simple_animal/aquatic/fish/large/bass = 5, + /mob/living/simple_animal/aquatic/fish/large/salmon = 5, + /mob/living/simple_animal/aquatic/fish/large/trout = 5, + /mob/living/simple_animal/aquatic/fish/large/pike = 3, + /mob/living/simple_animal/aquatic/fish/large/javelin = 3, + /obj/item/mollusc/clam/fished/pearl = 3, + /obj/item/trash/mollusc_shell/clam = 2, + /obj/item/trash/mollusc_shell/barnacle = 2, + /obj/item/remains/mouse = 2, + /obj/item/remains/lizard = 2, + /obj/item/stick = 1, + /obj/item/trash/mollusc_shell = 1, + /mob/living/simple_animal/aquatic/fish/large/koi = 1 + ) \ No newline at end of file diff --git a/mods/content/fishing/fishing_bait.dm b/mods/content/fishing/fishing_bait.dm new file mode 100644 index 00000000000..a0f3db926f6 --- /dev/null +++ b/mods/content/fishing/fishing_bait.dm @@ -0,0 +1,37 @@ +// Returns a value used as a multiplier in the fishing delay calc. Higher represents a stronger reduction in fishing time. +#define BAIT_VALUE_CONSTANT 0.1 +/obj/item/proc/get_bait_value() + . = 0 + for(var/mat in matter) + var/decl/material/bait_mat = GET_DECL(mat) + if(bait_mat.fishing_bait_value) + . += MATERIAL_UNITS_TO_REAGENTS_UNITS(matter[mat]) * bait_mat.fishing_bait_value * BAIT_VALUE_CONSTANT + for(var/decl/material/reagent as anything in REAGENT_VOLUMES(reagents)) + if(reagent.fishing_bait_value) + . += REAGENT_VOLUME(reagents, reagent) * reagent.fishing_bait_value * BAIT_VALUE_CONSTANT +#undef BAIT_VALUE_CONSTANT + +/decl/material + /// A multiplier for this material when used in fishing bait. + var/fishing_bait_value = 0 + +/decl/material/solid/organic/meat + fishing_bait_value = 1 + +/decl/material/solid/organic/plantmatter + fishing_bait_value = 0.75 + +/decl/material/liquid/nutriment + fishing_bait_value = 0.65 + +/decl/material/liquid/oil + fishing_bait_value = 0 + +/decl/material/solid/organic/skin + fishing_bait_value = 0.75 + +/decl/material/solid/organic/skin/feathers + fishing_bait_value = 0 + +/decl/material/solid/organic/skin/fur + fishing_bait_value = 0 \ No newline at end of file diff --git a/mods/content/fishing/fishing_designs.dm b/mods/content/fishing/fishing_designs.dm new file mode 100644 index 00000000000..9d1a864cbcc --- /dev/null +++ b/mods/content/fishing/fishing_designs.dm @@ -0,0 +1,5 @@ +/datum/fabricator_recipe/fishing_line + path = /obj/item/fishing_line + +/datum/fabricator_recipe/fishing_line_high_quality + path = /obj/item/fishing_line/high_quality \ No newline at end of file diff --git a/code/modules/fishing/fishing_line.dm b/mods/content/fishing/fishing_line.dm similarity index 93% rename from code/modules/fishing/fishing_line.dm rename to mods/content/fishing/fishing_line.dm index 28afa6aa1a2..7455c8af6b3 100644 --- a/code/modules/fishing/fishing_line.dm +++ b/mods/content/fishing/fishing_line.dm @@ -1,6 +1,6 @@ /obj/item/fishing_line name = "fishing line" - icon = 'icons/obj/fishing_line.dmi' + icon = 'mods/content/fishing/icons/fishing_line.dmi' icon_state = ICON_STATE_WORLD material_alteration = MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_DESC max_health = 100 diff --git a/mods/content/fishing/fishing_recipes.dm b/mods/content/fishing/fishing_recipes.dm new file mode 100644 index 00000000000..405773b3426 --- /dev/null +++ b/mods/content/fishing/fishing_recipes.dm @@ -0,0 +1,2 @@ +/decl/stack_recipe/planks/fishing_rod + result_type = /obj/item/fishing_rod \ No newline at end of file diff --git a/code/modules/fishing/fishing_rod.dm b/mods/content/fishing/fishing_rod.dm similarity index 98% rename from code/modules/fishing/fishing_rod.dm rename to mods/content/fishing/fishing_rod.dm index ae8342c0f67..eabc2f57cb0 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/mods/content/fishing/fishing_rod.dm @@ -9,7 +9,7 @@ color = /decl/material/solid/organic/wood/oak::color matter = null material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME | MAT_FLAG_ALTERATION_DESC - icon = 'icons/obj/fishing_rod.dmi' + icon = 'mods/content/fishing/icons/fishing_rod.dmi' icon_state = ICON_STATE_WORLD w_class = ITEM_SIZE_LARGE @@ -346,7 +346,7 @@ matter = list( /decl/material/solid/metal/steel = MATTER_AMOUNT_REINFORCEMENT ) - icon = 'icons/obj/fishing_rod_advanced.dmi' + icon = 'mods/content/fishing/icons/fishing_rod_advanced.dmi' material_alteration = MAT_FLAG_ALTERATION_COLOR fishing_rod_quality = 0.2 line = /obj/item/fishing_line/high_quality diff --git a/mods/content/fishing/icons/fishing_line.dmi b/mods/content/fishing/icons/fishing_line.dmi new file mode 100644 index 00000000000..4824616fa1a Binary files /dev/null and b/mods/content/fishing/icons/fishing_line.dmi differ diff --git a/icons/obj/fishing_rod.dmi b/mods/content/fishing/icons/fishing_rod.dmi similarity index 100% rename from icons/obj/fishing_rod.dmi rename to mods/content/fishing/icons/fishing_rod.dmi diff --git a/icons/obj/fishing_rod_advanced.dmi b/mods/content/fishing/icons/fishing_rod_advanced.dmi similarity index 100% rename from icons/obj/fishing_rod_advanced.dmi rename to mods/content/fishing/icons/fishing_rod_advanced.dmi diff --git a/mods/content/fishing/turf_fishing.dm b/mods/content/fishing/turf_fishing.dm new file mode 100644 index 00000000000..cc796cdcb3c --- /dev/null +++ b/mods/content/fishing/turf_fishing.dm @@ -0,0 +1,3 @@ +/turf/proc/get_fishing_result(obj/item/food/bait) + var/area/A = get_area(src) + return A.get_fishing_result(src, bait) \ No newline at end of file diff --git a/mods/content/government/away_sites/icarus/icarus_areas.dm b/mods/content/government/away_sites/icarus/icarus_areas.dm index 6ebe40524fb..000d9a7c529 100644 --- a/mods/content/government/away_sites/icarus/icarus_areas.dm +++ b/mods/content/government/away_sites/icarus/icarus_areas.dm @@ -1,4 +1,5 @@ /area/icarus + abstract_type = /area/icarus icon = 'mods/content/government/away_sites/icarus/icarus_sprites.dmi' /area/icarus/vessel diff --git a/mods/content/holodeck/_holodeck.dm b/mods/content/holodeck/_holodeck.dm new file mode 100644 index 00000000000..491e56bf744 --- /dev/null +++ b/mods/content/holodeck/_holodeck.dm @@ -0,0 +1,3 @@ +/decl/modpack/holodeck + name = "Holodecks and Hardlight Holograms" + desc = "Adds holodecks and support for hardlight hologram objects." \ No newline at end of file diff --git a/mods/content/holodeck/_holodeck.dme b/mods/content/holodeck/_holodeck.dme new file mode 100644 index 00000000000..80d4c5be2cd --- /dev/null +++ b/mods/content/holodeck/_holodeck.dme @@ -0,0 +1,18 @@ +#ifndef CONTENT_PACK_HOLODECK +#define CONTENT_PACK_HOLODECK +// BEGIN_INCLUDE +#include "_holodeck.dm" +#include "holo_items.dm" +#include "holo_mobs.dm" +#include "holo_objects.dm" +#include "holo_racks.dm" +#include "holo_tables.dm" +#include "holo_turfs.dm" +#include "holodeck_control_circuit.dm" +#include "holodeck_control_console.dm" +#include "holodeck_designs.dm" +#include "holodeck_programs.dm" +#include "maps_holodeck.dm" +#include "trader_overrides.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/mods/content/holodeck/holo_items.dm b/mods/content/holodeck/holo_items.dm new file mode 100644 index 00000000000..b62da36bd4f --- /dev/null +++ b/mods/content/holodeck/holo_items.dm @@ -0,0 +1,8 @@ +/obj/machinery/destructive_analyzer/can_deconstruct(var/obj/item/used_item) + if(used_item.holographic) + return FALSE + +/obj/item/grenade/spawnergrenade/fake_carp + origin_tech = @'{"materials":2,"magnets":2,"wormholes":5}' + spawner_type = /mob/living/simple_animal/hostile/carp/holodeck/fake + deliveryamt = 4 \ No newline at end of file diff --git a/mods/content/holodeck/holo_mobs.dm b/mods/content/holodeck/holo_mobs.dm new file mode 100644 index 00000000000..a9e914b20f6 --- /dev/null +++ b/mods/content/holodeck/holo_mobs.dm @@ -0,0 +1,61 @@ +//Holocarp + +/mob/living/simple_animal/hostile/carp/holodeck + icon = 'icons/mob/simple_animal/holocarp.dmi' + alpha = 127 + butchery_data = null + worthless = TRUE + +/mob/living/simple_animal/hostile/carp/holodeck/carp_randomify() + return + +/mob/living/simple_animal/hostile/carp/holodeck/on_update_icon() + SHOULD_CALL_PARENT(FALSE) + return + +/mob/living/simple_animal/hostile/carp/holodeck/Initialize() + . = ..() + set_light(2) //hologram lighting + +/mob/living/simple_animal/hostile/carp/holodeck/proc/set_safety(var/safe) + if (safe) + faction = MOB_FACTION_NEUTRAL + natural_weapon.set_base_attack_force(0) + environment_smash = 0 + ai?.try_destroy_surroundings = FALSE + else + faction = "carp" + natural_weapon.set_base_attack_force(natural_weapon.get_initial_base_attack_force()) + +/mob/living/simple_animal/hostile/carp/holodeck/gib(do_gibs = TRUE) + SHOULD_CALL_PARENT(FALSE) + if(stat != DEAD) + death(gibbed = TRUE) + if(stat == DEAD) + qdel(src) + return TRUE + return FALSE + +/mob/living/simple_animal/hostile/carp/holodeck/get_death_message(gibbed) + return "fades away..." + +/mob/living/simple_animal/hostile/carp/holodeck/get_self_death_message(gibbed) + return "You have been destroyed." + +/mob/living/simple_animal/hostile/carp/holodeck/death(gibbed) + . = ..() + if(. && !gibbed) + gib() + +// Non-dangerous holocarp +/mob/living/simple_animal/hostile/carp/holodeck/fake + faction = null + natural_weapon = /obj/item/natural_weapon/bite/fake + environment_smash = 0 + ai = /datum/mob_controller/aggressive/carp/fake + +/obj/item/natural_weapon/bite/fake + _base_attack_force = 0 + +/datum/mob_controller/aggressive/carp/fake + try_destroy_surroundings = FALSE \ No newline at end of file diff --git a/code/modules/holodeck/HolodeckObjects.dm b/mods/content/holodeck/holo_objects.dm similarity index 57% rename from code/modules/holodeck/HolodeckObjects.dm rename to mods/content/holodeck/holo_objects.dm index 407b1b196da..08f617da4fb 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/mods/content/holodeck/holo_objects.dm @@ -3,130 +3,28 @@ // Holographic tables are in code/modules/tables/presets.dm // Holographic racks are in code/modules/tables/rack.dm -/turf/floor/holofloor - thermal_conductivity = 0 - -/turf/floor/holofloor/get_lumcount(var/minlum = 0, var/maxlum = 1) - return 0.8 - -/turf/floor/holofloor/attackby(obj/item/used_item, mob/user) - return TRUE - // HOLOFLOOR DOES NOT GIVE A FUCK - -/turf/floor/holofloor/carpet - name = "brown carpet" - icon = 'icons/turf/flooring/carpet.dmi' - icon_state = "brown" - _flooring = /decl/flooring/carpet - -/turf/floor/holofloor/concrete - name = "brown carpet" - icon = 'icons/turf/flooring/carpet.dmi' - icon_state = "brown" - _flooring = /decl/flooring/carpet - -/turf/floor/holofloor/concrete - name = "floor" - icon = 'icons/turf/flooring/misc.dmi' - icon_state = "concrete" - _flooring = null - -/turf/floor/holofloor/tiled - name = "floor" - icon = 'icons/turf/flooring/tiles.dmi' - icon_state = "steel" - _flooring = /decl/flooring/tiling - -/turf/floor/holofloor/tiled/dark - name = "dark floor" - icon_state = "dark" - _flooring = /decl/flooring/tiling/dark - -/turf/floor/holofloor/tiled/stone - name = "stone floor" - icon_state = "stone" - _flooring = /decl/flooring/tiling/stone - -/turf/floor/holofloor/lino - name = "lino" - icon = 'icons/turf/flooring/linoleum.dmi' - icon_state = "lino" - _flooring = /decl/flooring/linoleum - -/turf/floor/holofloor/wood - name = "wooden floor" - icon = 'icons/turf/flooring/wood.dmi' - icon_state = "wood0" - color = WOOD_COLOR_CHOCOLATE - _flooring = /decl/flooring/wood - -/turf/floor/holofloor/grass - name = "lush grass" - icon = 'icons/turf/flooring/fakegrass.dmi' - icon_state = "grass0" - _flooring = /decl/flooring/grass/fake - -/turf/floor/holofloor/snow - name = "snow" - icon = 'icons/turf/flooring/snow.dmi' - icon_state = "snow0" - _flooring = /decl/flooring/snow/fake - -/turf/floor/holofloor/space - name = "\proper space" - icon = 'icons/turf/flooring/fake_space.dmi' - icon_state = "space0" - _flooring = /decl/flooring/fake_space - -/turf/floor/holofloor/reinforced - name = "reinforced holofloor" - icon = 'icons/turf/flooring/tiles.dmi' - _flooring = /decl/flooring/reinforced - icon_state = "reinforced" - -/turf/floor/holofloor/beach - desc = "Uncomfortably gritty for a hologram." - icon = 'icons/misc/beach.dmi' - _flooring = /decl/flooring/sand/fake - abstract_type = /turf/floor/holofloor/beach - -/turf/floor/holofloor/beach/sand - name = "sand" - icon_state = "desert0" - -/turf/floor/holofloor/beach/coastline - name = "coastline" - icon = 'icons/misc/beach2.dmi' - icon_state = "sandwater" - _flooring = /decl/flooring/sand/fake - -/turf/floor/holofloor/beach/water - name = "water" - icon_state = "seashallow" - _flooring = /decl/flooring/fake_water - -/turf/floor/holofloor/desert - name = "desert sand" - desc = "Uncomfortably gritty for a hologram." - icon = 'icons/turf/flooring/barren.dmi' - icon_state = "barren" - _flooring = /decl/flooring/sand/fake - -/turf/floor/holofloor/desert/Initialize(var/ml) - . = ..() - if(prob(10)) - LAZYADD(decals, image('icons/turf/flooring/decals.dmi', "asteroid[rand(0,9)]")) +/obj + /// if the obj is a holographic object spawned by the holodeck + var/holographic = FALSE /obj/structure/holostool name = "stool" desc = "Apply butt." - icon = 'icons/obj/furniture.dmi' + icon = 'icons/obj/stool.dmi' icon_state = "stool_padded_preview" anchored = TRUE + worthless = TRUE + holographic = TRUE /obj/item/clothing/gloves/boxing/hologlove name = "boxing gloves" desc = "Because you really needed another excuse to punch your crewmates." + worthless = TRUE + holographic = TRUE + +/obj/structure/window/reinforced/holowindow + worthless = TRUE + holographic = TRUE /obj/structure/window/reinforced/holowindow/full dir = NORTHEAST @@ -148,27 +46,31 @@ // This subtype is deleted when a ready button in the same area is pressed. /obj/structure/window/reinforced/holowindow/disappearing +/obj/machinery/door/window/holowindoor + holographic = TRUE + worthless = TRUE + /obj/machinery/door/window/holowindoor/attackby(obj/item/used_item, mob/user) - if (src.operating == 1) + if (operating) return TRUE - if(src.density && istype(used_item, /obj/item) && !istype(used_item, /obj/item/card)) - playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) + if(density && istype(used_item, /obj/item) && !istype(used_item, /obj/item/card)) + playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1) visible_message("\The [src] was hit by \the [used_item].") if(used_item.atom_damage_type == BRUTE || used_item.atom_damage_type == BURN) take_damage(used_item.expend_attack_force(user)) return TRUE - src.add_fingerprint(user) - if (src.allowed(user)) - if (src.density) + add_fingerprint(user) + if (allowed(user)) + if (density) open() else close() return TRUE - else if (src.density) + else if (density) flick("[base_state]deny", src) return TRUE return FALSE @@ -184,14 +86,18 @@ /obj/structure/bed/holobed tool_interaction_flags = 0 holographic = TRUE + worthless = TRUE material = /decl/material/solid/metal/aluminium/holographic /obj/structure/chair/holochair tool_interaction_flags = 0 holographic = TRUE + worthless = TRUE material = /decl/material/solid/metal/aluminium/holographic /obj/item/holo + holographic = TRUE + worthless = TRUE atom_damage_type = PAIN no_attack_log = 1 max_health = ITEM_HEALTH_NO_DAMAGE @@ -258,6 +164,8 @@ anchored = TRUE density = TRUE throwpass = 1 + holographic = TRUE + worthless = TRUE /obj/structure/holohoop/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) if (istype(mover,/obj/item) && mover.throwing) @@ -284,6 +192,8 @@ layer = TABLE_LAYER throwpass = 1 dir = EAST + holographic = TRUE + worthless = TRUE /obj/structure/holonet/end icon_state = "volleynet_end" @@ -315,6 +225,8 @@ idle_power_usage = 2 active_power_usage = 6 power_channel = ENVIRON + holographic = TRUE + worthless = TRUE /obj/machinery/readybutton/attack_ai(mob/living/silicon/ai/user) to_chat(user, "The AI is not to interact with these devices!") @@ -364,51 +276,3 @@ for(var/mob/M in currentarea) to_chat(M, "FIGHT!") - -//Holocarp - -/mob/living/simple_animal/hostile/carp/holodeck - icon = 'icons/mob/simple_animal/holocarp.dmi' - alpha = 127 - butchery_data = null - -/mob/living/simple_animal/hostile/carp/holodeck/carp_randomify() - return - -/mob/living/simple_animal/hostile/carp/holodeck/on_update_icon() - SHOULD_CALL_PARENT(FALSE) - return - -/mob/living/simple_animal/hostile/carp/holodeck/Initialize() - . = ..() - set_light(2) //hologram lighting - -/mob/living/simple_animal/hostile/carp/holodeck/proc/set_safety(var/safe) - if (safe) - faction = MOB_FACTION_NEUTRAL - natural_weapon.set_base_attack_force(0) - environment_smash = 0 - ai?.try_destroy_surroundings = FALSE - else - faction = "carp" - natural_weapon.set_base_attack_force(natural_weapon.get_initial_base_attack_force()) - -/mob/living/simple_animal/hostile/carp/holodeck/gib(do_gibs = TRUE) - SHOULD_CALL_PARENT(FALSE) - if(stat != DEAD) - death(gibbed = TRUE) - if(stat == DEAD) - qdel(src) - return TRUE - return FALSE - -/mob/living/simple_animal/hostile/carp/holodeck/get_death_message(gibbed) - return "fades away..." - -/mob/living/simple_animal/hostile/carp/holodeck/get_self_death_message(gibbed) - return "You have been destroyed." - -/mob/living/simple_animal/hostile/carp/holodeck/death(gibbed) - . = ..() - if(. && !gibbed) - gib() diff --git a/mods/content/holodeck/holo_racks.dm b/mods/content/holodeck/holo_racks.dm new file mode 100644 index 00000000000..9745f215b88 --- /dev/null +++ b/mods/content/holodeck/holo_racks.dm @@ -0,0 +1,12 @@ +/obj/structure/rack/holorack + holographic = TRUE + worthless = TRUE + color = COLOR_OFF_WHITE + material = /decl/material/solid/metal/aluminium/holographic + reinf_material = /decl/material/solid/metal/aluminium/holographic + +/obj/structure/rack/holorack/dismantle_structure(mob/user) + material = null + reinf_material = null + parts_type = null + . = ..() \ No newline at end of file diff --git a/mods/content/holodeck/holo_tables.dm b/mods/content/holodeck/holo_tables.dm new file mode 100644 index 00000000000..fe1e52be43c --- /dev/null +++ b/mods/content/holodeck/holo_tables.dm @@ -0,0 +1,22 @@ +/obj/structure/table/holotable + icon_state = "holo_preview" + holographic = TRUE + worthless = TRUE + color = COLOR_OFF_WHITE + material = /decl/material/solid/metal/aluminium/holographic + reinf_material = /decl/material/solid/metal/aluminium/holographic + +/obj/structure/table/holo_plastictable + icon_state = "holo_preview" + holographic = TRUE + worthless = TRUE + color = COLOR_OFF_WHITE + material = /decl/material/solid/organic/plastic/holographic + reinf_material = /decl/material/solid/organic/plastic/holographic + +/obj/structure/table/holo_woodentable + holographic = TRUE + worthless = TRUE + icon_state = "holo_preview" + material = /decl/material/solid/organic/wood/holographic + reinf_material = /decl/material/solid/organic/wood/holographic \ No newline at end of file diff --git a/mods/content/holodeck/holo_turfs.dm b/mods/content/holodeck/holo_turfs.dm new file mode 100644 index 00000000000..44b28b5fc96 --- /dev/null +++ b/mods/content/holodeck/holo_turfs.dm @@ -0,0 +1,114 @@ +/turf/floor/holofloor + abstract_type = /turf/floor/holofloor + thermal_conductivity = 0 + +/turf/floor/holofloor/get_lumcount(var/minlum = 0, var/maxlum = 1) + return 0.8 + +/turf/floor/holofloor/attackby(obj/item/used_item, mob/user) + return TRUE + // HOLOFLOOR DOES NOT GIVE A FUCK + +/turf/floor/holofloor/carpet + name = "brown carpet" + icon = 'icons/turf/flooring/carpet.dmi' + icon_state = "brown" + _flooring = /decl/flooring/carpet + +/turf/floor/holofloor/concrete + name = "brown carpet" + icon = 'icons/turf/flooring/carpet.dmi' + icon_state = "brown" + _flooring = /decl/flooring/carpet + +/turf/floor/holofloor/concrete + name = "floor" + icon = 'icons/turf/flooring/misc.dmi' + icon_state = "concrete" + _flooring = null + +/turf/floor/holofloor/tiled + name = "floor" + icon = 'icons/turf/flooring/tiles.dmi' + icon_state = "steel" + _flooring = /decl/flooring/tiling + +/turf/floor/holofloor/tiled/dark + name = "dark floor" + icon_state = "dark" + _flooring = /decl/flooring/tiling/dark + +/turf/floor/holofloor/tiled/stone + name = "stone floor" + icon_state = "stone" + _flooring = /decl/flooring/tiling/stone + +/turf/floor/holofloor/lino + name = "lino" + icon = 'icons/turf/flooring/linoleum.dmi' + icon_state = "lino" + _flooring = /decl/flooring/linoleum + +/turf/floor/holofloor/wood + name = "wooden floor" + icon = 'icons/turf/flooring/wood.dmi' + icon_state = "wood0" + color = WOOD_COLOR_CHOCOLATE + _flooring = /decl/flooring/wood + +/turf/floor/holofloor/grass + name = "lush grass" + icon = 'icons/turf/flooring/fakegrass.dmi' + icon_state = "grass0" + _flooring = /decl/flooring/grass/fake + +/turf/floor/holofloor/snow + name = "snow" + icon = 'icons/turf/flooring/snow.dmi' + icon_state = "snow0" + _flooring = /decl/flooring/snow/fake + +/turf/floor/holofloor/space + name = "\proper space" + icon = 'icons/turf/flooring/fake_space.dmi' + icon_state = "space0" + _flooring = /decl/flooring/fake_space + +/turf/floor/holofloor/reinforced + name = "reinforced holofloor" + icon = 'icons/turf/flooring/tiles.dmi' + _flooring = /decl/flooring/reinforced + icon_state = "reinforced" + +/turf/floor/holofloor/beach + desc = "Uncomfortably gritty for a hologram." + icon = 'icons/misc/beach.dmi' + _flooring = /decl/flooring/sand/fake + abstract_type = /turf/floor/holofloor/beach + +/turf/floor/holofloor/beach/sand + name = "sand" + icon_state = "desert0" + +/turf/floor/holofloor/beach/coastline + name = "coastline" + icon = 'icons/misc/beach2.dmi' + icon_state = "sandwater" + _flooring = /decl/flooring/sand/fake + +/turf/floor/holofloor/beach/water + name = "water" + icon_state = "seashallow" + _flooring = /decl/flooring/fake_water + +/turf/floor/holofloor/desert + name = "desert sand" + desc = "Uncomfortably gritty for a hologram." + icon = 'icons/turf/flooring/barren.dmi' + icon_state = "barren" + _flooring = /decl/flooring/sand/fake + +/turf/floor/holofloor/desert/Initialize(var/ml) + . = ..() + if(prob(10)) + LAZYADD(decals, image('icons/turf/flooring/decals.dmi', "asteroid[rand(0,9)]")) \ No newline at end of file diff --git a/code/game/objects/items/circuitboards/computer/holodeckcontrol.dm b/mods/content/holodeck/holodeck_control_circuit.dm similarity index 100% rename from code/game/objects/items/circuitboards/computer/holodeckcontrol.dm rename to mods/content/holodeck/holodeck_control_circuit.dm diff --git a/code/modules/holodeck/HolodeckControl.dm b/mods/content/holodeck/holodeck_control_console.dm similarity index 99% rename from code/modules/holodeck/HolodeckControl.dm rename to mods/content/holodeck/holodeck_control_console.dm index f5db252b01d..80b2597f9c0 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/mods/content/holodeck/holodeck_control_console.dm @@ -276,6 +276,7 @@ for(var/obj/holo_obj in holographic_objs) holo_obj.alpha *= 0.8 //give holodeck objs a slight transparency holo_obj.holographic = TRUE + holo_obj.worthless = TRUE if(HP.ambience) linkedholodeck.forced_ambience = HP.ambience diff --git a/mods/content/holodeck/holodeck_designs.dm b/mods/content/holodeck/holodeck_designs.dm new file mode 100644 index 00000000000..2160366208d --- /dev/null +++ b/mods/content/holodeck/holodeck_designs.dm @@ -0,0 +1,2 @@ +/datum/fabricator_recipe/imprinter/circuit/holo + path = /obj/item/stock_parts/circuitboard/holodeck_control \ No newline at end of file diff --git a/code/modules/holodeck/HolodeckPrograms.dm b/mods/content/holodeck/holodeck_programs.dm similarity index 100% rename from code/modules/holodeck/HolodeckPrograms.dm rename to mods/content/holodeck/holodeck_programs.dm diff --git a/mods/content/holodeck/maps_holodeck.dm b/mods/content/holodeck/maps_holodeck.dm new file mode 100644 index 00000000000..427fd2af9ba --- /dev/null +++ b/mods/content/holodeck/maps_holodeck.dm @@ -0,0 +1,9 @@ +/datum/map + var/list/holodeck_programs = list() // map of string ids to /datum/holodeck_program instances + var/list/holodeck_supported_programs = list() // map of maps - first level maps from list-of-programs string id (e.g. "BarPrograms") to another map + // this is in order to support multiple holodeck program listings for different holodecks + // second level maps from program friendly display names ("Picnic Area") to program string ids ("picnicarea") + // as defined in holodeck_programs + var/list/holodeck_restricted_programs = list() // as above... but EVIL! + var/list/holodeck_default_program = list() // map of program list string ids to default program string id + var/list/holodeck_off_program = list() // as above... but for being off i guess \ No newline at end of file diff --git a/mods/content/holodeck/trader_overrides.dm b/mods/content/holodeck/trader_overrides.dm new file mode 100644 index 00000000000..f3f957c0819 --- /dev/null +++ b/mods/content/holodeck/trader_overrides.dm @@ -0,0 +1,3 @@ +/datum/trader/ship/prank_shop/New() + LAZYSET(possible_trading_items, /obj/item/grenade/spawnergrenade/fake_carp, TRADER_THIS_TYPE) + ..() \ No newline at end of file diff --git a/mods/content/sealant_gun/sealant_gun.dm b/mods/content/sealant_gun/sealant_gun.dm index 23a753cc010..79e7a251d4f 100644 --- a/mods/content/sealant_gun/sealant_gun.dm +++ b/mods/content/sealant_gun/sealant_gun.dm @@ -4,6 +4,7 @@ icon = 'mods/content/sealant_gun/icons/sealant_gun.dmi' icon_state = ICON_STATE_WORLD autofire_enabled = TRUE + autofire_delay = 0.5 SECONDS has_safety = FALSE waterproof = TRUE w_class = ITEM_SIZE_GARGANTUAN diff --git a/mods/content/turbolift/_turbolift.dm b/mods/content/turbolift/_turbolift.dm new file mode 100644 index 00000000000..794dac19865 --- /dev/null +++ b/mods/content/turbolift/_turbolift.dm @@ -0,0 +1,3 @@ +/decl/modpack/turbolift + name = "Turbolifts" + desc = "Adds elevators and supporting code." \ No newline at end of file diff --git a/mods/content/turbolift/_turbolift.dme b/mods/content/turbolift/_turbolift.dme new file mode 100644 index 00000000000..27c46ca72c8 --- /dev/null +++ b/mods/content/turbolift/_turbolift.dme @@ -0,0 +1,14 @@ +#ifndef MODPACK_TURBOLIFT +#define MODPACK_TURBOLIFT +// BEGIN_INCLUDE +#include "_turbolift.dm" +#include "turbolift.dm" +#include "turbolift_areas.dm" +#include "turbolift_console.dm" +#include "turbolift_door.dm" +#include "turbolift_floor.dm" +#include "turbolift_init.dm" +#include "turbolift_map.dm" +#include "turbolift_turfs.dm" +// END_INCLUDE +#endif \ No newline at end of file diff --git a/icons/obj/doors/elevator/door.dmi b/mods/content/turbolift/icons/door/door.dmi similarity index 100% rename from icons/obj/doors/elevator/door.dmi rename to mods/content/turbolift/icons/door/door.dmi diff --git a/icons/obj/doors/elevator/fill_glass.dmi b/mods/content/turbolift/icons/door/fill_glass.dmi similarity index 100% rename from icons/obj/doors/elevator/fill_glass.dmi rename to mods/content/turbolift/icons/door/fill_glass.dmi diff --git a/icons/obj/doors/elevator/fill_steel.dmi b/mods/content/turbolift/icons/door/fill_steel.dmi similarity index 100% rename from icons/obj/doors/elevator/fill_steel.dmi rename to mods/content/turbolift/icons/door/fill_steel.dmi diff --git a/icons/obj/doors/elevator/lights_bolts.dmi b/mods/content/turbolift/icons/door/lights_bolts.dmi similarity index 100% rename from icons/obj/doors/elevator/lights_bolts.dmi rename to mods/content/turbolift/icons/door/lights_bolts.dmi diff --git a/icons/obj/doors/elevator/lights_deny.dmi b/mods/content/turbolift/icons/door/lights_deny.dmi similarity index 100% rename from icons/obj/doors/elevator/lights_deny.dmi rename to mods/content/turbolift/icons/door/lights_deny.dmi diff --git a/icons/obj/doors/elevator/lights_green.dmi b/mods/content/turbolift/icons/door/lights_green.dmi similarity index 100% rename from icons/obj/doors/elevator/lights_green.dmi rename to mods/content/turbolift/icons/door/lights_green.dmi diff --git a/icons/obj/turbolift.dmi b/mods/content/turbolift/icons/turbolift.dmi similarity index 100% rename from icons/obj/turbolift.dmi rename to mods/content/turbolift/icons/turbolift.dmi diff --git a/icons/obj/turbolift_preview_3x3.dmi b/mods/content/turbolift/icons/turbolift_preview_3x3.dmi similarity index 100% rename from icons/obj/turbolift_preview_3x3.dmi rename to mods/content/turbolift/icons/turbolift_preview_3x3.dmi diff --git a/icons/obj/turbolift_preview_5x5.dmi b/mods/content/turbolift/icons/turbolift_preview_5x5.dmi similarity index 100% rename from icons/obj/turbolift_preview_5x5.dmi rename to mods/content/turbolift/icons/turbolift_preview_5x5.dmi diff --git a/icons/obj/turbolift_preview_nowalls_3x3.dmi b/mods/content/turbolift/icons/turbolift_preview_nowalls_3x3.dmi similarity index 100% rename from icons/obj/turbolift_preview_nowalls_3x3.dmi rename to mods/content/turbolift/icons/turbolift_preview_nowalls_3x3.dmi diff --git a/icons/obj/turbolift_preview_nowalls_4x4.dmi b/mods/content/turbolift/icons/turbolift_preview_nowalls_4x4.dmi similarity index 100% rename from icons/obj/turbolift_preview_nowalls_4x4.dmi rename to mods/content/turbolift/icons/turbolift_preview_nowalls_4x4.dmi diff --git a/code/modules/turbolift/turbolift.dm b/mods/content/turbolift/turbolift.dm similarity index 100% rename from code/modules/turbolift/turbolift.dm rename to mods/content/turbolift/turbolift.dm diff --git a/code/modules/turbolift/turbolift_areas.dm b/mods/content/turbolift/turbolift_areas.dm similarity index 91% rename from code/modules/turbolift/turbolift_areas.dm rename to mods/content/turbolift/turbolift_areas.dm index 893f07ab67c..8874a14f998 100644 --- a/code/modules/turbolift/turbolift_areas.dm +++ b/mods/content/turbolift/turbolift_areas.dm @@ -1,5 +1,6 @@ // Used for creating the exchange areas. /area/turbolift + abstract_type = /area/turbolift name = "\improper Turbolift" base_turf = /turf/open requires_power = FALSE diff --git a/code/modules/turbolift/turbolift_console.dm b/mods/content/turbolift/turbolift_console.dm similarity index 98% rename from code/modules/turbolift/turbolift_console.dm rename to mods/content/turbolift/turbolift_console.dm index 03c3be6b0e4..4f09b77619f 100644 --- a/code/modules/turbolift/turbolift_console.dm +++ b/mods/content/turbolift/turbolift_console.dm @@ -1,7 +1,7 @@ // Base type, do not use. /obj/structure/lift name = "turbolift control component" - icon = 'icons/obj/turbolift.dmi' + icon = 'mods/content/turbolift/icons/turbolift.dmi' anchored = TRUE density = FALSE layer = ABOVE_OBJ_LAYER @@ -72,6 +72,7 @@ update_icon() /obj/structure/lift/button/standalone + directional_offset = null icon_state = "plinth" /obj/structure/lift/button/on_update_icon() diff --git a/code/modules/turbolift/turbolift_door.dm b/mods/content/turbolift/turbolift_door.dm similarity index 77% rename from code/modules/turbolift/turbolift_door.dm rename to mods/content/turbolift/turbolift_door.dm index f1254ef4f35..faed7d4ea29 100644 --- a/code/modules/turbolift/turbolift_door.dm +++ b/mods/content/turbolift/turbolift_door.dm @@ -5,12 +5,12 @@ autoclose = 0 glass = 1 airlock_type = "Lift" - icon = 'icons/obj/doors/elevator/door.dmi' - fill_file = 'icons/obj/doors/elevator/fill_steel.dmi' - glass_file = 'icons/obj/doors/elevator/fill_glass.dmi' - bolts_file = 'icons/obj/doors/elevator/lights_bolts.dmi' - deny_file = 'icons/obj/doors/elevator/lights_deny.dmi' - lights_file = 'icons/obj/doors/elevator/lights_green.dmi' + icon = 'mods/content/turbolift/icons/door/door.dmi' + fill_file = 'mods/content/turbolift/icons/door/fill_steel.dmi' + glass_file = 'mods/content/turbolift/icons/door/fill_glass.dmi' + bolts_file = 'mods/content/turbolift/icons/door/lights_bolts.dmi' + deny_file = 'mods/content/turbolift/icons/door/lights_deny.dmi' + lights_file = 'mods/content/turbolift/icons/door/lights_green.dmi' paintable = PAINT_WINDOW_PAINTABLE diff --git a/code/modules/turbolift/turbolift_floor.dm b/mods/content/turbolift/turbolift_floor.dm similarity index 100% rename from code/modules/turbolift/turbolift_floor.dm rename to mods/content/turbolift/turbolift_floor.dm diff --git a/mods/content/turbolift/turbolift_init.dm b/mods/content/turbolift/turbolift_init.dm new file mode 100644 index 00000000000..f5010303d30 --- /dev/null +++ b/mods/content/turbolift/turbolift_init.dm @@ -0,0 +1,16 @@ +/decl/modpack/turbolift + /// A list of turbolift holders to initialize. + var/list/obj/abstract/turbolift_spawner/turbolifts_to_initialize = list() + /// A list of turbolift datums whose currently-selected floor will open on misc-late init. + var/list/datum/turbolift/turbolifts_to_open = list() + +/decl/modpack/turbolift/on_mapping_pre_finalize() + // 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() + +/decl/modpack/turbolift/on_misc_late_init() + for(var/datum/turbolift/lift in turbolifts_to_open) + if(!QDELETED(lift)) + lift.open_doors() + turbolifts_to_open.Cut() diff --git a/code/modules/turbolift/turbolift_map.dm b/mods/content/turbolift/turbolift_map.dm similarity index 94% rename from code/modules/turbolift/turbolift_map.dm rename to mods/content/turbolift/turbolift_map.dm index d69a39fd5bb..eeed3f1976a 100644 --- a/code/modules/turbolift/turbolift_map.dm +++ b/mods/content/turbolift/turbolift_map.dm @@ -1,7 +1,7 @@ // Map object. /obj/abstract/turbolift_spawner name = "turbolift map placeholder" - icon = 'icons/obj/turbolift_preview_3x3.dmi' + icon = 'mods/content/turbolift/icons/turbolift_preview_3x3.dmi' dir = SOUTH // Direction of the holder determines the placement of the lift control panel and doors. var/depth = 1 // Number of floors to generate, including the initial floor. var/lift_size_x = 2 // Number of turfs on each axis to generate in addition to the first @@ -31,10 +31,12 @@ INITIALIZE_IMMEDIATE(/obj/abstract/turbolift_spawner) if(SSmapping.initialized) build_turbolift() else - SSmapping.turbolifts_to_initialize += src + var/decl/modpack/turbolift/turbolift_modpack = IMPLIED_DECL + turbolift_modpack.turbolifts_to_initialize += src /obj/abstract/turbolift_spawner/Destroy() - SSmapping.turbolifts_to_initialize -= src + var/decl/modpack/turbolift/turbolift_modpack = IMPLIED_DECL + turbolift_modpack.turbolifts_to_initialize -= src return ..() /obj/abstract/turbolift_spawner/proc/build_turbolift() @@ -254,6 +256,7 @@ INITIALIZE_IMMEDIATE(/obj/abstract/turbolift_spawner) if(SSmisc_late.initialized) lift.open_doors() else - SSmisc_late.turbolifts_to_open += lift + var/decl/modpack/turbolift/turbolift_modpack = IMPLIED_DECL + turbolift_modpack.turbolifts_to_open += lift qdel(src) // We're done. diff --git a/mods/content/turbolift/turbolift_turfs.dm b/mods/content/turbolift/turbolift_turfs.dm new file mode 100644 index 00000000000..4095ad376e6 --- /dev/null +++ b/mods/content/turbolift/turbolift_turfs.dm @@ -0,0 +1,2 @@ +/turf/wall/elevator + material = /decl/material/solid/metal/alienalloy/elevatorium diff --git a/mods/content/xenobiology/slime/items.dm b/mods/content/xenobiology/slime/items.dm index 26304f3e971..28c96213663 100644 --- a/mods/content/xenobiology/slime/items.dm +++ b/mods/content/xenobiology/slime/items.dm @@ -15,7 +15,7 @@ var/Uses = 1 // uses before it goes inert var/enhanced = 0 //has it been enhanced before? -/obj/item/slime_extract/get_base_value() +/obj/item/slime_extract/get_value_multiplier() . = ..() * Uses /obj/item/slime_extract/attackby(obj/item/used_item, mob/user) diff --git a/nano/templates/request_console.tmpl b/nano/templates/request_console.tmpl index 698f7b162c9..ee7fc4f0a2c 100644 --- a/nano/templates/request_console.tmpl +++ b/nano/templates/request_console.tmpl @@ -66,7 +66,7 @@ Used In File(s): \code\game\machinery\requests_console.dm
Message sent successfully.
{{:helper.link('Continue', 'arrowthick-1-e', { 'setScreen' : 0 })}}
{{else data.screen == 5}} -
An Error occurred. Message not sent.
+
An error occurred and your message could not be sent. Retry in 30 seconds. If the issue persists, contact your system administrator for assistance.
{{:helper.link('Continue', 'arrowthick-1-e', { 'setScreen' : 0 })}}
{{else data.screen == 6}}
@@ -104,9 +104,9 @@ Used In File(s): \code\game\machinery\requests_console.dm
{{else}} {{if data.newmessagepriority == 1}} -
There are new messages
+
There are new messages.
{{else data.newmessagepriority == 2}} -
NEW PRIORITY MESSAGES
+
NEW PRIORITY MESSAGE!
{{/if}}
{{:helper.link('View Messages', data.newmessagepriority ? 'mail-closed' : 'mail-open', { 'setScreen' : 6 })}}

diff --git a/nebula.dme b/nebula.dme index b40eee57081..fb2bf99de08 100644 --- a/nebula.dme +++ b/nebula.dme @@ -176,7 +176,7 @@ #include "code\_onclick\drag_drop.dm" #include "code\_onclick\ghost.dm" #include "code\_onclick\item_attack.dm" -#include "code\_onclick\MouseDrag.dm" +#include "code\_onclick\mouse_drag.dm" #include "code\_onclick\other_mobs.dm" #include "code\_onclick\rig.dm" #include "code\_onclick\hud\_defines.dm" @@ -273,6 +273,7 @@ #include "code\controllers\subsystems\ambience.dm" #include "code\controllers\subsystems\ao.dm" #include "code\controllers\subsystems\atoms.dm" +#include "code\controllers\subsystems\clickdrag.dm" #include "code\controllers\subsystems\configuration.dm" #include "code\controllers\subsystems\daycycle.dm" #include "code\controllers\subsystems\disposals.dm" @@ -782,7 +783,6 @@ #include "code\game\antagonist\antagonist_update.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" @@ -834,6 +834,7 @@ #include "code\game\machinery\dehumidifier.dm" #include "code\game\machinery\deployable.dm" #include "code\game\machinery\doppler_array.dm" +#include "code\game\machinery\emitter.dm" #include "code\game\machinery\flasher.dm" #include "code\game\machinery\floodlight.dm" #include "code\game\machinery\floor_light.dm" @@ -886,6 +887,7 @@ #include "code\game\machinery\_machines_base\machine_construction\blast_doors.dm" #include "code\game\machinery\_machines_base\machine_construction\computer.dm" #include "code\game\machinery\_machines_base\machine_construction\default.dm" +#include "code\game\machinery\_machines_base\machine_construction\emitter.dm" #include "code\game\machinery\_machines_base\machine_construction\frame.dm" #include "code\game\machinery\_machines_base\machine_construction\item_chassis.dm" #include "code\game\machinery\_machines_base\machine_construction\noninteractive.dm" @@ -1159,7 +1161,6 @@ #include "code\game\objects\items\circuitboards\wall.dm" #include "code\game\objects\items\circuitboards\computer\air_management.dm" #include "code\game\objects\items\circuitboards\computer\computer.dm" -#include "code\game\objects\items\circuitboards\computer\holodeckcontrol.dm" #include "code\game\objects\items\circuitboards\computer\modular.dm" #include "code\game\objects\items\circuitboards\computer\shuttle.dm" #include "code\game\objects\items\circuitboards\computer\station_alert.dm" @@ -1443,7 +1444,6 @@ #include "code\game\objects\structures\drying_rack.dm" #include "code\game\objects\structures\emergency_dispenser.dm" #include "code\game\objects\structures\extinguisher.dm" -#include "code\game\objects\structures\fences.dm" #include "code\game\objects\structures\fireaxe_cabinet.dm" #include "code\game\objects\structures\fires.dm" #include "code\game\objects\structures\fishtanks.dm" @@ -1548,6 +1548,8 @@ #include "code\game\objects\structures\decorations\_decoration.dm" #include "code\game\objects\structures\decorations\gargoyle.dm" #include "code\game\objects\structures\doors\_door.dm" +#include "code\game\objects\structures\fences\_fences.dm" +#include "code\game\objects\structures\fences\fence_types.dm" #include "code\game\objects\structures\flora\_flora.dm" #include "code\game\objects\structures\flora\bush.dm" #include "code\game\objects\structures\flora\grass.dm" @@ -2429,9 +2431,6 @@ #include "code\modules\fabrication\designs\textile\protective.dm" #include "code\modules\fabrication\designs\textile\space.dm" #include "code\modules\fabrication\designs\textile\storage.dm" -#include "code\modules\fishing\bait.dm" -#include "code\modules\fishing\fishing_line.dm" -#include "code\modules\fishing\fishing_rod.dm" #include "code\modules\fission\core.dm" #include "code\modules\fission\core_control.dm" #include "code\modules\fission\fission_circuits.dm" @@ -2595,9 +2594,6 @@ #include "code\modules\holidays\holiday_hook.dm" #include "code\modules\holidays\holiday_name.dm" #include "code\modules\holidays\holiday_special.dm" -#include "code\modules\holodeck\HolodeckControl.dm" -#include "code\modules\holodeck\HolodeckObjects.dm" -#include "code\modules\holodeck\HolodeckPrograms.dm" #include "code\modules\holomap\holomap.dm" #include "code\modules\hotloading\_admin.dm" #include "code\modules\hotloading\note.dm" @@ -2614,6 +2610,7 @@ #include "code\modules\hydroponics\seed_mobs.dm" #include "code\modules\hydroponics\seed_packets.dm" #include "code\modules\hydroponics\seed_storage.dm" +#include "code\modules\hydroponics\worm.dm" #include "code\modules\hydroponics\plant_types\seeds_herbs.dm" #include "code\modules\hydroponics\plant_types\seeds_misc.dm" #include "code\modules\hydroponics\spreading\spreading.dm" @@ -3475,25 +3472,12 @@ #include "code\modules\power\cable\heavycable.dm" #include "code\modules\power\cell\_cell.dm" #include "code\modules\power\cell\cell_types.dm" -#include "code\modules\power\singularity\collector.dm" -#include "code\modules\power\singularity\containment_field.dm" -#include "code\modules\power\singularity\emitter.dm" -#include "code\modules\power\singularity\field_generator.dm" -#include "code\modules\power\singularity\generator.dm" -#include "code\modules\power\singularity\singularity.dm" -#include "code\modules\power\singularity\singularity_events.dm" -#include "code\modules\power\singularity\singularity_stages.dm" -#include "code\modules\power\singularity\particle_accelerator\particle.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_accelerator.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_chamber.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_control.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_emitter.dm" -#include "code\modules\power\singularity\particle_accelerator\particle_power.dm" #include "code\modules\power\solar\solar_control.dm" #include "code\modules\power\solar\solar_panel.dm" #include "code\modules\power\solar\tracker.dm" +#include "code\modules\projectiles\_gun.dm" #include "code\modules\projectiles\ammunition.dm" -#include "code\modules\projectiles\gun.dm" +#include "code\modules\projectiles\autofire.dm" #include "code\modules\projectiles\projectile.dm" #include "code\modules\projectiles\secure.dm" #include "code\modules\projectiles\ammunition\boxes.dm" @@ -3809,6 +3793,19 @@ #include "code\modules\shuttles\shuttle_specops.dm" #include "code\modules\shuttles\shuttle_supply.dm" #include "code\modules\shuttles\shuttles_multi.dm" +#include "code\modules\singularity\collector.dm" +#include "code\modules\singularity\containment_field.dm" +#include "code\modules\singularity\field_generator.dm" +#include "code\modules\singularity\generator.dm" +#include "code\modules\singularity\singularity.dm" +#include "code\modules\singularity\singularity_events.dm" +#include "code\modules\singularity\singularity_stages.dm" +#include "code\modules\singularity\particle_accelerator\particle.dm" +#include "code\modules\singularity\particle_accelerator\particle_accelerator.dm" +#include "code\modules\singularity\particle_accelerator\particle_chamber.dm" +#include "code\modules\singularity\particle_accelerator\particle_control.dm" +#include "code\modules\singularity\particle_accelerator\particle_emitter.dm" +#include "code\modules\singularity\particle_accelerator\particle_power.dm" #include "code\modules\smes\_smes.dm" #include "code\modules\smes\smes_buildable.dm" #include "code\modules\smes\smes_circuit.dm" @@ -3937,13 +3934,6 @@ #include "code\modules\turbines\largeturbine.dm" #include "code\modules\turbines\smallturbine.dm" #include "code\modules\turbines\turbine_circuits.dm" -#include "code\modules\turbolift\turbolift.dm" -#include "code\modules\turbolift\turbolift_areas.dm" -#include "code\modules\turbolift\turbolift_console.dm" -#include "code\modules\turbolift\turbolift_door.dm" -#include "code\modules\turbolift\turbolift_floor.dm" -#include "code\modules\turbolift\turbolift_map.dm" -#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" diff --git a/tools/map_migrations/5229_breakerbox.txt b/tools/map_migrations/5229_breakerbox.txt new file mode 100644 index 00000000000..74f298b033c --- /dev/null +++ b/tools/map_migrations/5229_breakerbox.txt @@ -0,0 +1 @@ +/obj/machinery/power/breakerbox/@SUBTYPES : /obj/machinery/breakerbox{@OLD} \ No newline at end of file diff --git a/tools/map_migrations/5401_emitter_construct_state.txt b/tools/map_migrations/5401_emitter_construct_state.txt new file mode 100644 index 00000000000..8ce764621f5 --- /dev/null +++ b/tools/map_migrations/5401_emitter_construct_state.txt @@ -0,0 +1,8 @@ +# emitters use construct states instead of a bespoke state var +# handle redundant var sets if present +/obj/machinery/emitter/anchored{state = 2} : @OLD{@OLD; state = @SKIP; anchored = @SKIP} +/obj/machinery/emitter/gyrotron/anchored{state = 2} : @OLD{@OLD; state = @SKIP; anchored = @SKIP} +# remove state/anchored vars, change subtype if needed +/obj/machinery/emitter/@SUBTYPES{state = 2} : /obj/machinery/emitter/@SUBTYPES/anchored{@OLD; state = @SKIP; anchored = @SKIP} +/obj/machinery/emitter/@SUBTYPES{state = 1} : /obj/machinery/emitter/@SUBTYPES{@OLD; state = @SKIP; anchored = @SKIP} +/obj/machinery/emitter/@SUBTYPES{state = 0} : /obj/machinery/emitter/@SUBTYPES{@OLD; state = @SKIP; anchored = @SKIP}