diff --git a/code/__defines/armor.dm b/code/__defines/armor.dm index 155ccb22f4ac..c43e7cc2f263 100644 --- a/code/__defines/armor.dm +++ b/code/__defines/armor.dm @@ -33,6 +33,7 @@ #define ARMOR_RAD_MINOR 10 #define ARMOR_RAD_SMALL 25 #define ARMOR_RAD_RESISTANT 40 +#define ARMOR_RAD_LARGE 60 #define ARMOR_RAD_SHIELDED 100 #define ARMOR_BOMB_MINOR 10 diff --git a/code/_global_vars/lists/flavor.dm b/code/_global_vars/lists/flavor.dm index 80c11924a919..7a21627335d2 100644 --- a/code/_global_vars/lists/flavor.dm +++ b/code/_global_vars/lists/flavor.dm @@ -118,6 +118,7 @@ GLOBAL_GETTER(cable_colors, /list, SetupCableColors()) . = list() var/list/valid_cable_coils = typesof(/obj/item/stack/cable_coil) - typesof( + /obj/item/stack/cable_coil/five, /obj/item/stack/cable_coil/single, /obj/item/stack/cable_coil/cut, /obj/item/stack/cable_coil/cyborg, diff --git a/code/_global_vars/sound.dm b/code/_global_vars/sound.dm index bab80ae12bee..fec9867b5f09 100644 --- a/code/_global_vars/sound.dm +++ b/code/_global_vars/sound.dm @@ -128,4 +128,11 @@ var/global/list/sweeping_sound = list( 'sound/foley/sweeping5.ogg', 'sound/foley/sweeping6.ogg', 'sound/foley/sweeping7.ogg', -) \ No newline at end of file +) + +var/global/list/ricochet_sound = list( + 'sound/weapons/guns/ricochet1.ogg', + 'sound/weapons/guns/ricochet2.ogg', + 'sound/weapons/guns/ricochet3.ogg', + 'sound/weapons/guns/ricochet4.ogg' +) diff --git a/code/_helpers/type2type.dm b/code/_helpers/type2type.dm index 2cd597169bd9..ed43cf149ee3 100644 --- a/code/_helpers/type2type.dm +++ b/code/_helpers/type2type.dm @@ -1,13 +1,20 @@ /* * Holds procs designed to change one type of value, into another. * Contains: - * text2list & list2text + * alist2list * file2list * angle2dir * angle2text - * worldtime2text */ +// This proc does not support converting numerically indexed alists to assoc lists. +/proc/alist2list(alist/input) + . = list() + for(var/k,v in input) + if(isnum(k)) + CRASH("Numeric index passed to alist2list()!") + .[k] = v + // Splits the text of a file at seperator and returns them in a list. /proc/file2list(filename, seperator = "\n") return splittext(safe_file2text(filename), seperator) diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index 51398a81da00..727108ce080a 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -52,7 +52,6 @@ #define ui_movi "RIGHT-2:24,BOTTOM:5" #define ui_attack_selector "RIGHT-2:27,BOTTOM+2:9" #define ui_zonesel "RIGHT-1:28,BOTTOM:5" -#define ui_stamina "RIGHT-2:24,BOTTOM:8" #define ui_borg_module "RIGHT-1:28,BOTTOM+1:7" diff --git a/code/_onclick/hud/hud_types/_hud.dm b/code/_onclick/hud/hud_types/_hud.dm index 9e65b258399d..5a64f04f09a9 100644 --- a/code/_onclick/hud/hud_types/_hud.dm +++ b/code/_onclick/hud/hud_types/_hud.dm @@ -92,6 +92,9 @@ var/action_buttons_hidden = FALSE var/obj/screen/action_button/hide_toggle/hide_actions_toggle + var/const/HAND_UI_PER_ROW = 4 + var/const/HAND_UI_INITIAL_Y_OFFSET = 21 + // TODO: declify these. VAR_PROTECTED/gun_mode_toggle_type VAR_PRIVATE/obj/screen/gun/mode/gun_mode_toggle @@ -351,11 +354,10 @@ qdel(inv_box) // Rebuild offsets for the hand elements. - var/const/elems_per_row = 4 - var/hand_y_offset = 21 + var/hand_y_offset = HAND_UI_INITIAL_Y_OFFSET var/list/elements = hud_elements_hands?.Copy() while(length(elements)) - var/copy_index = min(length(elements), elems_per_row)+1 + var/copy_index = min(length(elements), HAND_UI_PER_ROW)+1 var/list/sublist = elements.Copy(1, copy_index) elements.Cut(1, copy_index) var/hand_x_offset = (world.icon_size/2) * (1 - length(sublist)) @@ -389,6 +391,7 @@ if(mymob.client) mymob.client.screen |= swap_elem + refresh_element(HUD_STAMINA) update_hand_elements() return TRUE diff --git a/code/_onclick/hud/screen/screen_stamina.dm b/code/_onclick/hud/screen/screen_stamina.dm index 5d4f34b32da9..851fdf1d8682 100644 --- a/code/_onclick/hud/screen/screen_stamina.dm +++ b/code/_onclick/hud/screen/screen_stamina.dm @@ -1,24 +1,41 @@ /obj/screen/stamina name = "stamina" - icon = 'icons/effects/progressbar.dmi' - icon_state = "prog_bar_100" + icon = 'icons/effects/staminabar.dmi' + icon_state = "bar" invisibility = INVISIBILITY_MAXIMUM - screen_loc = ui_stamina use_supplied_ui_color = FALSE use_supplied_ui_alpha = FALSE use_supplied_ui_icon = FALSE requires_ui_style = FALSE layer = HUD_BASE_LAYER + 0.1 // needs to layer over the movement intent element + var/const/STAMINA_STATE_PERIOD = 5 + +/obj/screen/stamina/Initialize(mapload, mob/_owner, decl/ui_style/ui_style, ui_color, ui_alpha, ui_cat) + . = ..() + update_icon() /obj/screen/stamina/on_update_icon() . = ..() var/mob/living/owner = owner_ref?.resolve() if(!istype(owner)) set_invisibility(INVISIBILITY_MAXIMUM) + return + + var/hand_row_offset = /datum/hud::HAND_UI_INITIAL_Y_OFFSET + (ceil(length(owner.get_held_item_slots()) / /datum/hud::HAND_UI_PER_ROW) * world.icon_size) + 16 + screen_loc = "CENTER:-32,BOTTOM:[hand_row_offset]" + + var/stamina = owner.get_stamina() + cut_overlays() + if(stamina < 100) + set_invisibility(INVISIBILITY_NONE) + var/stamina_amt = floor(stamina/STAMINA_STATE_PERIOD)*STAMINA_STATE_PERIOD + var/bar_overlay_state = "bar_[stamina_amt]" + if(stamina_amt > 0 && stamina <= 25) + bar_overlay_state = "[bar_overlay_state]_fail" + var/image/bar_overlay = image(icon = icon, icon_state = bar_overlay_state) + bar_overlay.appearance_flags |= RESET_COLOR + bar_overlay.color = COLOR_WHITE + add_overlay(bar_overlay) else - var/stamina = owner.get_stamina() - if(stamina < 100) - set_invisibility(INVISIBILITY_NONE) - icon_state = "prog_bar_[floor(stamina/5)*5][(stamina >= 5) && (stamina <= 25) ? "_fail" : null]" - else - set_invisibility(INVISIBILITY_MAXIMUM) + set_invisibility(INVISIBILITY_MAXIMUM) + compile_overlays() \ No newline at end of file diff --git a/code/controllers/subsystems/initialization/persistence.dm b/code/controllers/subsystems/initialization/persistence.dm index c1119f8ca859..767d46bbf0cc 100644 --- a/code/controllers/subsystems/initialization/persistence.dm +++ b/code/controllers/subsystems/initialization/persistence.dm @@ -38,11 +38,19 @@ SUBSYSTEM_DEF(persistence) var/turf/T = get_turf(value) if(!T) - return + return FALSE - var/area/A = get_area(T) - if(!A || (A.area_flags & AREA_FLAG_IS_NOT_PERSISTENT)) - return + var/decl/persistence_handler/handler = RESOLVE_TO_DECL(track_type) + if(!istype(handler)) + return FALSE + + if(handler.station_restricted && (!T || !(T.z in SSmapping.station_levels) )) + return FALSE + + if(handler.area_restricted) + var/area/A = get_area(T) + if(!A || (A.area_flags & AREA_FLAG_IS_NOT_PERSISTENT)) + return FALSE var/datum/level_data/level = SSmapping.levels_by_z[T.z] if(!istype(level) || !level.permit_persistence) diff --git a/code/datums/composite_sounds/vehicle_engine.dm b/code/datums/composite_sounds/vehicle_engine.dm new file mode 100644 index 000000000000..824d0044134b --- /dev/null +++ b/code/datums/composite_sounds/vehicle_engine.dm @@ -0,0 +1,7 @@ +/datum/composite_sound/vehicle_engine + start_sound = 'sound/machines/vehicle/engine_start.ogg' + start_length = 2 + mid_sounds = list('sound/machines/vehicle/engine_mid.ogg'=1) + mid_length = 6 + end_sound = 'sound/machines/vehicle/engine_end.ogg' + play_volume = 20 diff --git a/code/datums/repositories/atom_info.dm b/code/datums/repositories/atom_info.dm index a84f10736b76..2cfa4bd9ff3e 100644 --- a/code/datums/repositories/atom_info.dm +++ b/code/datums/repositories/atom_info.dm @@ -1,13 +1,14 @@ var/global/repository/atom_info/atom_info_repository = new() /repository/atom_info - var/list/matter_cache = list() + var/list/matter_cache = list() var/list/combined_worth_cache = list() - var/list/single_worth_cache = list() - var/list/name_cache = list() - var/list/description_cache = list() - var/list/matter_mult_cache = list() - var/list/origin_tech_cache = list() + var/list/single_worth_cache = list() + var/list/name_cache = list() + var/list/description_cache = list() + var/list/matter_mult_cache = list() + var/list/origin_tech_cache = list() + var/list/appearance_cache = list() /repository/atom_info/proc/create_key_for(var/_path, var/_mat, var/_amount) . = "[_path]" @@ -24,7 +25,7 @@ var/global/repository/atom_info/atom_info_repository = new() else . = new _path -/repository/atom_info/proc/update_cached_info_for(var/_path, var/_mat, var/_amount, var/key) +/repository/atom_info/proc/update_cached_info_for(var/_path, var/_mat, var/_amount, var/key, var/cache_appearance = FALSE) var/atom/instance if(!matter_cache[key]) instance = get_instance_of(_path, _mat, _amount) @@ -41,6 +42,9 @@ var/global/repository/atom_info/atom_info_repository = new() if(!description_cache[key]) instance = instance || get_instance_of(_path, _mat, _amount) description_cache[key] = instance.desc + if(cache_appearance && !appearance_cache[key]) + instance = instance || get_instance_of(_path, _mat, _amount) + appearance_cache[key] = instance.appearance if(!matter_mult_cache[key] && ispath(_path, /obj)) var/obj/obj_instance = instance || get_instance_of(_path, _mat, _amount) matter_mult_cache[key] = obj_instance.get_matter_amount_modifier() @@ -84,4 +88,10 @@ var/global/repository/atom_info/atom_info_repository = new() /repository/atom_info/proc/get_origin_tech_for(var/_path, var/_mat, var/_amount) var/key = create_key_for(_path, _mat, _amount) update_cached_info_for(_path, _mat, _amount, key) - . = origin_tech_cache[key] \ No newline at end of file + . = origin_tech_cache[key] + +// Bespoke proc; only cache appearance if and when this proc is called, not more generally. +/repository/atom_info/proc/get_appearance_of(var/_path, var/_mat, var/_amount) + var/key = create_key_for(_path, _mat, _amount) + update_cached_info_for(_path, _mat, _amount, key, cache_appearance = TRUE) + . = appearance_cache[key] diff --git a/code/datums/supplypacks/operations.dm b/code/datums/supplypacks/operations.dm index 09c51789dc3c..0ec89477c5e1 100644 --- a/code/datums/supplypacks/operations.dm +++ b/code/datums/supplypacks/operations.dm @@ -3,13 +3,13 @@ /decl/hierarchy/supply_pack/operations/cargotrain name = "Equipment - Cargo Train Tug" - contains = list(/obj/vehicle/train/cargo/engine) + contains = list(/obj/vehicle/train/engine) containertype = /obj/structure/largecrate containername = "cargo train tug crate" /decl/hierarchy/supply_pack/operations/cargotrailer name = "Equipment - Cargo Train Trolley" - contains = list(/obj/vehicle/train/cargo/trolley) + contains = list(/obj/vehicle/train/trolley) containertype = /obj/structure/largecrate containername = "cargo train trolley crate" diff --git a/code/datums/supplypacks/science.dm b/code/datums/supplypacks/science.dm index d56132bab632..13e8cd330f2e 100644 --- a/code/datums/supplypacks/science.dm +++ b/code/datums/supplypacks/science.dm @@ -66,12 +66,3 @@ name = "Gear - Illumination grenades" contains = list(/obj/item/grenade/light = 8) containername = "illumination grenade crate" - -/decl/hierarchy/supply_pack/science/stasis_cages - name = "Stasis Cage" - contains = list( - /obj/structure/stasis_cage = 1 - ) - containertype = /obj/structure/closet/crate/large - containername = "stasis cage crate" - access = access_xenofauna diff --git a/code/datums/supplypacks/supplypack.dm b/code/datums/supplypacks/supplypack.dm index 319d1dee4089..7544b406d35f 100644 --- a/code/datums/supplypacks/supplypack.dm +++ b/code/datums/supplypacks/supplypack.dm @@ -19,13 +19,16 @@ var/global/list/cargoprices = list() . = ..() // make sure children are set up if(is_category()) return // don't do any of this for categories + var/total_contained = 0 + for(var/entry in contains) + total_contained += max(1, contains[entry]) if(!num_contained) - for(var/entry in contains) - num_contained += max(1, contains[entry]) + num_contained = total_contained if(isnull(cost)) cost = 0 for(var/entry in contains) cost += atom_info_repository.get_combined_worth_for(entry) * max(1, contains[entry]) + cost *= num_contained / total_contained // if you get a random selection, it costs the expected value rather than the total worth. gambling! cost += containertype ? atom_info_repository.get_single_worth_for(containertype) : 0 cost = max(1, NONUNIT_CEILING((cost * WORTH_TO_SUPPLY_POINTS_CONSTANT * SSsupply.price_markup), WORTH_TO_SUPPLY_POINTS_ROUND_CONSTANT)) global.cargoprices[name] = cost diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 28a1dc589ec6..e698467579de 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -742,17 +742,19 @@ - `post_climb_check?`: If we should check if the user can continue climbing - Return: `TRUE` if they can climb, otherwise `FALSE` */ -/atom/proc/can_climb(var/mob/living/user, post_climb_check=0) +/atom/proc/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) if (!(atom_flags & ATOM_FLAG_CLIMBABLE) || !user.can_touch(src) || (!post_climb_check && climbers && (user in climbers))) return FALSE if (!user.Adjacent(src)) - to_chat(user, "You can't climb there, the way is blocked.") + if(!silent) + to_chat(user, SPAN_WARNING("You can't climb there, the way is blocked.")) return FALSE var/obj/occupied = turf_is_crowded(user) if(occupied) - to_chat(user, "There's \a [occupied] in the way.") + if(!silent) + to_chat(user, SPAN_WARNING("There's \a [occupied] in the way.")) return FALSE return TRUE @@ -1065,3 +1067,18 @@ if(blood_color) return FONT_COLORED(blood_color, "stained") return null + +// Used to mark a turf as containing objects that are dangerous to step onto. +/atom/proc/register_dangerous_to_step() + var/turf/T = get_turf(src) + if(T) + T.register_dangerous_object(src) + +/atom/proc/unregister_dangerous_to_step() + var/turf/T = get_turf(src) + if(T) + T.unregister_dangerous_object(src) + +// Test for if stepping on a tile containing this obj is safe to do, used for things like landmines and cliffs. +/atom/proc/is_safe_to_step(mob/living/stepper) + return TRUE diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 94c8602eefee..628b5880042e 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -628,3 +628,7 @@ /atom/movable/proc/is_valid_merchant_pad_target() return simulated + +// TODO reimplement this properly. +/atom/movable/proc/is_incorporeal() + return !simulated diff --git a/code/game/jobs/access_datum.dm b/code/game/jobs/access_datum.dm index 1ed16660eeb9..0d6369221804 100644 --- a/code/game/jobs/access_datum.dm +++ b/code/game/jobs/access_datum.dm @@ -268,12 +268,6 @@ var/global/const/access_research = "ACCESS_RESEARCH" //47 desc = "Science" region = ACCESS_REGION_RESEARCH -var/global/const/access_explorer = "ACCESS_EXPLORER" -/datum/access/explorer - id = access_explorer - desc = "Explorer" - region = ACCESS_REGION_GENERAL - var/global/const/access_mining = "ACCESS_MINING" //48 /datum/access/mining id = access_mining @@ -310,12 +304,6 @@ var/global/const/access_xenobiology = "ACCESS_XENOBIO" //55 desc = "Xenobiology Lab" region = ACCESS_REGION_RESEARCH -var/global/const/access_xenofauna = "ACCESS_XENOFAUNA" -/datum/access/xenofauna - id = access_xenofauna - desc = "Xenfauna Lab" - region = ACCESS_REGION_RESEARCH - var/global/const/access_ce = "ACCESS_CHIEF_ENGINEER" //56 /datum/access/ce id = access_ce diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index eab4f5d6445b..464042c168e3 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -161,7 +161,7 @@ TLV["temperature"] = list(T0C-26, T0C, T0C+40, T0C+66) // K var/decl/environment_data/env_info = GET_DECL(environment_type) - for(var/g in decls_repository.get_decl_paths_of_subtype(/decl/material/gas)) + for(var/g in get_filterable_material_types()) if(!env_info.important_gasses[g]) trace_gas += g // not everything in these lists is a subtype of /decl/material/gas, so: diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index b6989e112bdd..80e331893ec2 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -24,7 +24,7 @@ . = ..() if(!scrubbing_gas) scrubbing_gas = list() - for(var/g in decls_repository.get_decl_paths_of_subtype(/decl/material/gas)) + for(var/g in get_filterable_material_types()) if(g != /decl/material/gas/oxygen && g != /decl/material/gas/nitrogen) scrubbing_gas += g diff --git a/code/game/objects/__objs.dm b/code/game/objects/__objs.dm index 01cc88b50e31..123678e0cee3 100644 --- a/code/game/objects/__objs.dm +++ b/code/game/objects/__objs.dm @@ -493,4 +493,3 @@ if(anchored) return FALSE return ..() - diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index 52c615b4daeb..1bf090caf26e 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -280,6 +280,59 @@ steam.start() -- spawns the effect ADJ_STATUS(M, STAT_ASLEEP, 1) M.cough() +///////////////////////////////////////////// +// 'Elemental' smoke +///////////////////////////////////////////// +/obj/effect/effect/smoke/elemental + name = "cloud" + desc = "A cloud of some kind that seems really generic and boring." + opacity = FALSE + abstract_type = /obj/effect/effect/smoke/elemental + var/strength = 5 // How much damage to do inside each affect() + +/obj/effect/effect/smoke/elemental/Initialize() + START_PROCESSING(SSobj, src) + return ..() + +/obj/effect/effect/smoke/elemental/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/effect/smoke/elemental/Move(atom/old_loc, direction, forced = FALSE) + . = ..() + if(.) + for(var/mob/living/victim in range(1, src)) + affect(victim) + +/obj/effect/effect/smoke/elemental/Process() + for(var/mob/living/victim in range(1, src)) + affect(victim) + +/obj/effect/effect/smoke/elemental/proc/affect(mob/living/victim) + return + +/obj/effect/effect/smoke/elemental/fire + name = "burning cloud" + desc = "A cloud of something that is on fire." + color = COLOR_ORANGE + light_color = "#ff0000" + light_range = 2 + light_power = 5 + +/obj/effect/effect/smoke/elemental/fire/affect(mob/living/victim) + victim.take_damage(strength, BURN) + victim.ignite_fire() + +/obj/effect/effect/smoke/elemental/mist + name = "misty cloud" + desc = "A cloud filled with water vapor." + color = "#ccffff" + alpha = 128 + strength = 1 + +/obj/effect/effect/smoke/elemental/mist/affect(mob/living/victim) + victim.extinguish_fire() + ///////////////////////////////////////////// // Mustard Gas ///////////////////////////////////////////// @@ -358,6 +411,11 @@ steam.start() -- spawns the effect /datum/effect/effect/system/smoke_spread/sleepy smoke_type = /obj/effect/effect/smoke/sleepy +/datum/effect/effect/system/smoke_spread/fire + smoke_type = /obj/effect/effect/smoke/elemental/fire + +/datum/effect/effect/system/smoke_spread/mist + smoke_type = /obj/effect/effect/smoke/elemental/mist /datum/effect/effect/system/smoke_spread/mustard smoke_type = /obj/effect/effect/smoke/mustard diff --git a/code/game/objects/effects/map_effect/_map_effect.dm b/code/game/objects/effects/map_effect/_map_effect.dm new file mode 100644 index 000000000000..50cad4fa38c5 --- /dev/null +++ b/code/game/objects/effects/map_effect/_map_effect.dm @@ -0,0 +1,31 @@ +/obj/abstract/map_effect + icon = 'icons/effects/map_effects.dmi' + + // Below vars concern check_for_player_proximity() and is used to not waste effort if nobody is around to appreciate the effects. + /// If true, the game will not try to suppress this from firing if nobody is around to see it. + var/always_run = FALSE + /// How many tiles a mob with a client must be for this to run. + var/proximity_needed = 12 + /// If true, ghosts won't satisfy the above requirement. + var/ignore_ghosts = FALSE + /// If true, AFK people (5 minutes) won't satisfy it as well. + var/ignore_afk = TRUE + /// How long until we check for players again. + var/retry_delay = 5 SECONDS + /// Next time we're going to do ACTUAL WORK + var/next_attempt = 0 + +// Helper proc to optimize the use of effects by making sure they do not run if nobody is around to perceive it. +/obj/abstract/map_effect/proc/check_for_player_proximity(radius = 12, ignore_ghosts = FALSE, ignore_afk = TRUE) + if(!z) + return FALSE + for(var/mob/player as anything in player_list) + if(player.z != z) + continue + if(ignore_ghosts && isobserver(player)) + continue + if(ignore_afk && player.client && player.client.is_afk(5 MINUTES)) + continue + if(get_dist(player, src) <= radius) + return TRUE + return FALSE diff --git a/code/game/objects/effects/map_effect/interval/_interval.dm b/code/game/objects/effects/map_effect/interval/_interval.dm new file mode 100644 index 000000000000..ba898570c761 --- /dev/null +++ b/code/game/objects/effects/map_effect/interval/_interval.dm @@ -0,0 +1,32 @@ +// Base type for effects that run on variable intervals. +/obj/abstract/map_effect/interval + var/interval_lower_bound = 5 SECONDS // Lower number for how often the map_effect will trigger. + var/interval_upper_bound = 5 SECONDS // Higher number for above. + +/obj/abstract/map_effect/interval/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/abstract/map_effect/interval/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +// Override this for the specific thing to do. +/obj/abstract/map_effect/interval/proc/trigger_map_effect() + return + +// Handles the delay and making sure it doesn't run when it would be bad. +/obj/abstract/map_effect/interval/Process() + + //Not yet! + if(world.time < next_attempt) + return + + // Check to see if we're useful first. + if(!always_run && !check_for_player_proximity(proximity_needed, ignore_ghosts, ignore_afk)) + next_attempt = world.time + retry_delay + return + + // Hey there's someone nearby. + next_attempt = world.time + rand(interval_lower_bound, interval_upper_bound) + trigger_map_effect() diff --git a/code/game/objects/effects/map_effect/interval/effect_emitter.dm b/code/game/objects/effects/map_effect/interval/effect_emitter.dm new file mode 100644 index 000000000000..0daf63388e09 --- /dev/null +++ b/code/game/objects/effects/map_effect/interval/effect_emitter.dm @@ -0,0 +1,63 @@ +/obj/abstract/map_effect/interval/effect_emitter + /// Effect system attached. Set to type to create in Initialize(). + var/datum/effect/effect/system/effect_system = null + /// How many effect objects to create on each interval. Note that there's a hard cap on certain effect_systems. + var/effect_amount = 10 + /// If true, effects only move in cardinal directions. + var/effect_cardinals_only = FALSE + /// If set, effects emitted will always move in this direction. + var/effect_forced_dir + +/obj/abstract/map_effect/interval/effect_emitter/Initialize() + if(ispath(effect_system)) + effect_system = new effect_system() + if(!istype(effect_system)) + return INITIALIZE_HINT_QDEL + effect_system.attach(src) + return ..() + +/obj/abstract/map_effect/interval/effect_emitter/interval/Destroy() + QDEL_NULL(effect_system) + return ..() + + +/obj/abstract/map_effect/interval/effect_emitter/trigger_map_effect() + to_world("[type]: effect firing") + if(istype(effect_system) && !QDELETED(src)) + effect_system.set_up(effect_amount, effect_cardinals_only, src.loc, effect_forced_dir) + effect_system.start() + +// Makes sparks. +/obj/abstract/map_effect/interval/effect_emitter/sparks + name = "spark emitter" + icon_state = "spark_emitter" + effect_system = /datum/effect/effect/system/spark_spread + interval_lower_bound = 3 SECONDS + interval_upper_bound = 7 SECONDS + +// Makes ""steam"" that looks like fire extinguisher water except it does nothing. +/obj/abstract/map_effect/interval/effect_emitter/steam + name = "steam emitter" + icon_state = "smoke_emitter" + effect_system = /datum/effect/effect/system/steam_spread + +// Creates smoke clouds every so often. +/obj/abstract/map_effect/interval/effect_emitter/smoke + name = "smoke emitter" + icon_state = "smoke_emitter" + effect_system = /datum/effect/effect/system/smoke_spread + interval_lower_bound = 1 SECOND + interval_upper_bound = 1 SECOND + effect_amount = 2 + +/obj/abstract/map_effect/interval/effect_emitter/smoke/mist + name = "mist smoke emitter" + effect_system = /datum/effect/effect/system/smoke_spread/mist + +/obj/abstract/map_effect/interval/effect_emitter/smoke/bad + name = "bad smoke emitter" + effect_system = /datum/effect/effect/system/smoke_spread/bad + +/obj/abstract/map_effect/interval/effect_emitter/smoke/fire + name = "fire smoke emitter" + effect_system = /datum/effect/effect/system/smoke_spread/fire diff --git a/code/game/objects/effects/map_effect/interval/screen_shaker.dm b/code/game/objects/effects/map_effect/interval/screen_shaker.dm new file mode 100644 index 000000000000..724bb7a256df --- /dev/null +++ b/code/game/objects/effects/map_effect/interval/screen_shaker.dm @@ -0,0 +1,17 @@ +/obj/abstract/map_effect/interval/screen_shaker + name = "screen shaker" + icon_state = "screen_shaker" + interval_lower_bound = 1 SECOND + interval_upper_bound = 2 SECONDS + + /// How far the shaking effect extends to. By default it is one screen length. + var/shake_radius = 7 + /// How long the shaking lasts. + var/shake_duration = 2 + /// How much it shakes. + var/shake_strength = 1 + +/obj/abstract/map_effect/interval/screen_shaker/trigger_map_effect() + for(var/mob/player in player_list) + if(player.z == z && get_dist(src, player) <= shake_radius) + shake_camera(player, shake_duration, shake_strength) diff --git a/code/game/objects/effects/map_effect/interval/sound_emitter.dm b/code/game/objects/effects/map_effect/interval/sound_emitter.dm new file mode 100644 index 000000000000..058434882b32 --- /dev/null +++ b/code/game/objects/effects/map_effect/interval/sound_emitter.dm @@ -0,0 +1,51 @@ +/obj/abstract/map_effect/interval/sound_emitter +// Plays a sound at its location every so often. + name = "sound emitter" + icon_state = "sound_emitter" + /// How loud the sound is. 0 is silent, and 100 is loudest. Please be reasonable with the volume. Note that things like vacuum may affect the volume heard by other mobs. + var/sound_volume = 50 + /// If the sound will sound somewhat different each time. If a specific frequency is desired, sound_frequency must also be set. + var/sound_frequency_variance = TRUE + /// Set to make sounds heard from farther away than normal. + var/sound_extra_range = 0 + /// Within the 'fallout distance', the sound stays at the same volume, otherwise it attenuates. Higher numbers make the sound fade out more slowly with distance. + var/sound_fallout = 0 + /// If true, sounds will not be distorted due to the current area's 'sound environment'. It DOES NOT make the sound have a constant volume or z-level wide range, despite the misleading name. + var/sound_global = FALSE + /// Sets a specific custom frequency. sound_frequency_variance must be true as well. If sound_frequency is null, but sound_frequency_variance is true, a semi-random frequency will be chosen to the sound each time. + var/sound_frequency = null + /// Whether or not clients with the ambience preference disabled will hear this sound. + var/sound_is_ambience = TRUE + /// If false, walls will completely muffle the sound. + var/sound_ignore_walls = TRUE + +/obj/abstract/map_effect/interval/sound_emitter/proc/get_sounds_to_play() + return + +/obj/abstract/map_effect/interval/sound_emitter/trigger_map_effect() + playsound( + src, + pick(get_sounds_to_play()), + sound_volume, + sound_frequency_variance, + sound_extra_range, + sound_fallout, + sound_global, + sound_frequency, + sound_is_ambience, + sound_ignore_walls + ) + ..() + +/obj/abstract/map_effect/interval/sound_emitter/footsteps_wood + interval_lower_bound = 5 SECONDS + interval_upper_bound = 30 SECONDS + +/obj/abstract/map_effect/interval/sound_emitter/footsteps_wood/get_sounds_to_play() + var/static/list/sounds_to_play = list( + 'sound/effects/footstep/wood1.ogg', + 'sound/effects/footstep/wood5.ogg', + 'sound/effects/footstep/floor1.ogg', + 'sound/effects/footstep/floor5.ogg' + ) + return sounds_to_play diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm deleted file mode 100644 index ac59e8322d4a..000000000000 --- a/code/game/objects/effects/mines.dm +++ /dev/null @@ -1,98 +0,0 @@ -/obj/effect/mine - name = "Mine" - desc = "I Better stay away from that thing." - density = TRUE - anchored = TRUE - layer = OBJ_LAYER - icon = 'icons/obj/items/weapon/landmine.dmi' - icon_state = "uglymine" - var/triggerproc = PROC_REF(explode) // the proc that's called when the mine is triggered - var/triggered = 0 - -/obj/effect/mine/Initialize() - . = ..() - icon_state = "uglyminearmed" - -/obj/effect/mine/Crossed(atom/movable/AM) - if(!isobserver(AM)) - Bumped(AM) - -/obj/effect/mine/Bumped(mob/M) - - if(triggered) return - - if(ishuman(M)) - visible_message(SPAN_DANGER("\The [M] triggered \the [src]!")) - triggered = 1 - call(src,triggerproc)(M) - -/obj/effect/mine/proc/triggerrad(obj) - spark_at(src, cardinal_only = TRUE) - if(ismob(obj)) - var/mob/victim = obj - victim.radiation += 50 - if(ismob(obj)) - var/mob/mob = obj - mob.add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/disability))) - qdel(src) - -/obj/effect/mine/proc/triggerstun(obj) - if(ismob(obj)) - var/mob/M = obj - SET_STATUS_MAX(M, STAT_STUN, 30) - spark_at(src, cardinal_only = TRUE) - qdel(src) - -/obj/effect/mine/proc/triggern2o(obj) - //example: n2o triggerproc - //note: im lazy - - for (var/turf/target in range(1,src)) - if(target.simulated && !target.blocks_air) - target.assume_gas(/decl/material/gas/nitrous_oxide, 30) - - qdel(src) - -/obj/effect/mine/proc/triggerflame(obj) - for (var/turf/target in range(1,src)) - if(target.simulated && !target.blocks_air) - target.assume_gas(/decl/material/gas/hydrogen, 30) - target.hotspot_expose(1000, CELL_VOLUME) - - qdel(src) - -/obj/effect/mine/proc/triggerkick(obj) - spark_at(src, cardinal_only = TRUE) - if(ismob(obj)) - var/mob/victim = obj - qdel(victim.client) - qdel(src) - -/obj/effect/mine/proc/explode(obj) - explosion(loc, 0, 1, 2, 3) - qdel(src) - -/obj/effect/mine/dnascramble - name = "Radiation Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerrad) - -/obj/effect/mine/flame - name = "Incendiary Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerflame) - -/obj/effect/mine/kick - name = "Kick Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerkick) - -/obj/effect/mine/n2o - name = "N2O Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggern2o) - -/obj/effect/mine/stun - name = "Stun Mine" - icon_state = "uglymine" - triggerproc = PROC_REF(triggerstun) diff --git a/code/game/objects/effects/mines/_mine.dm b/code/game/objects/effects/mines/_mine.dm new file mode 100644 index 000000000000..a377a9d82869 --- /dev/null +++ b/code/game/objects/effects/mines/_mine.dm @@ -0,0 +1,202 @@ +/obj/item/mine + name = "mine" + desc = "A small landmine." + density = FALSE + anchored = FALSE + icon = 'icons/obj/mine.dmi' + icon_state = "mine" + + var/actual_name + var/actual_desc + var/actual_icon_state + var/hidden_alpha = 255 + + var/panel_open = FALSE + var/armed = FALSE + var/triggering = FALSE + var/datum/mine_payload/payload = /datum/mine_payload/explosive + +/obj/item/mine/Initialize() + . = ..() + if(ispath(payload)) + payload = new payload + register_dangerous_to_step() + + // We store and hide our appearance if we're armed, to avoid people gaming mines via desc. + actual_name = name + actual_desc = desc + actual_icon_state = icon_state + update_icon() + +/obj/item/mine/Destroy() + if(istype(payload)) + QDEL_NULL(payload) + unregister_dangerous_to_step() + return ..() + +/obj/item/mine/on_update_icon() + . = ..() + alpha = initial(alpha) + cut_overlays() + if(panel_open) + add_overlay("[icon_state]_open") + else if(armed) + add_overlay("[icon_state]_armed") + alpha = hidden_alpha + else + add_overlay("[icon_state]_safe") + +/obj/item/mine/attack_self(mob/user) // You do not want to move or throw a land mine while priming it... Explosives + Sudden Movement = Bad Times + if(armed) + to_chat(user, SPAN_WARNING("\The [src] is already armed!")) + return TRUE + add_fingerprint(user) + msg_admin_attack("[key_name_admin(user)] armed \the [src]") + user.visible_message( + SPAN_DANGER("\The [user] starts arming \the [src]."), + SPAN_DANGER("You start arming \the [src]. Hold still!") + ) + + if(user.do_skilled(10 SECONDS, SKILL_DEVICES, src)) + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + prime(user) + else if(prob(user.skill_fail_chance(SKILL_DEVICES, 50, SKILL_ADEPT))) + visible_message( + SPAN_DANGER("\The [user] accidentally triggers \the [src]!"), + SPAN_DANGER("You accidentally trigger \the [src]!") + ) + prime(user) + trigger_payload(user) + else + to_chat(user, SPAN_WARNING("You fumble with \the [src], but thankfully manage not to set it off prematurely.")) + return TRUE + +// debug proc, replace with proper disarm minigame +/obj/item/mine/proc/disarm() + armed = FALSE + triggering = FALSE + anchored = FALSE + name = actual_name + desc = actual_desc + icon_state = actual_icon_state + hidden_alpha = 255 + update_icon() + +/obj/item/mine/attack_hand(mob/living/user) + if(armed) + trigger_payload() + return TRUE + return ..() + +/obj/item/mine/attackby(obj/item/W, mob/living/user) + + if(IS_SCREWDRIVER(W)) + if(W.do_tool_interaction(TOOL_SCREWDRIVER, user, src, 15 SECONDS, start_message = "carefully adjusting \the [src]'s casing", check_skill = SKILL_DEVICES)) + panel_open = !panel_open + visible_message(SPAN_NOTICE("\The [user] carefully [(panel_open ? "opens" : "closes")] the casing of \the [src].")) + update_icon() + else if(armed) + if(prob(user.skill_fail_chance(SKILL_DEVICES, 75, SKILL_PROF))) + to_chat(user, SPAN_DANGER("You set off \the [src]!")) + trigger_payload(user) + else + to_chat(user, SPAN_WARNING("You fumble with \the [src], but thankfully manage not to set it off prematurely.")) + return TRUE + + if(armed) + if(panel_open && IS_WIRECUTTER(W)) + if(W.do_tool_interaction(TOOL_WIRECUTTERS, user, src, 30 SECONDS, start_message = "painstakingly disarming \the [src]", check_skill = SKILL_DEVICES)) + visible_message(SPAN_NOTICE("\The [user] disarms \the [src]!")) + disarm() + return TRUE + if(armed) // checking again in case the do_after() stacks + if(prob(user.skill_fail_chance(SKILL_DEVICES, 75, SKILL_PROF))) + to_chat(user, SPAN_DANGER("You set off \the [src]!")) + trigger_payload(user) + else + to_chat(user, SPAN_WARNING("You fumble with \the [src], but thankfully manage not to set it off prematurely.")) + return TRUE + + return ..() + +/obj/item/mine/proc/prime(mob/user) + + if(armed) + return + + if(user) + visible_message(SPAN_NOTICE("\The [src] beeps as the priming sequence completes.")) + user.drop_from_inventory(src, get_turf(user)) + add_fingerprint(user) + + anchored = TRUE + armed = TRUE + + if(istype(loc, /turf/floor) && prob(65)) + var/turf/floor/floor = loc + var/decl/flooring/flooring = floor.get_topmost_flooring() + if(flooring.can_conceal_hazards) + hidden_alpha = pick(50, 90, 120) + + name = "mine" + desc = "A small landmine." + icon_state = "mine" + update_icon() + +/obj/item/mine/forceMove() + var/turf/old_turf = get_turf(loc) + . = ..() + if(.) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf?.unregister_dangerous_object(src) + new_turf?.register_dangerous_object(src) + +/obj/item/mine/Move() + var/turf/old_turf = get_turf(loc) + . = ..() + if(.) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf?.unregister_dangerous_object(src) + new_turf?.register_dangerous_object(src) + +/obj/item/mine/proc/trigger_payload(var/mob/living/M) + if(!triggering && payload && armed) + triggering = TRUE + if(ismob(loc)) + var/mob/holder = loc + holder.drop_from_inventory(src) + visible_message("\The [src] goes off!") + payload.trigger_payload(src, M) + disarm() // the mine can be reused if the payload doesn't destroy it. + return TRUE + return FALSE + +/obj/item/mine/bullet_act() + if(prob(50)) + trigger_payload() + if(!QDELETED(src)) + ..() + +/obj/item/mine/explosion_act(severity) + if(severity <= 2 || prob(50)) + trigger_payload() + if(!QDELETED(src)) + . = ..() + +/obj/item/mine/Crossed(atom/movable/AM) + . = ..() + if(istype(AM) && !AM.is_incorporeal()) + Bumped(AM) + +/obj/item/mine/Bumped(atom/movable/AM) + . = ..() + if(armed && !QDELETED(src) && !is_safe_to_step(AM)) + trigger_payload(AM) + +// This tells AI mobs to not be dumb and step on mines willingly. +/obj/item/mine/is_safe_to_step(mob/living/stepper) + if(!armed) + return TRUE + return !armed || stepper.can_overcome_gravity() diff --git a/code/game/objects/effects/mines/_mine_payload.dm b/code/game/objects/effects/mines/_mine_payload.dm new file mode 100644 index 000000000000..99139be493d7 --- /dev/null +++ b/code/game/objects/effects/mines/_mine_payload.dm @@ -0,0 +1,22 @@ +/datum/mine_payload + var/do_sparks = TRUE + var/destroy_self_on_trigger = TRUE + +/datum/mine_payload/proc/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + if(do_sparks) + var/datum/effect/effect/system/spark_spread/s = new + s.set_up(3, 1, owner) + s.start() + if(destroy_self_on_trigger) + if(!QDELETED(owner)) + QDEL_IN(owner, 1) + else + owner.disarm() // some mines can be reused + +/datum/mine_payload/proc/remove_from_mine() + return + +/datum/mine_payload/explosive/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner] detonates!") + explosion(owner.loc, 0, 2, 3, 4) //land mines are dangerous, folks. diff --git a/code/game/objects/effects/mines/mine_assembly.dm b/code/game/objects/effects/mines/mine_assembly.dm new file mode 100644 index 000000000000..6a40fa0ae30d --- /dev/null +++ b/code/game/objects/effects/mines/mine_assembly.dm @@ -0,0 +1,67 @@ +/obj/item/mine/assembly + name = "mine assembly" + desc = "A small pressure-triggered device. Accepts grenades and tank transfer valves." + payload = null + + var/static/list/accepts_items = list( + /obj/item/transfer_valve = /datum/mine_payload/assembly/tank_transfer_valve, + /obj/item/grenade = /datum/mine_payload/assembly/grenade + ) + +/obj/item/mine/assembly/mapped + armed = TRUE + +/obj/item/mine/assembly/attackby(obj/item/W, mob/living/user) + if(!armed && !triggering) + var/datum/mine_payload/assembly/attached_payload = payload + if(attached_payload?.attached) + if(IS_SCREWDRIVER(W)) + to_chat(user, "You disconnect \the [src]'s [attached_payload.attached.name] and remove it.") + attached_payload.attached.forceMove(get_turf(user)) + payload.remove_from_mine() + QDEL_NULL(payload) + else + for(var/loadtype in accepts_items) + if(istype(W, loadtype)) + user.drop_from_inventory(W) + W.forceMove(src) + var/payload_type = accepts_items[loadtype] + attached_payload = new payload_type + attached_payload.attached = W + payload = attached_payload + return TRUE + return ..() + +/datum/mine_payload/assembly + var/obj/item/attached + +/datum/mine_payload/assembly/New(var/obj/item/_attaching) + ..() + attached = _attaching + +/datum/mine_payload/assembly/Destroy() + QDEL_NULL(attached) + . = ..() + +/datum/mine_payload/assembly/remove_from_mine() + attached = null + +/datum/mine_payload/assembly/tank_transfer_valve/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(istype(attached, /obj/item/transfer_valve)) + var/obj/item/transfer_valve/ttv = attached + ttv.forceMove(get_turf(owner)) + ttv.toggle_valve() + remove_from_mine() + +/datum/mine_payload/assembly/grenade/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(istype(attached, /obj/item/grenade)) + var/obj/item/grenade/grenade = attached + grenade.forceMove(get_turf(owner)) + if(ismob(trigger)) + var/mob/victim = trigger + if(victim.ckey) + msg_admin_attack("[key_name_admin(victim)] stepped on \a [owner], triggering [grenade]") + grenade.activate() + remove_from_mine() diff --git a/code/game/objects/effects/mines/mine_emp.dm b/code/game/objects/effects/mines/mine_emp.dm new file mode 100644 index 000000000000..5f8a54ad12f8 --- /dev/null +++ b/code/game/objects/effects/mines/mine_emp.dm @@ -0,0 +1,12 @@ +/obj/item/mine/emp + name = "\improper EMP mine" + desc = "A small explosive mine with a lightning bolt symbol on the side." + payload = /datum/mine_payload/emp + +/obj/item/mine/emp/mapped + armed = TRUE + +/datum/mine_payload/emp/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner] flashes violently before disintegrating!") + empulse(owner.loc, 2, 4, 7, 10, 1) // As strong as an EMP grenade diff --git a/code/game/objects/effects/mines/mine_frag.dm b/code/game/objects/effects/mines/mine_frag.dm new file mode 100644 index 000000000000..de11402ca5a2 --- /dev/null +++ b/code/game/objects/effects/mines/mine_frag.dm @@ -0,0 +1,23 @@ +/obj/item/mine/frag + name = "fragmentation mine" + desc = "A small explosive mine with 'FRAG' and a grenade symbol on the side." + payload = /datum/mine_payload/frag + +/obj/item/mine/frag/mapped + armed = TRUE + +/datum/mine_payload/frag + var/fragment_types = list(/obj/item/projectile/bullet/pellet/fragment) + var/num_fragments = 20 //total number of fragments produced by the grenade + //The radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern + var/spread_range = 7 + var/explosion_size = 3 + +/datum/mine_payload/frag/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner] detonates!") + var/turf/O = get_turf(owner) + if(O) + owner.fragmentate(O, num_fragments, spread_range, fragment_types) + if(explosion_size) + explosion(O, -1, -1, round(explosion_size/2), explosion_size, FALSE) diff --git a/code/game/objects/effects/mines/mine_incendiary.dm b/code/game/objects/effects/mines/mine_incendiary.dm new file mode 100644 index 000000000000..f8b8a49ab2d6 --- /dev/null +++ b/code/game/objects/effects/mines/mine_incendiary.dm @@ -0,0 +1,16 @@ +/obj/item/mine/incendiary + name = "incendiary mine" + desc = "A small explosive mine with a fire symbol on the side." + payload = /datum/mine_payload/incendiary + +/obj/item/mine/incendiary/mapped + armed = TRUE + +/datum/mine_payload/incendiary/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + for(var/turf/floor/target in range(1, owner)) + if(!target.blocks_air) + target.assume_gas(/decl/material/gas/hydrogen, 10) + target.assume_gas(/decl/material/gas/oxygen, 5) + target.hotspot_expose(1000, CELL_VOLUME) + owner.visible_message("\The [owner] spews a cloud of flaming gas!") diff --git a/code/game/objects/effects/mines/mine_kick.dm b/code/game/objects/effects/mines/mine_kick.dm new file mode 100644 index 000000000000..ab38a2a2fb31 --- /dev/null +++ b/code/game/objects/effects/mines/mine_kick.dm @@ -0,0 +1,17 @@ +/obj/item/mine/kick + name = "kick mine" + desc = "Concentrated war crimes. Handle with care." + payload = /datum/mine_payload/kick + +/obj/item/mine/kick/mapped + armed = TRUE + +/datum/mine_payload/kick/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(isexosuit(trigger)) + var/mob/living/exosuit/mech = trigger + for(var/mob/pilot in mech.pilots) + qdel(pilot.client) + if(ismob(trigger)) + var/mob/M = trigger + qdel(M.client) diff --git a/code/game/objects/effects/mines/mine_napalm.dm b/code/game/objects/effects/mines/mine_napalm.dm new file mode 100644 index 000000000000..6bceace3f0e6 --- /dev/null +++ b/code/game/objects/effects/mines/mine_napalm.dm @@ -0,0 +1,15 @@ +/obj/item/mine/napalm + name = "napalm mine" + desc = "A small explosive mine with a fire symbol on the side." + payload = /datum/mine_payload/napalm + +/obj/item/mine/napalm/mapped + armed = TRUE + +/datum/mine_payload/napalm/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(isliving(trigger)) + var/mob/living/M = trigger + M.adjust_fire_intensity(5) + M.fire_act() + owner.visible_message(SPAN_DANGER("\The [owner] bursts into flames!")) diff --git a/code/game/objects/effects/mines/mine_radiation.dm b/code/game/objects/effects/mines/mine_radiation.dm new file mode 100644 index 000000000000..08b00d2a6594 --- /dev/null +++ b/code/game/objects/effects/mines/mine_radiation.dm @@ -0,0 +1,14 @@ +/obj/item/mine/radiation + name = "radiation mine" + desc = "A small explosive mine with a radiation symbol on the side." + payload = /datum/mine_payload/radiation + +/obj/item/mine/radiation/mapped + armed = TRUE + +/datum/mine_payload/radiation/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(isliving(trigger)) + var/mob/living/victim = trigger + victim.apply_random_mutation(50) + owner.visible_message(SPAN_DANGER("\The [owner] flashes violently before disintegrating!")) diff --git a/code/game/objects/effects/mines/mine_sleeping.dm b/code/game/objects/effects/mines/mine_sleeping.dm new file mode 100644 index 000000000000..b6d5ad9a0dee --- /dev/null +++ b/code/game/objects/effects/mines/mine_sleeping.dm @@ -0,0 +1,14 @@ +/obj/item/mine/sleeping + name = "nitrous oxide mine" + desc = "A small explosive mine with three Z's on the side." + payload = /datum/mine_payload/sleeping + +/obj/item/mine/sleeping/mapped + armed = TRUE + +/datum/mine_payload/sleeping/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + for (var/turf/floor/target in range(1, owner)) + if(!target.blocks_air) + target.assume_gas(/decl/material/gas/nitrous_oxide, 30) + owner.visible_message("\The [owner] sprays a cloud of gas!") diff --git a/code/game/objects/effects/mines/mine_stun.dm b/code/game/objects/effects/mines/mine_stun.dm new file mode 100644 index 000000000000..26b5704b304b --- /dev/null +++ b/code/game/objects/effects/mines/mine_stun.dm @@ -0,0 +1,14 @@ +/obj/item/mine/stun + name = "stun mine" + desc = "A small explosive mine with a lightning bolt symbol on the side." + payload = /datum/mine_payload/stun + +/obj/item/mine/stun/mapped + armed = TRUE + +/datum/mine_payload/stun/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + if(ismob(trigger)) + var/mob/M = trigger + SET_STATUS_MAX(M, STAT_STUN, 30) + owner.visible_message("\The [owner] flashes violently before disintegrating!") diff --git a/code/game/objects/effects/mines/mine_training.dm b/code/game/objects/effects/mines/mine_training.dm new file mode 100644 index 000000000000..13b23f31945c --- /dev/null +++ b/code/game/objects/effects/mines/mine_training.dm @@ -0,0 +1,15 @@ +/obj/item/mine/training + name = "training mine" + desc = "A mine with its payload removed, for EOD training and demonstrations." + payload = /datum/mine_payload/training + +/obj/item/mine/training/mapped + armed = TRUE + +/datum/mine_payload/training + do_sparks = FALSE + destroy_self_on_trigger = FALSE + +/datum/mine_payload/training/trigger_payload(var/obj/item/mine/owner, var/atom/trigger) + ..() + owner.visible_message("\The [owner]'s light flashes rapidly as it 'explodes'.") diff --git a/code/game/objects/effects/temporary.dm b/code/game/objects/effects/temporary.dm index b56d38275f17..c5efa1c7e916 100644 --- a/code/game/objects/effects/temporary.dm +++ b/code/game/objects/effects/temporary.dm @@ -1,6 +1,7 @@ //temporary visual effects /obj/effect/temp_visual icon_state = "nothing" + icon = 'icons/effects/effects.dmi' anchored = TRUE layer = ABOVE_HUMAN_LAYER mouse_opacity = MOUSE_OPACITY_UNCLICKABLE @@ -14,12 +15,10 @@ QDEL_IN(src, duration) /obj/effect/temp_visual/emp_burst - icon = 'icons/effects/effects.dmi' icon_state = "empdisable" /obj/effect/temp_visual/emppulse name = "electromagnetic pulse" - icon = 'icons/effects/effects.dmi' icon_state = "emppulse" duration = 2 SECONDS @@ -49,4 +48,52 @@ target_pixel_x = 16 if(set_dir & WEST) target_pixel_x = -16 - animate(src, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = duration) \ No newline at end of file + animate(src, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = duration) + +/obj/effect/temp_visual/impact_effect + plane = ABOVE_LIGHTING_PLANE + layer = ABOVE_LIGHTING_LAYER // So they're visible even in a shootout in maint. + duration = 5 + icon_state = "impact_bullet" + icon = 'icons/effects/impact_effects.dmi' + +/obj/effect/temp_visual/impact_effect/Initialize(mapload, obj/item/projectile/P, _x, _y) + default_pixel_x = _x + default_pixel_y = _y + pixel_x = default_pixel_x + pixel_y = default_pixel_y + . = ..() + +/obj/effect/temp_visual/impact_effect/red_laser + icon_state = "impact_laser" + duration = 4 + +/obj/effect/temp_visual/impact_effect/blue_laser + icon_state = "impact_laser_blue" + duration = 4 + +/obj/effect/temp_visual/impact_effect/green_laser + icon_state = "impact_laser_green" + duration = 4 + +/obj/effect/temp_visual/impact_effect/purple_laser + icon_state = "impact_laser_purple" + duration = 4 + +// Colors itself based on the projectile. +// Checks light_color and color. +/obj/effect/temp_visual/impact_effect/monochrome_laser + icon_state = "impact_laser_monochrome" + duration = 4 + +/obj/effect/temp_visual/impact_effect/monochrome_laser/Initialize(mapload, obj/item/projectile/P, x, y) + if(istype(P)) + if(P.light_color) + color = P.light_color + else if(P.color) + color = P.color + return ..() + +/obj/effect/temp_visual/impact_effect/ion + icon_state = "shieldsparkles" + duration = 6 diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index fe665eacab52..2b33e1440cb0 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -1,6 +1,7 @@ var/global/list/all_gps_units = list() /obj/item/gps - name = "global coordinate system" + name = "global positioning system" + base_name = "global positioning system" desc = "A handheld relay used to triangulate the approximate coordinates of the device in spacetime." icon = 'icons/obj/items/device/locator.dmi' icon_state = ICON_STATE_WORLD @@ -24,21 +25,36 @@ var/global/list/all_gps_units = list() var/can_hide_signal = FALSE // If it can toggle the above var. var/is_special_gps_marker = FALSE // How the GPS marker should be handled. + var/tag_category // Any special category for this tracker to sit in (used by xenofauna tags) + var/list/tag_categories // Any special categories this tracker should show (used in xenofauna GPS) + var/mob/holder var/is_in_processing_list = FALSE var/list/tracking_devices var/list/showing_tracked_names - var/obj/compass_holder/compass + VAR_PRIVATE/obj/compass_holder/_compass var/list/decals + /obj/item/gps/Initialize() global.all_gps_units += src . = ..() - name = "[initial(name)] ([gps_tag])" events_repository.register(/decl/observ/moved, src, src, PROC_REF(update_holder)) - compass = new(src) + create_compass() update_holder() update_icon() + update_name() + +/obj/item/gps/proc/set_gps_tag(_tag) + _tag = sanitize(_tag) + if(istext(_tag) && length(_tag) > 0 && gps_tag != _tag) + gps_tag = _tag + update_name() + +/obj/item/gps/update_name() + . = ..() + if(gps_tag) + SetName("[base_name] ([gps_tag])") /obj/item/gps/get_examine_strings(mob/user, distance, infix, suffix) . = ..() @@ -60,7 +76,8 @@ var/global/list/all_gps_units = list() if(holder && (force_clear || loc != holder)) moved_event.unregister(holder, src) dir_set_event.unregister(holder, src) - holder.client?.screen -= compass + if(_compass) + holder.client?.screen -= _compass holder = null if(!force_clear && ismob(loc)) @@ -72,16 +89,16 @@ var/global/list/all_gps_units = list() if(!is_in_processing_list) START_PROCESSING(SSobj, src) is_in_processing_list = TRUE - if(holder.client) + if(holder.client && _compass) if(check_visible_to_holder()) - holder.client.screen |= compass + holder.client.screen |= _compass else - holder.client.screen -= compass + holder.client.screen -= _compass else STOP_PROCESSING(SSobj, src) is_in_processing_list = FALSE - if(holder?.client) - holder.client.screen -= compass + if(holder?.client && _compass) + holder.client.screen -= _compass /obj/item/gps/equipped_robot() . = ..() @@ -105,13 +122,12 @@ var/global/list/all_gps_units = list() global.all_gps_units -= src events_repository.unregister(/decl/observ/moved, src, src, PROC_REF(update_holder)) update_holder(force_clear = TRUE) - QDEL_NULL(compass) + QDEL_NULL(_compass) return ..() /obj/item/gps/proc/can_track(var/obj/item/gps/other, var/reachable_z_levels) - if(!other.tracking || other.emped || other.hide_signal) + if(!other.tracking || other.emped || other.hide_signal || (other.tag_category && !(other.tag_category in tag_categories))) return FALSE - var/turf/origin = get_turf(src) var/turf/target = get_turf(other) if(!istype(origin) || !istype(target)) @@ -131,9 +147,12 @@ var/global/list/all_gps_units = list() LAZYDISTINCTADD(reachable_z_levels, adding_sites) return (target.z in reachable_z_levels) +/obj/item/gps/proc/create_compass() + _compass ||= new(src) + /obj/item/gps/proc/update_compass(var/update_compass_icon) - compass.hide_waypoints(FALSE) + _compass?.hide_waypoints(FALSE) var/turf/my_turf = get_turf(src) for(var/thing in tracking_devices) @@ -143,14 +162,15 @@ var/global/list/all_gps_units = list() LAZYREMOVE(showing_tracked_names, thing) continue - var/turf/gps_turf = get_turf(gps) - var/gps_tag = LAZYACCESS(showing_tracked_names, thing) ? gps.gps_tag : null - if(istype(gps_turf)) - compass.set_waypoint("\ref[gps]", gps_tag, gps_turf.x, gps_turf.y, gps_turf.z, LAZYACCESS(tracking_devices, "\ref[gps]")) - if(can_track(gps) && my_turf && gps_turf != my_turf) - compass.show_waypoint("\ref[gps]") + if(_compass) + var/turf/gps_turf = get_turf(gps) + var/use_gps_tag = LAZYACCESS(showing_tracked_names, thing) ? gps.gps_tag : null + if(istype(gps_turf)) + _compass.set_waypoint("\ref[gps]", use_gps_tag, gps_turf.x, gps_turf.y, gps_turf.z, LAZYACCESS(tracking_devices, "\ref[gps]")) + if(can_track(gps) && my_turf && gps_turf != my_turf) + _compass.show_waypoint("\ref[gps]") - compass.rebuild_overlay_lists(update_compass_icon) + _compass?.rebuild_overlay_lists(update_compass_icon) /obj/item/gps/proc/toggle_tracking(var/mob/user, var/silent) @@ -321,7 +341,7 @@ var/global/list/all_gps_units = list() if(href_list["stop_track"]) var/gps_ref = href_list["stop_track"] var/obj/item/gps/gps = locate(gps_ref) - compass.clear_waypoint(gps_ref) + _compass?.clear_waypoint(gps_ref) LAZYREMOVE(tracking_devices, gps_ref) LAZYREMOVE(showing_tracked_names, gps_ref) if(istype(gps) && !QDELETED(gps)) @@ -353,8 +373,7 @@ var/global/list/all_gps_units = list() var/a = input("Please enter desired tag.", name, gps_tag) as text a = uppertext(copytext(sanitize(a), 1, 11)) if(CanInteract(user, topic_state)) - gps_tag = a - name = "[initial(name)] ([gps_tag])" + set_gps_tag(a) to_chat(user, SPAN_NOTICE("You set your GPS's tag to '[gps_tag]'.")) . = TOPIC_REFRESH @@ -398,6 +417,7 @@ var/global/list/all_gps_units = list() // Department subtypes. /obj/item/gps/mining + gps_tag = "MIN0" color = "#c08f45" decals = list( "stripe-outside" = "#702e98", @@ -405,6 +425,7 @@ var/global/list/all_gps_units = list() ) /obj/item/gps/science + gps_tag = "SCI0" color = "#dbcfdf" decals = list( "stripe-outside" = "#cc33ff", @@ -412,17 +433,25 @@ var/global/list/all_gps_units = list() ) /obj/item/gps/medical + gps_tag = "MED0" color = "#ebebeb" decals = list( "stripe-outside" = "#6ab8fe", "stripe-inside" = "#339efe" ) -/obj/item/gps/explorer - color = "#4a4a4a" +/obj/item/gps/security + gps_tag = "SEC0" + color = "#5c0000" decals = list( - "stripe-outside" = "#500677", - "stripe-inside" = "#68099e" + "stripe-outside" = "#ff0000", + "stripe-inside" = "#800000" + ) + +/obj/item/gps/security/hos + decals = list( + "stripe-outside" = "#ffae00", + "stripe-inside" = "#9e7900" ) /obj/item/gps/xenofauna diff --git a/code/game/objects/items/devices/radio/headsets_shared.dm b/code/game/objects/items/devices/radio/headsets_shared.dm index ed6bfeb87d4f..475a5e515909 100644 --- a/code/game/objects/items/devices/radio/headsets_shared.dm +++ b/code/game/objects/items/devices/radio/headsets_shared.dm @@ -140,8 +140,7 @@ access_research, access_medical, access_cargo, - access_bar, - access_explorer + access_bar ) /obj/item/radio/headset/heads/hop @@ -156,8 +155,7 @@ access_cargo, access_bridge, access_security, - access_mining, - access_explorer + access_mining ) /obj/item/radio/headset/heads/hos diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index cbe87ec2d299..f2a7ee2d6d44 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -8,14 +8,14 @@ /obj/item/borg/overdrive name = "overdrive" icon = 'icons/obj/signs/warnings.dmi' - icon_state = "shock" + icon_state = "shock-large" /********************************************************************** HUD/SIGHT things ***********************************************************************/ /obj/item/borg/sight icon = 'icons/obj/signs/warnings.dmi' - icon_state = "secureareaold" + icon_state = "securearea-large" var/sight_mode = null var/glasses_hud_type diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm index fc19d7e17dd6..20fec64b1b05 100644 --- a/code/game/objects/items/weapons/material/knives.dm +++ b/code/game/objects/items/weapons/material/knives.dm @@ -129,4 +129,4 @@ name = "lightweight utility knife" desc = "A lightweight utility knife made out of a titanium alloy." material = /decl/material/solid/metal/titanium - draw_handle = FALSE \ No newline at end of file + draw_handle = FALSE diff --git a/code/game/objects/items/weapons/material/swiss.dm b/code/game/objects/items/weapons/material/swiss.dm index 33db97ea49cd..b91369cf2356 100644 --- a/code/game/objects/items/weapons/material/swiss.dm +++ b/code/game/objects/items/weapons/material/swiss.dm @@ -1,14 +1,3 @@ -#define SWISSKNF_CLOSED "Close" -#define SWISSKNF_LBLADE "Large Blade" -#define SWISSKNF_SBLADE "Small Blade" -#define SWISSKNF_CLIFTER "Cap Lifter-Screwdriver" -#define SWISSKNF_COPENER "Can Opener-Screwdriver" -#define SWISSKNF_CSCREW "Corkscrew" -#define SWISSKNF_GBLADE "Glass Cutter" -#define SWISSKNF_WCUTTER "Wirecutters" -#define SWISSKNF_WBLADE "Wood Saw" -#define SWISSKNF_CROWBAR "Pry Bar" - /obj/item/knife/folding/swiss name = "combi-knife" desc = "A small, colourable, multi-purpose folding knife." @@ -17,6 +6,17 @@ material = /decl/material/solid/metal/steel material_alteration = MAT_FLAG_ALTERATION_COLOR | MAT_FLAG_ALTERATION_NAME + var/const/SWISSKNF_CLOSED = "Close" + var/const/SWISSKNF_LBLADE = "Large Blade" + var/const/SWISSKNF_SBLADE = "Small Blade" + var/const/SWISSKNF_CLIFTER = "Cap Lifter-Screwdriver" + var/const/SWISSKNF_COPENER = "Can Opener-Screwdriver" + var/const/SWISSKNF_CSCREW = "Corkscrew" + var/const/SWISSKNF_GBLADE = "Glass Cutter" + var/const/SWISSKNF_WCUTTER = "Wirecutters" + var/const/SWISSKNF_WBLADE = "Wood Saw" + var/const/SWISSKNF_CROWBAR = "Pry Bar" + var/active_tool = SWISSKNF_CLOSED var/list/tools = list(SWISSKNF_LBLADE, SWISSKNF_CLIFTER, SWISSKNF_COPENER) var/static/list/sharp_tools = list(SWISSKNF_LBLADE, SWISSKNF_SBLADE, SWISSKNF_GBLADE, SWISSKNF_WBLADE) @@ -151,25 +151,8 @@ handle_color = COLOR_AMBER tools = list(SWISSKNF_LBLADE, SWISSKNF_SBLADE, SWISSKNF_CLIFTER, SWISSKNF_COPENER, SWISSKNF_WBLADE, SWISSKNF_WCUTTER) -/obj/item/knife/folding/swiss/explorer - name = "explorer's combi-knife" - desc = "A small, purple, multi-purpose folding knife. This one adds a wood saw and prybar." - handle_color = COLOR_PURPLE - tools = list(SWISSKNF_LBLADE, SWISSKNF_SBLADE, SWISSKNF_CLIFTER, SWISSKNF_COPENER, SWISSKNF_WBLADE, SWISSKNF_CROWBAR) - /obj/item/knife/folding/swiss/loot name = "black combi-knife" desc = "A small, silver, multi-purpose folding knife. This one adds a small blade and corkscrew." handle_color = COLOR_GRAY40 tools = list(SWISSKNF_LBLADE, SWISSKNF_SBLADE, SWISSKNF_CLIFTER, SWISSKNF_COPENER, SWISSKNF_CSCREW) - -#undef SWISSKNF_CLOSED -#undef SWISSKNF_LBLADE -#undef SWISSKNF_SBLADE -#undef SWISSKNF_CLIFTER -#undef SWISSKNF_COPENER -#undef SWISSKNF_CSCREW -#undef SWISSKNF_GBLADE -#undef SWISSKNF_WCUTTER -#undef SWISSKNF_WBLADE -#undef SWISSKNF_CROWBAR diff --git a/code/game/objects/random/subtypes/mobs.dm b/code/game/objects/random/subtypes/mobs.dm index 03e86f8f5f4c..d26977a5bf19 100644 --- a/code/game/objects/random/subtypes/mobs.dm +++ b/code/game/objects/random/subtypes/mobs.dm @@ -49,3 +49,26 @@ /mob/living/simple_animal/hostile/scarybat/cave = 4 ) return spawnable_choices + +/obj/random/hostile/hivebot + name = "Random Hivebot" + icon = /mob/living/simple_animal/hostile/hivebot::icon + icon_state = /mob/living/simple_animal/hostile/hivebot::icon_state + +/obj/random/hostile/hivebot/spawn_choices() + var/static/list/spawnable_choices = typesof(/mob/living/simple_animal/hostile/hivebot) + return spawnable_choices + +/obj/random/hostile/hivebot/melee + name = "Random Melee Hivebot" + +/obj/random/hostile/hivebot/melee/spawn_choices() + var/static/list/spawnable_choices = typesof(/mob/living/simple_animal/hostile/hivebot/melee) + return spawnable_choices + +/obj/random/hostile/hivebot/ranged + name = "Random Ranged Hivebot" + +/obj/random/hostile/hivebot/ranged/spawn_choices() + var/static/list/spawnable_choices = typesof(/mob/living/simple_animal/hostile/hivebot/ranged) + return spawnable_choices diff --git a/code/game/objects/random/subtypes/salvage.dm b/code/game/objects/random/subtypes/salvage.dm new file mode 100644 index 000000000000..298557addcac --- /dev/null +++ b/code/game/objects/random/subtypes/salvage.dm @@ -0,0 +1,128 @@ +/obj/random/scrapped_gun + name = "random scrapped gun" + icon = /obj/item/gun/projectile/automatic/assault_rifle::icon + icon_state = /obj/item/gun/projectile/automatic/assault_rifle::icon_state + +/obj/random/scrapped_gun/spawn_choices() + var/static/list/spawn_choices = list( + /obj/random/scrapped_pistol = 10, + /obj/random/scrapped_smg = 5, + /obj/random/scrapped_laser = 5, + /obj/random/scrapped_shotgun = 3, + /obj/random/scrapped_ionrifle = 3, + /obj/random/scrapped_assault = 1, + /obj/random/scrapped_flechette = 1, + /obj/random/scrapped_grenadelauncher = 1, + /obj/random/scrapped_dartgun = 1 + ) + return spawn_choices + +/obj/random/scrapped_assault + name = "random scrapped assault rifle" + icon = /obj/item/gun/projectile/automatic/assault_rifle::icon + icon_state = /obj/item/gun/projectile/automatic/assault_rifle::icon_state + +/obj/random/scrapped_assault/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/ballistic/assault = 10, + /obj/item/gun/projectile/automatic/assault_rifle = 1 + ) + return spawn_choices + +/obj/random/scrapped_pistol + name = "random scrapped pistol" + icon = /obj/item/gun/projectile/pistol::icon + icon_state = /obj/item/gun/projectile/pistol::icon_state + +/obj/random/scrapped_pistol/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/ballistic/pistol = 10, + /obj/item/gun/projectile/pistol = 1 + ) + return spawn_choices + +/obj/random/scrapped_ionrifle + name = "random scrapped ion rifle" + icon = /obj/item/gun/energy/ionrifle::icon + icon_state = /obj/item/gun/energy/ionrifle::icon_state + +/obj/random/scrapped_ionrifle/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/energy/ionrifle = 10, + /obj/item/gun/energy/ionrifle = 1 + ) + return spawn_choices + +/obj/random/scrapped_grenadelauncher + name = "random scrapped grenade launcher" + icon = /obj/item/gun/launcher/grenade::icon + icon_state = /obj/item/gun/launcher/grenade::icon_state + +/obj/random/scrapped_grenadelauncher/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/launcher/grenade = 10, + /obj/item/gun/launcher/grenade = 1 + ) + return spawn_choices + +/obj/random/scrapped_laser + name = "random scrapped laser rifle" + icon = /obj/item/gun/energy/laser::icon + icon_state = /obj/item/gun/energy/laser::icon_state + +/obj/random/scrapped_laser/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/energy/laserrifle = 10, + /obj/item/gun/energy/laser = 1 + ) + return spawn_choices + +/obj/random/scrapped_smg + name = "random scrapped submachine gun" + icon = /obj/item/gun/projectile/automatic/smg::icon + icon_state = /obj/item/gun/projectile/automatic/smg::icon_state + +/obj/random/scrapped_smg/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/ballistic/smg = 10, + /obj/item/gun/projectile/automatic/smg = 1 + ) + return spawn_choices + +/obj/random/scrapped_shotgun + name = "random scrapped shotgun" + icon = /obj/item/gun/projectile/shotgun/pump::icon + icon_state = /obj/item/gun/projectile/shotgun/pump::icon_state + +/obj/random/scrapped_shotgun/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/ballistic/shotgun_pump = 10, + /obj/item/salvage/ballistic/shotgun_doublebarrel = 10, + /obj/item/gun/projectile/shotgun/pump = 1, + /obj/item/gun/projectile/shotgun/doublebarrel = 1 + ) + return spawn_choices + +/obj/random/scrapped_dartgun + name = "random scrapped dartgun" + icon = /obj/item/gun/projectile/dartgun::icon + icon_state = /obj/item/gun/projectile/dartgun::icon_state + +/obj/random/scrapped_dartgun/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/launcher/dartgun = 10, + /obj/item/gun/projectile/dartgun = 1 + ) + return spawn_choices + +/obj/random/scrapped_flechette + name = "random scrapped flechette rifle" + icon = /obj/item/gun/magnetic/railgun/flechette::icon + icon_state = /obj/item/gun/magnetic/railgun/flechette::icon_state + +/obj/random/scrapped_flechette/spawn_choices() + var/static/list/spawn_choices = list( + /obj/item/salvage/magnetic/flechette = 10, + /obj/item/gun/magnetic/railgun/flechette = 1 + ) + return spawn_choices diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index 3f5b94ea0716..8598d9db8d55 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -60,7 +60,6 @@ /obj/structure/catwalk/can_climb_from_below(var/mob/climber) return TRUE - /obj/structure/catwalk/proc/redraw_nearby_catwalks() for(var/direction in global.alldirs) var/obj/structure/catwalk/L = locate() in get_step(src, direction) diff --git a/code/game/objects/structures/cliffs.dm b/code/game/objects/structures/cliffs.dm new file mode 100644 index 000000000000..5745913052ab --- /dev/null +++ b/code/game/objects/structures/cliffs.dm @@ -0,0 +1,300 @@ +/* +Cliffs give a visual illusion of depth by separating two places while presenting a 'top' and 'bottom' side. + +Ported from Polaris. + +Mobs moving into a cliff from the bottom side will simply bump into it and be denied moving into the tile, +where as mobs moving into a cliff from the top side will 'fall' off the cliff, forcing them to the bottom, causing significant damage and stunning them. + +Mobs can climb this while wearing climbing equipment by clickdragging themselves onto a cliff, as if it were a table. + +Flying mobs can pass over all cliffs with no risk of falling. + +Projectiles and thrown objects can pass, however if moving upwards, there is a chance for it to be stopped by the cliff. +This makes fighting something that is on top of a cliff more challenging. + +As a note, dir points upwards, e.g. pointing WEST means the left side is 'up', and the right side is 'down'. + +When mapping these in, be sure to give at least a one tile clearance, as NORTH facing cliffs expand to +two tiles on initialization, and which way a cliff is facing may change during maploading. +*/ + +/obj/structure/cliff + name = "cliff" + desc = "A steep rock ledge. You might be able to climb it if you feel bold enough." + icon = 'icons/obj/structures/cliffs.dmi' + anchored = TRUE + density = TRUE + opacity = FALSE + atom_flags = ATOM_FLAG_CLIMBABLE + appearance_flags = KEEP_TOGETHER + climb_speed_mult = 2 + + var/icon_variant = null // Used to make cliffs less repetitive by having a selection of sprites to display. + var/corner = FALSE // Used for icon things. + var/ramp = FALSE // Ditto. + var/bottom = FALSE // Used for 'bottom' typed cliffs, to avoid infinite cliffs, and for icons. + + var/is_double_cliff = FALSE // Set to true when making the two-tile cliffs, used for projectile checks. + var/uphill_penalty = 30 // Odds of a projectile not making it up the cliff. + +/obj/structure/cliff/Initialize() + . = ..() + register_dangerous_to_step() + +/obj/structure/cliff/Destroy() + unregister_dangerous_to_step() + if(is_double_cliff && !bottom) + var/turf/other = get_step(src, SOUTH) + if(istype(other)) + for(var/obj/structure/cliff/bottom/bottom in other) + qdel(bottom) + . = ..() + +/obj/structure/cliff/get_examine_hints(mob/user, distance, infix, suffix) + . = ..() + var/static/desc_string = "Walking off the edge of a cliff while on top will cause you to fall off, causing severe injury.
\ + You can climb this cliff if wearing special climbing equipment, by click-dragging yourself onto the cliff.
\ + Projectiles traveling up a cliff may hit the cliff instead, making it more difficult to fight something \ + on top." + LAZYADD(., desc_string) + +/obj/structure/cliff/Move() + var/turf/old_turf = get_turf(src) + . = ..() + if(.) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf.unregister_dangerous_object(src) + new_turf.register_dangerous_object(src) + +// These arrange their sprites at runtime, as opposed to being statically placed in the map file. +/obj/structure/cliff/automatic + icon_state = "cliffbuilder" + dir = NORTH + +/obj/structure/cliff/automatic/corner + icon_state = "cliffbuilder-corner" + dir = NORTHEAST + corner = TRUE + +// Tiny part that doesn't block, used for making 'ramps'. +/obj/structure/cliff/automatic/ramp + icon_state = "cliffbuilder-ramp" + dir = NORTHEAST + density = FALSE + ramp = TRUE + +// Made automatically as needed by automatic cliffs. +/obj/structure/cliff/bottom + bottom = TRUE + is_spawnable_type = FALSE + +/obj/structure/cliff/automatic/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +// Paranoid about the maploader, direction is very important to cliffs, since they may get bigger if initialized while facing NORTH. +/obj/structure/cliff/automatic/LateInitialize() + if(dir in global.cardinal) + icon_variant = pick("a", "b", "c") + + if(dir & NORTH && !bottom) // North-facing cliffs require more cliffs to be made. + make_bottom() + + update_icon() + +/obj/structure/cliff/proc/make_bottom() + // First, make sure there's room to put the bottom side. + var/turf/turf = locate(x, y - 1, z) + if(!istype(turf)) + return FALSE + + // Now make the bottom cliff have mostly the same variables. + var/obj/structure/cliff/bottom/bottom_cliff = new(turf) + is_double_cliff = TRUE + climb_speed_mult /= 2 // Since there are two cliffs to climb when going north, both take half the time. + + bottom_cliff.dir = dir + bottom_cliff.is_double_cliff = TRUE + bottom_cliff.climb_speed_mult = climb_speed_mult + bottom_cliff.icon_variant = icon_variant + bottom_cliff.corner = corner + bottom_cliff.ramp = ramp + bottom_cliff.layer = layer - 0.1 + bottom_cliff.density = density + bottom_cliff.update_icon() + +/obj/structure/cliff/set_dir(new_dir) + ..() + update_icon() + +/obj/structure/cliff/on_update_icon() + icon_state = "cliff-[dir][icon_variant][bottom ? "-bottom" : ""][corner ? "-corner" : ""][ramp ? "-ramp" : ""]" + + // Now for making the top-side look like a different turf. + var/turf/turf = get_step(src, dir) + if(!istype(turf)) + return + + underlays.Cut() + var/subtraction_icon_state = "[icon_state]-subtract" + if(turf && (check_state_in_icon(subtraction_icon_state, icon))) + var/image/subtract = image(icon, subtraction_icon_state) + subtract.blend_mode = BLEND_SUBTRACT + underlays += subtract + +// Movement-related code. +/obj/structure/cliff/CanPass(atom/movable/mover, turf/target) + if(isliving(mover)) + var/mob/living/faller = mover + if(faller.can_overcome_gravity()) // Flying mobs can always pass. + return TRUE + return ..() + + else if(!istype(mover, /obj/item/projectile) && !mover.throwing) // 'sliding' objects can fall / bump into cliffs. + return ..() + + // Projectiles and objects flying 'upward' have a chance to hit the cliff instead, wasting the shot. + else if(istype(mover, /obj)) + var/obj/O = mover + if(check_shield_arc(src, dir, O)) // This is actually for mobs but it will work for our purposes as well. + if(prob(uphill_penalty / (1 + is_double_cliff) )) // Firing upwards facing NORTH means it will likely have to pass through two cliffs, so the chance is halved. + return FALSE + return TRUE + +/obj/structure/cliff/Bumped(atom/movable/mover) + if(!istype(mover, /obj/item/projectile) && !mover.throwing && should_fall(mover)) + fall_off_cliff(mover) + return + ..() + +/obj/structure/cliff/proc/should_fall(atom/movable/mover) + if(isliving(mover)) + var/mob/living/faller = mover + if(faller.can_overcome_gravity()) + return FALSE + var/turf/turf = get_turf(mover) + if(turf && get_dir(turf, loc) & global.reverse_dir[dir]) // dir points 'up' the cliff, e.g. cliff pointing NORTH will cause someone to fall if moving SOUTH into it. + return TRUE + return FALSE + +/obj/structure/cliff/proc/fall_off_cliff(atom/movable/mover) + . = FALSE + + var/mob/living/faller + if(isliving(mover)) + faller = mover + var/turf/turf = get_step(src, global.reverse_dir[dir]) + var/displaced = FALSE + + if(dir in list(EAST, WEST)) // Apply an offset if flying sideways, to help maintain the illusion of depth. + for(var/i = 1 to 2) + var/turf/new_T = locate(turf.x, turf.y - i, turf.z) + if(!new_T || locate(/obj/structure/cliff) in new_T) + break + turf = new_T + displaced = TRUE + + if(!istype(turf)) + return + + var/safe_fall = FALSE + if(istype(faller)) + safe_fall = faller.can_overcome_gravity() + else if(istype(mover, /obj/vehicle/bike)) + var/obj/vehicle/bike/Bi = mover + if(Bi.on) + safe_fall = TRUE + + // Buckled people can't react to save themselves, if they're not on a vehicle. + if(!istype(mover, /obj/vehicle) && !isexosuit(mover) && !faller && mover.buckled_mob) + faller = mover.buckled_mob + + if(safe_fall) + visible_message(SPAN_NOTICE("\The [mover] glides down from \the [src].")) + else + visible_message(SPAN_DANGER("\The [mover] falls off \the [src]!")) + + mover.forceMove(turf) + + var/harm = !is_double_cliff ? 1 : 0.5 + if(!safe_fall) + // Do the actual hurting. Double cliffs do halved damage due to them most likely hitting twice. + if(faller) + SET_STATUS_MAX(faller, STAT_WEAK, (5 * harm)) + + if(istype(mover, /obj/vehicle)) + var/obj/vehicle/vehicle = mover + vehicle.take_damage(40 * harm) + vehicle.visible_message(SPAN_WARNING("\The [vehicle] absorbs some of the impact, damaging it.")) + harm = round(harm * 0.5) + if(vehicle.buckled_mob) + var/damage = clamp(vehicle.buckled_mob.get_max_health() * 0.4, 20, 100) + vehicle.buckled_mob.take_damage(damage * harm, BRUTE, inflicter = src) + shake_camera(vehicle.buckled_mob, 1, 1) + else if(isexosuit(mover)) + var/mob/living/exosuit/Mech = mover + harm = round(harm * 0.5) + var/list/passengers = list() + for(var/mob/living/passenger in Mech.pilots) + passengers |= passenger + passenger.take_damage(clamp(faller.get_max_health() * 0.4, 10, 50) * harm, BRUTE, inflicter = src) + shake_camera(passenger, 1, 1) + to_chat(passenger, SPAN_DANGER("\The [Mech] shakes, bouncing you violently!")) + Mech.take_damage(clamp(Mech.get_max_health() * 0.4 * harm, 50, 300)) + if(QDELETED(Mech) && length(passengers)) // Damage caused the mech to explode, or otherwise vanish. + for(var/mob/living/victim in passengers) + to_chat(victim, SPAN_DANGER("The exosuit shears apart around you, throwing you from the debris!")) + victim.throw_at_random(FALSE,2,1, 32) + + playsound(mover, 'sound/effects/break_stone.ogg', 70, 1) + + var/fall_time = 3 + if(displaced) // Make the fall look more natural when falling sideways. + mover.pixel_z = 32 * 2 + animate(mover, pixel_z = 0, time = fall_time) + + sleep(fall_time) // A brief delay inbetween the two sounds helps sell the 'ouch' effect. + + if(QDELETED(src) || QDELETED(mover) || QDELETED(turf)) + return + + if(safe_fall) + visible_message(SPAN_NOTICE("\The [mover] lands on \the [turf].")) + playsound(mover, "rustle", 25, 1) + return + + playsound(mover, "punch", 70, 1) + + visible_message(SPAN_DANGER("\The [mover] hits \the [turf]!")) + + if(faller) + // The bigger they are, the harder they fall. + // They will take at least 20 damage at the minimum, and tries to scale up to 40% of their max health. + // This scaling is capped at 100 total damage, which occurs if the thing that fell has more than 250 health. + faller.take_damage(clamp(faller.get_max_health() * 0.4, 20, 100) * harm, BRUTE, ran_zone(), inflicter = src) + shake_camera(faller, 1, 1) + + // Now fall off more cliffs below this one if they exist. + var/obj/structure/cliff/bottom_cliff = locate() in turf + if(bottom_cliff && !QDELETED(mover)) // Exosuits are deleted when destroyed. This is to prevent phantom exosuits. + visible_message(SPAN_DANGER("\The [mover] rolls down towards \the [bottom_cliff]!")) + addtimer(CALLBACK(bottom_cliff, TYPE_PROC_REF(/obj/structure/cliff, fall_off_cliff), mover), 5) + +/obj/structure/cliff/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) + // Cliff climbing requires climbing gear. + if(ishuman(user)) + var/mob/living/human/H = user + var/obj/item/clothing/shoes/shoes = H.get_equipped_item(slot_shoes_str) + if(shoes?.rock_climbing) + return ..() // Do the other checks too. + if(!silent) + to_chat(user, SPAN_WARNING("\The [src] is too steep to climb unassisted.")) + return FALSE + +// This tells AI mobs to not be dumb and step off cliffs willingly. +/obj/structure/cliff/is_safe_to_step(mob/living/stepper) + if(should_fall(stepper)) + return FALSE + return ..() diff --git a/code/game/objects/structures/fishtanks.dm b/code/game/objects/structures/fishtanks.dm index c9ab4bc5f72e..2e828ed6e803 100644 --- a/code/game/objects/structures/fishtanks.dm +++ b/code/game/objects/structures/fishtanks.dm @@ -138,25 +138,25 @@ var/global/list/global/aquarium_states_and_layers = list( for(var/atom/movable/AM in get_contained_external_atoms()) add_overlay(AM) -/obj/structure/glass_tank/can_climb(var/mob/living/user, post_climb_check=0) +/obj/structure/glass_tank/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) if (!user.can_touch(src) || !(atom_flags & ATOM_FLAG_CLIMBABLE) || (!post_climb_check && (user in climbers))) - return 0 - + return FALSE if (!Adjacent(user)) - to_chat(user, SPAN_DANGER("You can't climb there, the way is blocked.")) - return 0 - + if(!silent) + to_chat(user, SPAN_WARNING("You can't climb there, the way is blocked.")) + return FALSE var/obj/occupied = turf_is_crowded() if(occupied) - to_chat(user, SPAN_DANGER("There's \a [occupied] in the way.")) - return 0 - return 1 + if(!silent) + to_chat(user, SPAN_WARNING("There's \a [occupied] in the way.")) + return FALSE + return TRUE /obj/structure/glass_tank/do_climb(var/mob/living/user) if(!istype(user) || !can_climb(user)) return user.visible_message(SPAN_WARNING("\The [user] starts climbing into \the [src]!")) - if(!do_after(user,50)) + if(!do_after(user, 5 SECONDS)) return if (!can_climb(user)) return diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index b04f59f9a150..003d51d5b13d 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -314,13 +314,13 @@ WOOD_RAILING_SUBTYPE(yew) if(!QDELETED(src)) qdel(src) -/obj/structure/railing/can_climb(var/mob/living/user, post_climb_check=0) - . = ..() - if(. && get_turf(user) == get_turf(src)) +/obj/structure/railing/can_climb(mob/living/user, post_climb_check = FALSE, silent = FALSE) + if((. = ..()) && get_turf(user) == get_turf(src)) var/turf/T = get_step(src, dir) if(T.turf_is_crowded(user)) - to_chat(user, "You can't climb there, the way is blocked.") - return 0 + if(!silent) + to_chat(user, SPAN_WARNING("You can't climb there, the way is blocked.")) + return FALSE /obj/structure/railing/do_climb(var/mob/living/user) . = ..() diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs/_signs.dm similarity index 90% rename from code/game/objects/structures/signs.dm rename to code/game/objects/structures/signs/_signs.dm index 0b1245e25d7c..2241befe6254 100644 --- a/code/game/objects/structures/signs.dm +++ b/code/game/objects/structures/signs/_signs.dm @@ -87,7 +87,7 @@ ///A wall mountable sign structure /obj/structure/sign name = "sign" - icon = 'icons/obj/signs/warnings.dmi' + icon = 'icons/obj/signs/signs.dmi' anchored = TRUE opacity = FALSE density = FALSE @@ -134,3 +134,18 @@ /obj/structure/sign/double/handle_default_screwdriver_attackby(mob/user, obj/item/screwdriver) return FALSE + +/obj/structure/sign/clock + name = "clock" + desc = "It's a functionally useless print-out of a clock face." + icon_state = "clock" + +/obj/structure/sign/calendar + name = "calendar" + desc = "It's a functionally useless print-out of a calendar." + icon_state = "calendar" + +/obj/structure/sign/periodic_table + name = "periodic table" + desc = "It's an old, outdated copy of the periodic table of elements." + icon_state = "periodic" diff --git a/code/game/objects/structures/signs/bar_signs.dm b/code/game/objects/structures/signs/bar_signs.dm index 51396fea7bbf..1d53cf2fa3a8 100644 --- a/code/game/objects/structures/signs/bar_signs.dm +++ b/code/game/objects/structures/signs/bar_signs.dm @@ -1,4 +1,3 @@ - /obj/structure/sign/double/maltesefalcon name = "The Maltese Falcon" desc = "The Maltese Falcon, Space Bar and Grill." @@ -13,3 +12,4 @@ /obj/structure/sign/double/maltesefalcon/right icon_state = "maltesefalcon-right" + diff --git a/code/game/objects/structures/signs/decks.dm b/code/game/objects/structures/signs/decks.dm new file mode 100644 index 000000000000..3fc6dadc59fd --- /dev/null +++ b/code/game/objects/structures/signs/decks.dm @@ -0,0 +1,95 @@ +///////////////////////////////////////////////////// +// Deck Signs +///////////////////////////////////////////////////// + +///A sign for indicating what level is the current level vertically +/obj/structure/sign/deck + abstract_type = /obj/structure/sign/deck + name = "current level sign" + desc = "A sign indicating on what level the observer is currently on." + icon = 'icons/obj/signs/decks.dmi' + +///////////////////////////////////////////////////// +// Deck Signs Definition +///////////////////////////////////////////////////// + +/obj/structure/sign/deck/bridge + name = "\improper Bridge Deck" + icon_state = "deck-b" + +/obj/structure/sign/deck/first + name = "\improper First Deck" + icon_state = "deck-1" + +/obj/structure/sign/deck/second + name = "\improper Second Deck" + icon_state = "deck-2" + +/obj/structure/sign/deck/third + name = "\improper Third Deck" + icon_state = "deck-3" + +/obj/structure/sign/deck/fourth + name = "\improper Fourth Deck" + icon_state = "deck-4" + +/obj/structure/sign/deck/fifth + name = "\improper Fifth Deck" + icon_state = "deck-5" + +/obj/structure/sign/deck/bridge/large + icon_state = "deck-b-large" + +/obj/structure/sign/deck/first/large + icon_state = "deck-1-large" + +/obj/structure/sign/deck/second/large + icon_state = "deck-2-large" + +/obj/structure/sign/deck/third/large + icon_state = "deck-3-large" + +/obj/structure/sign/deck/fourth/large + icon_state = "deck-4-large" + +/obj/structure/sign/deck/level_basement + name = "\improper Basemenet Level" + icon_state = "level-b" + +/obj/structure/sign/deck/level_one + name = "\improper Level One" + icon_state = "level-1" + +/obj/structure/sign/deck/level_two + name = "\improper Level Two" + icon_state = "level-2" + +/obj/structure/sign/deck/level_three + name = "\improper Level Three" + icon_state = "level-3" + +/obj/structure/sign/deck/level_four + name = "\improper Level Four" + icon_state = "level-4" + +/obj/structure/sign/deck/level_ground + name = "\improper Ground Level" + icon_state = "level-g" + +/obj/structure/sign/deck/level_basement/large + icon_state = "level-b-large" + +/obj/structure/sign/deck/level_one/large + icon_state = "level-1-large" + +/obj/structure/sign/deck/level_two/large + icon_state = "level-2-large" + +/obj/structure/sign/deck/level_three/large + icon_state = "level-3-large" + +/obj/structure/sign/deck/level_four/large + icon_state = "level-4-large" + +/obj/structure/sign/deck/level_ground/large + icon_state = "level-g-large" diff --git a/code/game/objects/structures/signs/department_signs.dm b/code/game/objects/structures/signs/departments.dm similarity index 77% rename from code/game/objects/structures/signs/department_signs.dm rename to code/game/objects/structures/signs/departments.dm index 92fa661be71e..0018ae5d6a88 100644 --- a/code/game/objects/structures/signs/department_signs.dm +++ b/code/game/objects/structures/signs/departments.dm @@ -4,7 +4,7 @@ /obj/structure/sign/department abstract_type = /obj/structure/sign/department - icon = 'icons/obj/signs/slim_location_signs.dmi' + icon = 'icons/obj/signs/departments.dmi' /////////////////////////////////////////////////////////////////////////////////// // Department Sign Definitions @@ -18,36 +18,37 @@ /obj/structure/sign/department/science_2 name = "\improper RESEARCH" desc = "A sign labelling an area where research is performed." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "science2" +/obj/structure/sign/department/science_3 + icon_state = "science1" + /obj/structure/sign/department/xenobio_1 name = "\improper XENOBIOLOGY" desc = "A sign labelling an area as a place where xenobiological entities are researched." icon_state = "xenobio" +/obj/structure/sign/department/xenobio_1/large + icon_state = "xenobio-large" + /obj/structure/sign/department/xenobio_2 name = "\improper XENOBIOLOGY" desc = "A sign labelling an area as a place where xenobiological entities are researched." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "xenobio2" /obj/structure/sign/department/xenobio_3 name = "\improper XENOBIOLOGY" desc = "A sign labelling an area as a place where xenobiological entities are researched." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "xenobio3" /obj/structure/sign/department/xenobio_4 name = "\improper XENOBIOLOGY" desc = "A sign labelling an area as a place where xenobiological entities are researched." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "xenobio4" /obj/structure/sign/department/xenoarch name = "\improper XENOARCHAEOLOGY" desc = "A sign labelling an area as a place where xenoarchaeological finds are researched." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "xenobio4" /obj/structure/sign/department/chemistry @@ -55,16 +56,20 @@ desc = "A sign labelling an area containing chemical equipment." icon_state = "chemistry" +/obj/structure/sign/department/chemistry/alt_1 + icon_state = "chemistry1" + +/obj/structure/sign/department/chemistry/alt_2 + icon_state = "chemistry2" + /obj/structure/sign/department/xenoflora name = "\improper XENOFLORA" desc = "A sign labelling an area as a place where xenobiological plants are researched." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "hydro4" /obj/structure/sign/department/botany name = "\improper BOTANY" - desc = "A warning sign which reads 'BOTANY!'." - icon = 'icons/obj/signs/location_signs.dmi' + desc = "A warning sign which reads 'BOTANY'." icon_state = "hydro3" /obj/structure/sign/department/hydro @@ -75,19 +80,30 @@ /obj/structure/sign/department/hydrostorage name = "\improper HYDROPONICS STORAGE" desc = "A sign labelling an area as a place where plant growing supplies are kept." - icon = 'icons/obj/signs/location_signs.dmi' icon_state = "hydro3" +/obj/structure/sign/department/hydro/alt_1 + icon_state = "hydro1" + +/obj/structure/sign/department/hydro/alt_2 + icon_state = "hydro2" + /obj/structure/sign/department/janitor name = "\improper JANITORIAL CLOSET" desc = "A sign indicating a room used to store cleaning supplies." icon_state = "janitor" +/obj/structure/sign/department/janitor/alt + icon_state = "custodian" + /obj/structure/sign/department/engineering name = "\improper ENGINEERING" desc = "A sign labelling an area as the Engineering department." icon_state = "engineering" +/obj/structure/sign/department/engineering/engine + icon_state = "engine" + /obj/structure/sign/department/telecomms name = "\improper TELECOMMUNICATIONS" desc = "A sign labelling an area as the Telecommunications room." @@ -98,11 +114,17 @@ desc = "A sign labelling the area as a cargo bay." icon_state = "cargo" +/obj/structure/sign/department/cargo/large + icon_state = "cargo-large" + /obj/structure/sign/department/mail_delivery name = "\improper MAIL DELIVERY" desc = "A sign labelling a mail delivery point." icon_state = "mail" +/obj/structure/sign/department/mail_delivery/large + icon_state = "mail-large" + /obj/structure/sign/department/bridge name = "\improper BRIDGE" desc = "A sign indicating the Bridge. Not the kind you cross rivers with, the other kind." @@ -121,6 +143,9 @@ /obj/structure/sign/department/security/alt icon_state = "sec_cuff" +/obj/structure/sign/department/security/large + icon_state = "security" + /obj/structure/sign/department/eva name = "\improper EVA" desc = "A sign indicating this is where Extra Vehicular Activity equipment is stored." @@ -141,41 +166,44 @@ desc = "A sign that lets you know that this is where you want to be when the station is full of holes and on fire." icon_state = "evac" -/obj/structure/sign/department/watercloset - name = "\improper BATHROOMS" +/obj/structure/sign/department/evac/large + icon_state = "evac-large" + +/obj/structure/sign/department/restroom + name = "restroom" desc = "Need to take a piss? You've come to the right place." icon_state = "watercloset" +/obj/structure/sign/department/restroom/alt + icon_state = "restroom" + /obj/structure/sign/department/examroom - name = "\improper Exam Room" + name = "exam room" icon_state = "examroom" -/obj/structure/sign/department/redcross +/obj/structure/sign/department/examroom/large + icon_state = "examroom-large" + +/obj/structure/sign/department/cross name = "medbay" desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." - icon = 'icons/obj/signs/medical.dmi' icon_state = "redcross" -/obj/structure/sign/department/greencross - name = "medbay" - desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." - icon = 'icons/obj/signs/medical.dmi' +/obj/structure/sign/department/cross/green icon_state = "greencross" -/obj/structure/sign/department/bluecross_1 - name = "infirmary" - desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." - icon = 'icons/obj/signs/medical.dmi' +/obj/structure/sign/department/cross/blue icon_state = "bluecross" -/obj/structure/sign/department/bluecross_2 - name = "infirmary" - desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." - icon = 'icons/obj/signs/medical.dmi' +/obj/structure/sign/department/cross/blue2 icon_state = "bluecross2" -/obj/structure/sign/department/star_of_life +/obj/structure/sign/department/cross/star_of_life name = "emergency" desc = "The blue six-pointed star with a rod of Asclepius is the intergalactic symbol of emergency medical services." - icon = 'icons/obj/signs/medical.dmi' - icon_state = "staroflife" \ No newline at end of file + icon_state = "staroflife" + +/obj/structure/sign/department/chapel + name = "\improper CHAPEL" + desc = "A sign labelling this area as the Chapel." + icon_state = "holy" diff --git a/code/game/objects/structures/signs/direction_signs.dm b/code/game/objects/structures/signs/direction_signs.dm deleted file mode 100644 index 1b331afc9941..000000000000 --- a/code/game/objects/structures/signs/direction_signs.dm +++ /dev/null @@ -1,120 +0,0 @@ -///////////////////////////////////////////////////// -//Direction Signs -///////////////////////////////////////////////////// - -///Signs for showing the way to passerby. The dir of the sign is the direction it points towards. The icon of the sign itself is always south facing. -/obj/structure/sign/directions - name = "direction sign" - desc = "A direction sign, claiming to know the way." - icon = 'icons/obj/signs/directions.dmi' - icon_state = "direction" - //Direction signs are always meant to face south! The arrow on the sign matches the direction it points to. - directional_offset = @'{"NORTH":{"y":32}, "SOUTH":{"y":32}, "WEST":{"y":32}, "EAST":{"y":32}}' - -/obj/structure/sign/directions/update_description() - desc = "A direction sign, pointing out \the [name] is [global.dir_name(dir)]." - -///////////////////////////////////////////////////// -//Direction Signs Definition -///////////////////////////////////////////////////// - -/obj/structure/sign/directions/science - name = "\improper Research Division" - icon_state = "direction_sci" - -/obj/structure/sign/directions/engineering - name = "\improper Engineering Bay" - icon_state = "direction_eng" - -/obj/structure/sign/directions/security - name = "\improper Security Wing" - icon_state = "direction_sec" - -/obj/structure/sign/directions/medical - name = "\improper Medical Bay" - icon_state = "direction_med" - -/obj/structure/sign/directions/evac - name = "\improper Evacuation Wing" - icon_state = "direction_evac" - -/obj/structure/sign/directions/bridge - name = "\improper Bridge" - icon_state = "direction_bridge" - -/obj/structure/sign/directions/supply - name = "\improper Supply Office" - icon_state = "direction_supply" - -/obj/structure/sign/directions/infirmary - name = "\improper Infirmary" - icon_state = "direction_infirm" - -/obj/structure/sign/directions/pods - name = "\improper ESCAPE PODS" - icon_state = "direction_pods" - -/obj/structure/sign/directions/cryo - name = "\improper Cryogenic Storage" - icon_state = "direction_cryo" - - -///////////////////////////////////////////////////// -// Hangar Signs -///////////////////////////////////////////////////// -/obj/structure/sign/hangar - abstract_type = /obj/structure/sign/hangar - name = "hangar sign" - desc = "A sign indicating which hangar the observer is near." - icon = 'icons/obj/signs/hangars.dmi' - -/obj/structure/sign/hangar/one - name = "\improper Hangar One" - icon_state = "hangar-1" - -/obj/structure/sign/hangar/two - name = "\improper Hangar Two" - icon_state = "hangar-2" - -/obj/structure/sign/hangar/three - name = "\improper Hangar Three" - icon_state = "hangar-3" - -///////////////////////////////////////////////////// -// Deck Signs -///////////////////////////////////////////////////// - -///A sign for indicating what level is the current level vertically -/obj/structure/sign/deck - abstract_type = /obj/structure/sign/deck - name = "current level sign" - desc = "A sign indicating on what level the observer is currently on." - icon = 'icons/obj/signs/slim_decks.dmi' - -///////////////////////////////////////////////////// -// Deck Signs Definition -///////////////////////////////////////////////////// - -/obj/structure/sign/deck/bridge - name = "\improper Bridge Deck" - icon_state = "deck-b" - -/obj/structure/sign/deck/first - name = "\improper First Deck" - icon_state = "deck-1" - -/obj/structure/sign/deck/second - name = "\improper Second Deck" - icon_state = "deck-2" - -/obj/structure/sign/deck/third - name = "\improper Third Deck" - icon_state = "deck-3" - -/obj/structure/sign/deck/fourth - name = "\improper Fourth Deck" - icon_state = "deck-4" - -/obj/structure/sign/deck/fifth - name = "\improper Fifth Deck" - icon_state = "deck-5" \ No newline at end of file diff --git a/code/game/objects/structures/signs/directions.dm b/code/game/objects/structures/signs/directions.dm new file mode 100644 index 000000000000..c89ae69448fb --- /dev/null +++ b/code/game/objects/structures/signs/directions.dm @@ -0,0 +1,282 @@ +///////////////////////////////////////////////////// +//Direction Signs +///////////////////////////////////////////////////// + +///Signs for showing the way to passerby. The dir of the sign is the direction it points towards. The icon of the sign itself is always south facing. +/obj/structure/sign/directions + name = "direction sign" + desc = "A direction sign, claiming to know the way." + icon = 'icons/obj/signs/directions.dmi' + icon_state = "direction" + //Direction signs are always meant to face south! The arrow on the sign matches the direction it points to. + directional_offset = @'{"NORTH":{"y":32}, "SOUTH":{"y":32}, "WEST":{"y":32}, "EAST":{"y":32}}' + +/obj/structure/sign/directions/update_description() + desc = "A direction sign, pointing out \the [name] is [global.dir_name(dir)]." + +///////////////////////////////////////////////////// +//Direction Signs Definition +///////////////////////////////////////////////////// + +/obj/structure/sign/directions/science + name = "\improper Research Division" + icon_state = "direction_sci" + +/obj/structure/sign/directions/science/xeno + name = "\improper Xenobiology" + icon_state = "direction_xeno" + +/obj/structure/sign/directions/science/xenoarch + name = "\improper Xenoarchaeology" + icon_state = "direction_xenoarch" + +/obj/structure/sign/directions/science/xenoflora + name = "\improper Xenoflora" + icon_state = "direction_xflora" + +/obj/structure/sign/directions/science/xenobiology + icon_state = "direction_xbio" + +/obj/structure/sign/directions/science/exploration + name = "\improper Exploration" + icon_state = "direction_explo" + +/obj/structure/sign/directions/science/toxins + name = "\improper Toxins" + icon_state = "direction_toxins" + +/obj/structure/sign/directions/science/robotics + name = "\improper Robotics" + icon_state = "direction_robotics" + +/obj/structure/sign/directions/science/rnd + name = "\improper Research and Development" + icon_state = "direction_rnd" + +/obj/structure/sign/directions/engineering + name = "\improper Engineering Bay" + icon_state = "direction_eng" + +/obj/structure/sign/directions/engineering/solars + name = "\improper Solar Array" + icon_state = "direction_solar" + +/obj/structure/sign/directions/engineering/engeqp + name = "\improper Engineering Equipment" + icon_state = "direction_engeqp" + +/obj/structure/sign/directions/engineering/reactor + name = "\improper Reactor Core" + icon_state = "direction_core" + +/obj/structure/sign/directions/engineering/atmospherics + name = "\improper Atmospherics" + icon_state = "direction_atmos" + +/obj/structure/sign/directions/cargo + name = "\improper Cargo" + icon_state = "direction_crg" + +/obj/structure/sign/directions/cargo/supply + name = "\improper Supply Office" + icon_state = "direction_supply" + +/obj/structure/sign/directions/cargo/mining + name = "\improper Mining" + icon_state = "direction_mining" + +/obj/structure/sign/directions/cargo/refinery + name = "\improper Refinery" + icon_state = "direction_refinery" + +/obj/structure/sign/directions/security + name = "\improper Security Wing" + icon_state = "direction_sec" + +/obj/structure/sign/directions/security/interrogation + name = "\improper Interrogation" + icon_state = "direction_interrogation" + +/obj/structure/sign/directions/security/internal_affairs + name = "\improper Internal Affairs" + icon_state = "direction_intaff" + +/obj/structure/sign/directions/security/forensics + name = "\improper Forensics" + icon_state = "direction_forensics" + +/obj/structure/sign/directions/security/forensics/alt + name = "\improper Forensics Laboratory" + icon_state = "direction_lab" + +/obj/structure/sign/directions/security/brig + name = "\improper Security Brig" + icon_state = "direction_brig" + +/obj/structure/sign/directions/security/armory + name = "\improper Security Armory" + icon_state = "direction_armory" + +/obj/structure/sign/directions/security/seceqp + name = "\improper Security Equipment" + icon_state = "direction_seceqp" + +/obj/structure/sign/directions/medical + name = "\improper Medical Bay" + icon_state = "direction_med" + +/obj/structure/sign/directions/medical/morgue + name = "\improper Morgue" + icon_state = "direction_morgue" + +/obj/structure/sign/directions/medical/equipment + name = "\improper Medical Equipment" + icon_state = "direction_medeqp" + +/obj/structure/sign/directions/medical/virology + name = "\improper Virology" + icon_state = "direction_viro" + +/obj/structure/sign/directions/medical/surgery + name = "\improper Surgery" + icon_state = "direction_surgery" + +/obj/structure/sign/directions/medical/operating_1 + name = "\improper Operating Theatre 1" + icon_state = "direction_op1" + +/obj/structure/sign/directions/medical/operating_2 + name = "\improper Operating Theatre 2" + icon_state = "direction_op2" + +/obj/structure/sign/directions/medical/cloning + name = "\improper Cloning" + icon_state = "direction_cloning" + +/obj/structure/sign/directions/medical/resleeve + name = "\improper Resleeving" + icon_state = "direction_resleeve" + +/obj/structure/sign/directions/medical/chemlab + name = "\improper Chemistry Laboratory" + icon_state = "direction_chemlab" + +/obj/structure/sign/directions/evac + name = "\improper Evacuation Wing" + icon_state = "direction_evac" + +/obj/structure/sign/directions/bridge + name = "\improper Bridge" + icon_state = "direction_bridge" + +/obj/structure/sign/directions/infirmary + name = "\improper Infirmary" + icon_state = "direction_infirm" + +/obj/structure/sign/directions/pods + name = "\improper Escape Pods" + icon_state = "direction_pods" + +/obj/structure/sign/directions/cryo + name = "\improper Cryogenic Storage" + icon_state = "direction_cryo" + +/obj/structure/sign/directions/elevator + name = "\improper Elevator" + icon_state = "direction_elv" + +/obj/structure/sign/directions/command + name = "\improper Command" + icon_state = "direction_command" + +/obj/structure/sign/directions/dorms + name = "\improper Dormitories" + icon_state = "direction_dorms" + +/obj/structure/sign/directions/teleporter + name = "\improper Teleporter" + icon_state = "direction_teleport" + +/obj/structure/sign/directions/roomnum + name = "\improper Private Room" + icon_state = "direction_roomnum" // TODO: move this to signs.dmi or something. + +/obj/structure/sign/directions/recreation + name = "\improper Recreation" + icon_state = "direction_recreation" + +/obj/structure/sign/directions/pool + name = "\improper Pool" + icon_state = "direction_pool" + +/obj/structure/sign/directions/janitor + name = "\improper Custodial Office" + icon_state = "direction_janitor" + +/obj/structure/sign/directions/eva + name = "\improper EVA" + icon_state = "direction_eva" + +/obj/structure/sign/directions/bar + name = "\improper Bar" + icon_state = "direction_bar" + +/obj/structure/sign/directions/ai_core + name = "\improper AI Core" + icon_state = "direction_ai_core" + +/obj/structure/sign/directions/gravity + name = "\improper Gravity Management" + icon_state = "direction_grav" + +/obj/structure/sign/directions/telecomms + name = "\improper Telecommunications" + icon_state = "direction_tcomms" + +/obj/structure/sign/directions/kitchen + name = "\improper Kitchen" + icon_state = "direction_kitchen" + +/obj/structure/sign/directions/tram + name = "\improper Transit" + icon_state = "direction_tram" + +/obj/structure/sign/directions/chapel + name = "\improper Chapel" + icon_state = "direction_chapel" + +/obj/structure/sign/directions/library + name = "\improper Library" + icon_state = "direction_library" + +/obj/structure/sign/directions/dock + name = "\improper Dock" + icon_state = "direction_dock" + +/obj/structure/sign/directions/gym + name = "\improper Gymnasium" + icon_state = "direction_gym" + +/obj/structure/sign/directions/exit + name = "\improper Emergency Exit" + icon_state = "exit_sign" + +/obj/structure/sign/directions/stairs + name = "\improper Stairwell" + icon_state = "stairwell" + +/obj/structure/sign/directions/stairs/up + icon_state = "stairs_up" + +/obj/structure/sign/directions/stairs/down + icon_state = "stairs_down" + +/obj/structure/sign/directions/ladder + name = "\improper Ladder" + icon_state = "ladderwell" + +/obj/structure/sign/directions/ladder/up + icon_state = "ladder_up" + +/obj/structure/sign/directions/ladder/down + icon_state = "ladder_down" diff --git a/code/game/objects/structures/signs/flags.dm b/code/game/objects/structures/signs/flags.dm new file mode 100644 index 000000000000..8fce965bc1aa --- /dev/null +++ b/code/game/objects/structures/signs/flags.dm @@ -0,0 +1,27 @@ +/obj/structure/sign/double/flag + name = "flag" + desc = "A plain flag." + icon = 'icons/obj/signs/flags.dmi' + icon_state = "flag" +/obj/structure/sign/double/flag/left + icon_state = "flag_l" +/obj/structure/sign/double/flag/right + icon_state = "flag_r" + +/obj/structure/sign/double/flag/pirate + name = "pirate flag" + desc = "Yarr harr, fiddle de dee!" + icon_state = "pirate" +/obj/structure/sign/double/flag/pirate/left + icon_state = "pirate_l" +/obj/structure/sign/double/flag/pirate/right + icon_state = "pirate_r" + +/obj/structure/sign/double/flag/catpirate + name = "cat pirate flag" + desc = "Nyarr harr, fiddle de dee!" + icon_state = "catpirate" +/obj/structure/sign/double/flag/catpirate/left + icon_state = "catpirate_l" +/obj/structure/sign/double/flag/catpirate/right + icon_state = "catpirate_r" diff --git a/code/game/objects/structures/signs/hangar.dm b/code/game/objects/structures/signs/hangar.dm new file mode 100644 index 000000000000..0ad300afe060 --- /dev/null +++ b/code/game/objects/structures/signs/hangar.dm @@ -0,0 +1,20 @@ +///////////////////////////////////////////////////// +// Hangar Signs +///////////////////////////////////////////////////// +/obj/structure/sign/hangar + abstract_type = /obj/structure/sign/hangar + name = "hangar sign" + desc = "A sign indicating which hangar the observer is near." + icon = 'icons/obj/signs/hangars.dmi' + +/obj/structure/sign/hangar/one + name = "\improper Hangar One" + icon_state = "hangar-1" + +/obj/structure/sign/hangar/two + name = "\improper Hangar Two" + icon_state = "hangar-2" + +/obj/structure/sign/hangar/three + name = "\improper Hangar Three" + icon_state = "hangar-3" diff --git a/code/game/objects/structures/signs/levels.dm b/code/game/objects/structures/signs/levels.dm new file mode 100644 index 000000000000..214b43bc6508 --- /dev/null +++ b/code/game/objects/structures/signs/levels.dm @@ -0,0 +1,232 @@ +/obj/structure/sign/levels + icon = 'icons/obj/signs/levels.dmi' + icon_state = "level" + //Level signs are always meant to face south! The arrow on the sign matches the direction it points to. + directional_offset = @'{"NORTH":{"y":32}, "SOUTH":{"y":32}, "WEST":{"y":32}, "EAST":{"y":32}}' + +/obj/structure/sign/levels/update_description() + desc = "A sign indicating your position within \the [name]." + +/obj/structure/sign/levels/engineering + name = "\improper Engineering" + icon_state = "level_eng" + +/obj/structure/sign/levels/engineering/core + name = "\improper Reactor Core" + icon_state = "level_core" + +/obj/structure/sign/levels/engineering/solar + name = "\improper Solar Array" + icon_state = "level_solar" + +/obj/structure/sign/levels/engineering/atmos + name = "\improper Atmospherics" + icon_state = "level_atmos" + +/obj/structure/sign/levels/engineering/gravity + name = "\improper Gravity Control" + icon_state = "level_grav" + +/obj/structure/sign/levels/engineering/equipment + name = "\improper Engineering Equipment" + icon_state = "level_engeqp" + +/obj/structure/sign/levels/medical + name = "\improper Medical" + icon_state = "level_med" + +/obj/structure/sign/levels/medical/virology + name = "\improper Virology" + icon_state = "level_viro" + +/obj/structure/sign/levels/medical/morgue + name = "\improper Morgue" + icon_state = "level_morgue" + +/obj/structure/sign/levels/medical/surgery + name = "\improper Surgery" + icon_state = "level_surgery" + +/obj/structure/sign/levels/medical/cloning + name = "\improper Cloning" + icon_state = "level_cloning" + +/obj/structure/sign/levels/medical/resleeve + name = "\improper Resleeving" + icon_state = "level_resleeve" + +/obj/structure/sign/levels/medical/chemlab + name = "\improper Chemistry" + icon_state = "level_chemlab" + +/obj/structure/sign/levels/medical/equipment + name = "\improper Medical Equipment" + icon_state = "level_medeqp" + +/obj/structure/sign/levels/medical/operating_1 + name = "\improper Operating Theatre 1" + icon_state = "level_op1" + +/obj/structure/sign/levels/medical/operating_2 + name = "\improper Operating Theatre 2" + icon_state = "level_op2" + +/obj/structure/sign/levels/security + name = "\improper Security" + icon_state = "level_sec" + +/obj/structure/sign/levels/security/seceqp + name = "\improper Security Equipment" + icon_state = "level_seceqp" + +/obj/structure/sign/levels/security/interrogation + name = "\improper Interrogation" + icon_state = "level_interrogation" + +/obj/structure/sign/levels/security/forensics + name = "\improper Forensics" + icon_state = "level_forensics" + +/obj/structure/sign/levels/security/brig + name = "\improper Security Brig" + icon_state = "level_brig" + +/obj/structure/sign/levels/security/armory + name = "\improper Security Armory" + icon_state = "level_armory" + +/obj/structure/sign/levels/security/internalaffairs + name = "\improper Internal Affairs" + icon_state = "level_intaff" + +/obj/structure/sign/levels/cryo + name = "\improper Cryogenics" + icon_state = "level_cry" + +/obj/structure/sign/levels/evac + name = "\improper Evac Wing" + icon_state = "level_evac" + +/obj/structure/sign/levels/eva + name = "\improper EVA" + icon_state = "level_eva" + +/obj/structure/sign/levels/command + name = "\improper Command" + icon_state = "level_command" + +/obj/structure/sign/levels/science + name = "\improper Research Wing" + icon_state = "level_sci" + +/obj/structure/sign/levels/science/xenoflora + name = "\improper Xenoflora" + icon_state = "level_xflora" + +/obj/structure/sign/levels/science/xenobiology + name = "\improper Xenobiology" + icon_state = "level_xbio" + +/obj/structure/sign/levels/science/exploration + name = "\improper Exploration" + icon_state = "level_explo" + +/obj/structure/sign/levels/science/robotics + name = "\improper Robotics" + icon_state = "level_robotics" + +/obj/structure/sign/levels/science/toxins + name = "\improper Toxins" + icon_state = "level_toxins" + +/obj/structure/sign/levels/science/xenoarch + name = "\improper Xenoarchaeology" + icon_state = "level_xenoarch" + +/obj/structure/sign/levels/science/rnd + name = "\improper Research and Development" + icon_state = "level_rnd" + +/obj/structure/sign/levels/dorms + name = "\improper Dormitories" + icon_state = "level_dorms" + +/obj/structure/sign/levels/cargo + name = "\improper Cargo" + icon_state = "level_crg" + +/obj/structure/sign/levels/cargo/mining + name = "\improper Mining" + icon_state = "level_mining" + +/obj/structure/sign/levels/cargo/refinery + name = "\improper Refinery" + icon_state = "level_refinery" + +/obj/structure/sign/levels/recreation + name = "\improper Recreation" + icon_state = "level_recreation" + +/obj/structure/sign/levels/laboratory + name = "\improper Laboratory" + icon_state = "level_lab" + +/obj/structure/sign/levels/xeno + name = "\improper Xenobiology" + icon_state = "level_xeno" + +/obj/structure/sign/levels/ai_core + name = "\improper AI Core" + icon_state = "level_ai_core" + +/obj/structure/sign/levels/bridge + name = "\improper Bridge" + icon_state = "level_bridge" + +/obj/structure/sign/levels/teleporter + name = "\improper Teleporter" + icon_state = "level_teleport" + +/obj/structure/sign/levels/telecomms + name = "\improper Telecommunications" + icon_state = "level_tcomms" + +/obj/structure/sign/levels/elevator + name = "\improper Elevator" + icon_state = "level_elv" + +/obj/structure/sign/levels/bar + name = "\improper Bar" + icon_state = "level_bar" + +/obj/structure/sign/levels/kitchen + name = "\improper Kitchen" + icon_state = "level_kitchen" + +/obj/structure/sign/levels/tram + name = "\improper Tram" + icon_state = "level_tram" + +/obj/structure/sign/levels/janitor + name = "\improper Janitor" + icon_state = "level_janitor" + +/obj/structure/sign/levels/chapel + name = "\improper Chapel" + icon_state = "level_chapel" + +/obj/structure/sign/levels/library + name = "\improper Library" + icon_state = "level_library" + +/obj/structure/sign/levels/dock + name = "\improper Docks" + icon_state = "level_dock" + +/obj/structure/sign/levels/gym + name = "\improper Gymnasium" + icon_state = "level_gym" + +/obj/structure/sign/levels/pool + name = "\improper Pool" + icon_state = "level_pool" diff --git a/code/game/objects/structures/signs/plaques.dm b/code/game/objects/structures/signs/plaques.dm index 19d72c4e1efc..4a4257437d12 100644 --- a/code/game/objects/structures/signs/plaques.dm +++ b/code/game/objects/structures/signs/plaques.dm @@ -19,6 +19,10 @@ /obj/structure/sign/plaque/dark icon_state = "darkplaque" +/obj/structure/sign/plaque/floor + desc = "A floor-mounted commemorative plaque." + icon_state = "floorplaque" + /obj/structure/sign/plaque/golden name = "The Most Robust Men Award for Robustness" desc = "To be Robust is not an action or a way of life, but a mental state. Only those with the force of Will strong enough to act during a crisis, saving friend from foe, are truly Robust. Stay Robust my friends." diff --git a/code/game/objects/structures/signs/warning_signs.dm b/code/game/objects/structures/signs/warning_signs.dm index 7165a62c8a04..2bcb946dec88 100644 --- a/code/game/objects/structures/signs/warning_signs.dm +++ b/code/game/objects/structures/signs/warning_signs.dm @@ -6,7 +6,7 @@ /obj/structure/sign/warning name = "\improper WARNING" desc = "You've been warned!" - icon = 'icons/obj/signs/slim_warnings.dmi' + icon = 'icons/obj/signs/warnings.dmi' icon_state = "securearea" directional_offset = @'{"NORTH":{"y":-32}, "SOUTH":{"y":32}, "WEST":{"x":34}, "EAST":{"x":-34}}' @@ -28,25 +28,28 @@ name = "\improper EXTERNAL AIRLOCK" icon_state = "doors" -/obj/structure/sign/warning/evac - name = "\improper KEEP CLEAR: EVAC DOCKING AREA" - icon = 'icons/obj/signs/warnings.dmi' - icon_state = "evac" +/obj/structure/sign/warning/airlock/large + icon_state = "doors-large" + +/obj/structure/sign/warning/pods + name = "\improper WARNING: ESCAPE POD DOCKING AREA" + icon_state = "pods" /obj/structure/sign/warning/deathsposal name = "\improper DISPOSAL LEADS TO SPACE" - icon = 'icons/obj/signs/warnings.dmi' icon_state = "deathsposal" /obj/structure/sign/warning/shock name = "\improper HIGH VOLTAGE" - icon = 'icons/obj/signs/warnings.dmi' icon_state = "shock" /obj/structure/sign/warning/compressed_gas name = "\improper COMPRESSED GAS" icon_state = "hikpa" +/obj/structure/sign/warning/compressed_gas/large + icon_state = "hikpa-large" + /obj/structure/sign/warning/docking_area name = "\improper KEEP CLEAR: DOCKING AREA" @@ -57,19 +60,24 @@ name = "\improper MOVING PARTS" icon_state = "movingparts" +/obj/structure/sign/warning/moving_parts/large + icon_state = "movingparts-large" + /obj/structure/sign/warning/nosmoking_1 name = "\improper NO SMOKING" icon_state = "nosmoking" +/obj/structure/sign/warning/nosmoking_1/large + icon_state = "nosmoking-large" + /obj/structure/sign/warning/nosmoking_2 name = "\improper NO SMOKING" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "nosmoking2" /obj/structure/sign/warning/nosmoking_burned name = "\improper NO SMOKING" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "nosmoking2_b" + /obj/structure/sign/warning/nosmoking_burned/update_description() . = ..() desc += " It looks charred." @@ -77,22 +85,37 @@ /obj/structure/sign/warning/smoking name = "\improper SMOKING" icon_state = "smoking" + /obj/structure/sign/warning/smoking/update_description() . = ..() desc += " Hell yeah." +/obj/structure/sign/warning/smoking/large + icon_state = "smoking-large" + /obj/structure/sign/warning/secure_area name = "\improper SECURE AREA" icon_state = "securearea2" +/obj/structure/sign/warning/secure_area/large + icon_state = "securearea2-large" + +/obj/structure/sign/warning/large + icon_state = "securearea-large" + /obj/structure/sign/warning/armory name = "\improper ARMORY" icon_state = "armory" +/obj/structure/sign/warning/armory/large + icon_state = "armory-large" + /obj/structure/sign/warning/server_room name = "\improper SERVER ROOM" icon_state = "server" +/obj/structure/sign/warning/server_room/large + icon_state = "server-large" /////////////////////////////////////////////////////////////////////////////////// // Hazard Sign Definitions @@ -100,13 +123,15 @@ /obj/structure/sign/warning/biohazard name = "\improper BIOHAZARD" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "bio" /obj/structure/sign/warning/radioactive name = "\improper RADIOACTIVE AREA" icon_state = "radiation" +/obj/structure/sign/warning/radioactive/large + icon_state = "radiation-large" + /obj/structure/sign/warning/radioactive/alt name = "\improper IONIZING RADIATION" icon_state = "radiation_2" @@ -115,10 +140,16 @@ name = "\improper DANGER: FIRE" icon_state = "fire" +/obj/structure/sign/warning/fire/large + icon_state = "fire-large" + /obj/structure/sign/warning/high_voltage name = "\improper HIGH VOLTAGE" icon_state = "shock" +/obj/structure/sign/warning/high_voltage/large + icon_state = "shock-large" + /obj/structure/sign/warning/hot_exhaust name = "\improper HOT EXHAUST" icon_state = "fire" @@ -132,17 +163,14 @@ /obj/structure/sign/warning/bomb_range name = "\improper BOMB RANGE" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "blast" /obj/structure/sign/warning/fall name = "\improper FALL HAZARD" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "falling" /obj/structure/sign/warning/lethal_turrets name = "\improper LETHAL TURRETS" - icon = 'icons/obj/signs/warnings.dmi' //This one is a full-size sign icon_state = "turrets" /obj/structure/sign/warning/lethal_turrets/update_description() @@ -156,6 +184,9 @@ name = "\improper HARD VACUUM AHEAD" icon_state = "space" +/obj/structure/sign/warning/vacuum/large + icon_state = "space-large" + /obj/structure/sign/warning/vent_port name = "\improper EJECTION/VENTING PORT" @@ -165,14 +196,54 @@ /obj/structure/sign/warning/mass_spectrometry name = "\improper MASS SPECTROMETRY" -//legacy stuff - The exodus map still uses it -/obj/structure/sign/warning/pods - name = "\improper ESCAPE PODS" - icon = 'icons/obj/signs/directions.dmi' - icon_state = "podsnorth" -/obj/structure/sign/warning/pods/south - icon_state = "podssouth" -/obj/structure/sign/warning/pods/east - icon_state = "podseast" -/obj/structure/sign/warning/pods/west - icon_state = "podswest" \ No newline at end of file +/obj/structure/sign/warning/acid + name = "\improper WARNING: CORROSIVE MATERIALS" + icon_state = "acid" + +/obj/structure/sign/warning/cold + name = "\improper WARNING: LOW TEMPERATURES" + icon_state = "cold" + +/obj/structure/sign/warning/lava + name = "\improper WARNING: MOLTEN ROCK" + icon_state = "lava" + +/obj/structure/sign/warning/malfunction + name = "\improper IN CASE OF MALFUNCTION" + icon_state = "rogueai" + +/obj/structure/sign/warning/explosives + name = "\improper WARNING: HIGH EXPLOSIVES" + icon_state = "explosives" + +/obj/structure/sign/warning/chemicals + name = "\improper WARNING: RISK OF CHEMICAL EXPOSURE" + icon_state = "chemdiamond" + +/obj/structure/sign/warning/atmos_co2 + name = "\improper WARNING: CO2" + icon_state = "atmos_co2" + +/obj/structure/sign/warning/atmos_n2o + name = "\improper WARNING: N2O" + icon_state = "atmos_n2o" + +/obj/structure/sign/warning/atmos_phoron + name = "\improper WARNING: EXOTIC MATTER" + icon_state = "atmos_phoron" + +/obj/structure/sign/warning/atmos_o2 + name = "\improper WARNING: O2" + icon_state = "atmos_o2" + +/obj/structure/sign/warning/atmos_air + name = "\improper WARNING: PRESSURIZED AIR" + icon_state = "atmos_air" + +/obj/structure/sign/warning/atmos_n2 + name = "\improper WARNING: N2" + icon_state = "atmos_n2" + +/obj/structure/sign/warning/atmos_waste + name = "\improper WARNING: WASTE UNDER PRESSURE" + icon_state = "atmos_waste" diff --git a/code/game/objects/structures/stasis_cage.dm b/code/game/objects/structures/stasis_cage.dm deleted file mode 100644 index 5474cb223cf5..000000000000 --- a/code/game/objects/structures/stasis_cage.dm +++ /dev/null @@ -1,93 +0,0 @@ -/obj/structure/stasis_cage - name = "stasis cage" - desc = "A high-tech animal cage, designed to keep contained fauna docile and safe." - icon = 'icons/obj/stasis_cage.dmi' - icon_state = "stasis_cage" - density = TRUE - layer = ABOVE_OBJ_LAYER - - var/mob/living/simple_animal/contained - -/obj/structure/stasis_cage/Initialize() - . = ..() - - var/mob/living/simple_animal/A = locate() in loc - if(A) - contain(A) - -/obj/structure/stasis_cage/attackby(obj/item/used_item, mob/user) - if(contained && istype(used_item, /obj/item/scanner/xenobio)) - return contained.attackby(used_item, user) - . = ..() - -/obj/structure/stasis_cage/attack_hand(var/mob/user) - if(!user.check_dexterity(DEXTERITY_SIMPLE_MACHINES, TRUE)) - return ..() - try_release(user) - return TRUE - -/obj/structure/stasis_cage/attack_robot(var/mob/user) - if(CanPhysicallyInteract(user)) - try_release(user) - return TRUE - -/obj/structure/stasis_cage/proc/try_release(mob/user) - if(!contained) - to_chat(user, SPAN_NOTICE("There's no animals inside \the [src]")) - return - user.visible_message("[user] begins undoing the locks and latches on \the [src].") - if(do_after(user, 20, src)) - user.visible_message("[user] releases \the [contained] from \the [src]!") - release() - -/obj/structure/stasis_cage/on_update_icon() - ..() - if(contained) - icon_state = "[initial(icon_state)]_on" - else - icon_state = initial(icon_state) - -/obj/structure/stasis_cage/get_examine_strings(mob/user, distance, infix, suffix) - . = ..() - if(contained) - . += "\The [contained] is kept inside." - -/obj/structure/stasis_cage/proc/contain(var/mob/living/simple_animal/animal) - if(contained || !istype(animal)) - return - - contained = animal - animal.forceMove(src) - animal.in_stasis = 1 - update_icon() - -/obj/structure/stasis_cage/proc/release() - if(!contained) - return - - contained.dropInto(src) - contained.in_stasis = 0 - contained = null - update_icon() - -/obj/structure/stasis_cage/Destroy() - release() - return ..() - -/mob/living/simple_animal/handle_mouse_drop(atom/over, mob/user, params) - if(istype(over, /obj/structure/stasis_cage)) - var/obj/structure/stasis_cage/cage = over - if(!stat && !istype(buckled, /obj/effect/energy_net)) - to_chat(user, SPAN_WARNING("It's going to be difficult to convince \the [src] to move into \the [cage] without capturing it in a net.")) - return TRUE - user.visible_message( \ - SPAN_NOTICE("\The [user] begins stuffing \the [src] into \the [cage]."), \ - SPAN_NOTICE("You begin stuffing \the [src] into \the [cage].")) - Bumped(user) - if(do_after(user, 20, cage)) - cage.visible_message( \ - SPAN_NOTICE("\The [user] has stuffed \the [src] into \the [cage]."), \ - SPAN_NOTICE("You have stuffed \the [src] into \the [cage].")) - cage.contain(src) - return TRUE - . = ..() diff --git a/code/game/sound.dm b/code/game/sound.dm index 3f0d24378d25..6aa5066cea21 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -141,29 +141,29 @@ var/global/const/FALLOFF_SOUNDS = 0.5 /proc/get_sfx(soundin) if(istext(soundin)) switch(soundin) - if ("shatter") soundin = pick(global.shatter_sound) - if ("explosion") soundin = pick(global.explosion_sound) - if ("sparks") soundin = pick(global.spark_sound) - if ("rustle") soundin = pick(global.rustle_sound) - if ("punch") soundin = pick(global.punch_sound) + if ("shatter") soundin = pick(global.shatter_sound) + if ("explosion") soundin = pick(global.explosion_sound) + if ("sparks") soundin = pick(global.spark_sound) + if ("rustle") soundin = pick(global.rustle_sound) + if ("punch") soundin = pick(global.punch_sound) if ("light_strike") soundin = pick(global.light_strike_sound) - if ("clownstep") soundin = pick(global.clown_sound) - if ("swing_hit") soundin = pick(global.swing_hit_sound) - if ("hiss") soundin = pick(global.hiss_sound) - if ("pageturn") soundin = pick(global.page_sound) - if ("fracture") soundin = pick(global.fracture_sound) - if ("light_bic") soundin = pick(global.lighter_sound) - if ("keyboard") soundin = pick(global.keyboard_sound) - if ("keystroke") soundin = pick(global.keystroke_sound) - if ("switch") soundin = pick(global.switch_sound) - if ("button") soundin = pick(global.button_sound) - if ("chop") soundin = pick(global.chop_sound) - if ("glasscrack") soundin = pick(global.glasscrack_sound) - if ("tray_hit") soundin = pick(global.tray_hit_sound) - if ("sweeping") soundin = pick(global.sweeping_sound) + if ("clownstep") soundin = pick(global.clown_sound) + if ("swing_hit") soundin = pick(global.swing_hit_sound) + if ("hiss") soundin = pick(global.hiss_sound) + if ("pageturn") soundin = pick(global.page_sound) + if ("fracture") soundin = pick(global.fracture_sound) + if ("light_bic") soundin = pick(global.lighter_sound) + if ("keyboard") soundin = pick(global.keyboard_sound) + if ("keystroke") soundin = pick(global.keystroke_sound) + if ("switch") soundin = pick(global.switch_sound) + if ("button") soundin = pick(global.button_sound) + if ("chop") soundin = pick(global.chop_sound) + if ("glasscrack") soundin = pick(global.glasscrack_sound) + if ("tray_hit") soundin = pick(global.tray_hit_sound) + if ("sweeping") soundin = pick(global.sweeping_sound) + if("ricochet") soundin = pick(global.ricochet_sound) return soundin - ///Volume to play DTMF key sounds at. They're pretty loud, so 15 is fine. #define VOL_DTMF_KEY 15 diff --git a/code/game/turfs/flooring/_flooring.dm b/code/game/turfs/flooring/_flooring.dm index 57f759c0bdce..f30adf01de71 100644 --- a/code/game/turfs/flooring/_flooring.dm +++ b/code/game/turfs/flooring/_flooring.dm @@ -27,6 +27,7 @@ var/global/list/flooring_cache = list() var/damage_temperature var/icon_edge_layer = FLOOR_EDGE_NONE var/has_environment_proc + var/can_conceal_hazards = FALSE /// Unbuildable if not set. Must be /obj/item/stack. var/build_type @@ -393,3 +394,6 @@ var/global/list/flooring_cache = list() /// contaminant is, optionally, the material of the coating that wants to be added. /decl/flooring/proc/can_show_coating_footprints(turf/target, decl/material/contaminant) return TRUE + +/decl/flooring/proc/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle::base_speed diff --git a/code/game/turfs/flooring/_flooring_decals.dm b/code/game/turfs/flooring/_flooring_decals.dm index 50ca2d36ab71..b1847ad05ab8 100644 --- a/code/game/turfs/flooring/_flooring_decals.dm +++ b/code/game/turfs/flooring/_flooring_decals.dm @@ -1221,7 +1221,7 @@ var/global/list/floor_decals = list() /obj/effect/floor_decal/solarpanel icon_state = "solarpanel" -/obj/effect/floor_decal/snow +/obj/effect/floor_decal/snow_floor icon = 'icons/turf/overlays.dmi' icon_state = "snowfloor" @@ -1634,4 +1634,34 @@ var/global/list/floor_decals = list() /obj/effect/floor_decal/corner_oldtile/green/full name = "corner oldtile full" - icon_state = "corner_oldtile_full" \ No newline at end of file + icon_state = "corner_oldtile_full" + +// Decorative overlays. +/obj/effect/floor_decal/vines + name = "vines" + desc = "A tangle of plant growth." + icon_state = "vines" + +/obj/effect/floor_decal/vines/top + icon_state = "vines_top" + +/obj/effect/floor_decal/vines/mid + icon_state = "vines_mid" + +/obj/effect/floor_decal/vines/bottom + icon_state = "vines_bottom" + +/obj/effect/floor_decal/snow + name = "snow" + desc = "A fine dusting of snow." + icon_state = "snowy" + +/obj/effect/floor_decal/rust + name = "rust" + desc = "A clumpy area of rust." + icon_state = "rusted" + +/obj/effect/floor_decal/floornums + name = "floor marker" + desc = "A number corresponding to the position of this floor." + icon_state = "floornums" diff --git a/code/game/turfs/flooring/flooring_concrete.dm b/code/game/turfs/flooring/flooring_concrete.dm index e5d6edd5f174..89ed297a1f7a 100644 --- a/code/game/turfs/flooring/flooring_concrete.dm +++ b/code/game/turfs/flooring/flooring_concrete.dm @@ -7,6 +7,7 @@ force_material = /decl/material/solid/stone/concrete constructed = TRUE uid = "floor_concrete" + can_conceal_hazards = TRUE /decl/flooring/concrete/reinforced name = "reinforced concrete" diff --git a/code/game/turfs/flooring/flooring_grass.dm b/code/game/turfs/flooring/flooring_grass.dm index dc53aaf46808..f51600fa91dc 100644 --- a/code/game/turfs/flooring/flooring_grass.dm +++ b/code/game/turfs/flooring/flooring_grass.dm @@ -14,6 +14,8 @@ force_material = /decl/material/solid/organic/plantmatter/grass growth_value = 1.2 // Shouldn't really matter since you can't plant on grass, it turns to dirt first. uid = "floor_grass" + can_conceal_hazards = TRUE + var/harvestable = FALSE /decl/flooring/grass/fire_act(turf/floor/target, datum/gas_mixture/air, exposed_temperature, exposed_volume) @@ -48,6 +50,9 @@ return TRUE return ..() +/decl/flooring/grass/get_vehicle_transit_delay(obj/vehicle/vehicle) + return 1 + /decl/flooring/grass/fake desc = "Do they smoke grass out in space, Bowie? Or do they smoke AstroTurf?" icon = 'icons/turf/flooring/fakegrass.dmi' @@ -56,3 +61,6 @@ build_type = /obj/item/stack/tile/grass force_material = /decl/material/solid/organic/plastic uid = "floor_grass_fake" + +/decl/flooring/grass/fake/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle::base_speed diff --git a/code/game/turfs/flooring/flooring_mud.dm b/code/game/turfs/flooring/flooring_mud.dm index fd2ccbaf7042..e7f26e889bec 100644 --- a/code/game/turfs/flooring/flooring_mud.dm +++ b/code/game/turfs/flooring/flooring_mud.dm @@ -34,6 +34,9 @@ return FALSE return ..() +/decl/flooring/mud/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 1.4 : 1.5 + /decl/flooring/dry_mud name = "dry mud" desc = "This was once mud, but forgot to keep hydrated." @@ -48,6 +51,9 @@ force_material = /decl/material/solid/soil uid = "floor_dry_mud" +/decl/flooring/dry_mud/get_vehicle_transit_delay(obj/vehicle/vehicle) + return 1 + /decl/flooring/dry_mud/fluid_act(turf/floor/target, datum/reagents/fluids) if(target.get_topmost_flooring() == src) target.set_flooring(/decl/flooring/mud) @@ -70,6 +76,7 @@ force_material = /decl/material/solid/soil growth_value = 1 uid = "floor_dirt" + can_conceal_hazards = TRUE /decl/flooring/dirt/fluid_act(turf/floor/target, datum/reagents/fluids) if(target.get_topmost_flooring() == src) @@ -79,3 +86,6 @@ target.set_base_flooring(/decl/flooring/mud) . = TRUE return . || ..() + +/decl/flooring/dirt/get_vehicle_transit_delay(obj/vehicle/vehicle) + return 1 diff --git a/code/game/turfs/flooring/flooring_natural.dm b/code/game/turfs/flooring/flooring_natural.dm index d05fdf330100..932289909a91 100644 --- a/code/game/turfs/flooring/flooring_natural.dm +++ b/code/game/turfs/flooring/flooring_natural.dm @@ -73,6 +73,9 @@ force_material = /decl/material/solid/ice uid = "floor_ice" +/decl/flooring/ice/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 0.8 : ..() + /decl/flooring/ice/update_turf_icon(turf/floor/target) . = ..() if(istype(target)) diff --git a/code/game/turfs/flooring/flooring_rock.dm b/code/game/turfs/flooring/flooring_rock.dm index 95a57226afe1..94227641346c 100644 --- a/code/game/turfs/flooring/flooring_rock.dm +++ b/code/game/turfs/flooring/flooring_rock.dm @@ -15,3 +15,6 @@ ASSERT(turf_material?.adjective_name) target.SetName("[turf_material.adjective_name] [name]") target.desc = "An expanse of bare [turf_material.solid_name]." + +/decl/flooring/rock/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 1.5 : ..() diff --git a/code/game/turfs/flooring/flooring_snow.dm b/code/game/turfs/flooring/flooring_snow.dm index 6b28bb27374a..72362928c06e 100644 --- a/code/game/turfs/flooring/flooring_snow.dm +++ b/code/game/turfs/flooring/flooring_snow.dm @@ -13,6 +13,7 @@ print_type = /obj/effect/footprints drop_material_on_remove = TRUE uid = "floor_snow" + can_conceal_hazards = TRUE /decl/flooring/snow/get_movement_delay(var/travel_dir, var/mob/mover) . = ..() @@ -47,6 +48,9 @@ return FALSE return ..() +/decl/flooring/snow/get_vehicle_transit_delay(obj/vehicle/vehicle) + return vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE ? 0.8 : 1.7 + /decl/flooring/permafrost name = "permafrost" desc = "A stretch of frozen soil that hasn't seen a thaw for many seasons." @@ -55,8 +59,16 @@ force_material = /decl/material/solid/ice uid = "floor_permafrost" +/decl/flooring/permafrost/get_vehicle_transit_delay(obj/vehicle/vehicle) + if(holographic) + return vehicle::base_speed + if(vehicle.vehicle_transit_type == vehicle::VEHICLE_SNOWMOBILE) + return 0.8 + return ..() + /decl/flooring/snow/fake name = "holosnow" desc = "Not quite the same as snow on an entertainment terminal, but close." holographic = TRUE uid = "floor_snow_fake" + diff --git a/code/game/turfs/floors/_floor.dm b/code/game/turfs/floors/_floor.dm index 6739729434c2..ece70741f5c2 100644 --- a/code/game/turfs/floors/_floor.dm +++ b/code/game/turfs/floors/_floor.dm @@ -199,3 +199,9 @@ /turf/floor/can_show_coating_footprints(decl/material/contaminant = null) return ..() && get_topmost_flooring()?.can_show_coating_footprints(src, contaminant) + +/turf/floor/proc/get_vehicle_transit_delay(obj/vehicle/vehicle) + var/decl/flooring/terrain = get_topmost_flooring() + if(!istype(vehicle) || QDELETED(vehicle) || !istype(terrain) || vehicle.vehicle_transit_type == vehicle::VEHICLE_GENERIC) + return vehicle::base_speed + return terrain.get_vehicle_transit_delay(vehicle) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index fdc9684e137f..d2b367402d54 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -7,6 +7,9 @@ temperature_sensitive = TRUE atom_flags = ATOM_FLAG_OPEN_CONTAINER + // Linear lazylist of weakrefs to dangerous things on this turf. + var/list/dangerous_objects + /// Will participate in ZAS, join zones, etc. var/zone_membership_candidate = FALSE /// Will participate in external atmosphere simulation if the turf is outside and no zone is set. @@ -937,3 +940,24 @@ /turf/take_vaporized_reagent(reagent, amount) return assume_gas(reagent, round(amount / REAGENT_UNITS_PER_GAS_MOLE)) + +// Tells the turf that it currently contains something that automated movement should consider if planning to enter the tile. +// This uses lazy list macros to reduce memory footprint, since for 99% of turfs the list would've been empty anyways. +/turf/proc/register_dangerous_object(atom/thing) + if(!istype(thing)) + return FALSE + LAZYDISTINCTADD(dangerous_objects, weakref(thing)) + +// Similar to above, for when the dangerous object stops being dangerous/gets deleted/moved/etc. +/turf/proc/unregister_dangerous_object(atom/thing) + if(!istype(thing)) + return FALSE + LAZYREMOVE(dangerous_objects, weakref(thing)) + +/turf/proc/is_safe_to_enter(mob/living/stepper) + if(LAZYLEN(dangerous_objects)) + for(var/weakref/ref in dangerous_objects) + var/atom/thing = ref.resolve() + if(istype(thing) && !QDELETED(thing) && !thing.is_safe_to_step(stepper)) + return FALSE + return TRUE diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 5c849feec9a2..ee3311a90cb5 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -80,6 +80,7 @@ var/old_ambient_light_old_r = ambient_light_old_r var/old_ambient_light_old_g = ambient_light_old_g var/old_ambient_light_old_b = ambient_light_old_b + var/old_dangerous_objects = dangerous_objects var/old_zone_membership_candidate = zone_membership_candidate @@ -104,6 +105,7 @@ // Set our observation bookkeeping lists back. changed_turf.event_listeners = old_event_listeners changed_turf._listening_to = old_listening_to + changed_turf.dangerous_objects = old_dangerous_objects changed_turf.affecting_heat_sources = old_affecting_heat_sources diff --git a/code/game/turfs/walls/_wall.dm b/code/game/turfs/walls/_wall.dm index 1d26dbb74a65..3706a99fcfa1 100644 --- a/code/game/turfs/walls/_wall.dm +++ b/code/game/turfs/walls/_wall.dm @@ -129,8 +129,10 @@ var/global/list/wall_fullblend_objects = list( var/proj_damage = Proj.get_structure_damage() - if(Proj.ricochet_sounds && prob(15)) - playsound(src, pick(Proj.ricochet_sounds), 100, 1) + if(prob(15)) + var/list/ricochet_sounds = Proj.get_ricochet_sounds() + if(length(ricochet_sounds)) + playsound(src, pick(ricochet_sounds), 100, 1) if(reinf_material) if(Proj.atom_damage_type == BURN) diff --git a/code/modules/admin/verbs/grief_fixers.dm b/code/modules/admin/verbs/grief_fixers.dm index 9288165c9b4a..7fd10e57eb41 100644 --- a/code/modules/admin/verbs/grief_fixers.dm +++ b/code/modules/admin/verbs/grief_fixers.dm @@ -55,9 +55,8 @@ /decl/atmos_grief_fix_step/reset_turfs/act() var/list/unsorted_overlays = list() - var/list/all_gasses = decls_repository.get_decls_of_subtype(/decl/material/gas) - for(var/id in all_gasses) - var/decl/material/mat = all_gasses[id] + for(var/id,m in get_filterable_material_types()) + var/decl/material/mat = m unsorted_overlays |= mat.gas_tile_overlay for(var/turf/T in world) diff --git a/code/modules/atmospherics/components/unary/vent_scrubber.dm b/code/modules/atmospherics/components/unary/vent_scrubber.dm index 2a2aac82fd5e..58fdc8f1717e 100644 --- a/code/modules/atmospherics/components/unary/vent_scrubber.dm +++ b/code/modules/atmospherics/components/unary/vent_scrubber.dm @@ -64,7 +64,7 @@ id_tag = "[sequential_id("obj/machinery")]" if(!scrubbing_gas) scrubbing_gas = list() - for(var/g in decls_repository.get_decl_paths_of_subtype(/decl/material/gas)) + for(var/g in get_filterable_material_types()) if(g != /decl/material/gas/oxygen && g != /decl/material/gas/nitrogen) scrubbing_gas += g . = ..() diff --git a/code/modules/client/preference_setup/loadout/lists/footwear.dm b/code/modules/client/preference_setup/loadout/lists/footwear.dm index 72760aca59c6..b9990a778bac 100644 --- a/code/modules/client/preference_setup/loadout/lists/footwear.dm +++ b/code/modules/client/preference_setup/loadout/lists/footwear.dm @@ -26,7 +26,8 @@ /obj/item/clothing/shoes/workboots, /obj/item/clothing/shoes/jackboots/duty, /obj/item/clothing/shoes/jackboots/jungleboots, - /obj/item/clothing/shoes/jackboots/desertboots + /obj/item/clothing/shoes/jackboots/desertboots, + /obj/item/clothing/shoes/winterboots ) /decl/loadout_option/shoes/color diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 50adae6297ca..eb812a3b3b06 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -332,8 +332,14 @@ CHAMELEON_VERB(/obj/item/clothing/chameleon, "Change Accessory Appearance") P.icon = initial(copy_projectile.icon) P.icon_state = initial(copy_projectile.icon_state) P.pass_flags = initial(copy_projectile.pass_flags) + P.fire_sound = initial(copy_projectile.fire_sound) + P.silenced = copy_projectile.silenced + P.hitsound = initial(copy_projectile.hitsound) + P.hitsound_non_mob = initial(copy_projectile.hitsound_non_mob) P.hitscan = initial(copy_projectile.hitscan) P.step_delay = initial(copy_projectile.step_delay) + P.speed = initial(copy_projectile.speed) + P.range = initial(copy_projectile.range) P.muzzle_type = initial(copy_projectile.muzzle_type) P.tracer_type = initial(copy_projectile.tracer_type) P.impact_type = initial(copy_projectile.impact_type) diff --git a/code/modules/clothing/permits/_permit.dm b/code/modules/clothing/permits/_permit.dm index 1f42fce9e5cf..e3bb8aadda70 100644 --- a/code/modules/clothing/permits/_permit.dm +++ b/code/modules/clothing/permits/_permit.dm @@ -66,11 +66,6 @@ name = "bar shotgun permit" desc = "A card indicating that the owner is allowed to carry a shotgun in the bar." -/obj/item/clothing/permit/gun/planetside - name = "planetside weapon permit" - desc = "A card indicating that the owner is allowed to carry a weapon while on the surface." - detail_color = COLOR_PALE_PINK - /obj/item/clothing/permit/gun/paramedic name = "paramedic weapon permit" desc = "A card indicating that the owner is allowed to carry a weapon while on EVA retrieval missions." @@ -80,7 +75,3 @@ name = "holy weapon permit" desc = "A card indicating that the owner is allowed to carry a weapon for religious rites and purposes." detail_color = COLOR_GRAY15 - -/obj/item/clothing/permit/gun/planetside/exploration - name = "explorer weapon permit" - desc = "A card indicating that the owner is allowed to carry weaponry during active exploration missions." \ No newline at end of file diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index 589a7f0d79e1..8b90dd6119a0 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -17,6 +17,7 @@ fallback_slot = slot_shoes_str _base_attack_force = 5 + var/rock_climbing = FALSE var/can_fit_under_magboots = TRUE var/can_add_cuffs = TRUE var/obj/item/handcuffs/attached_cuffs = null diff --git a/code/modules/clothing/suits/_suit_hood.dm b/code/modules/clothing/suits/_suit_hood.dm index dc9dd9cad93a..42e02944dd17 100644 --- a/code/modules/clothing/suits/_suit_hood.dm +++ b/code/modules/clothing/suits/_suit_hood.dm @@ -1,21 +1,6 @@ /obj/item/clothing/suit var/obj/item/clothing/head/hood -/obj/item/clothing/suit/get_hood() - if(istype(hood)) - return hood - return ..() - -/obj/item/clothing/suit/set_material(new_material) - . = ..() - if(. && istype(hood)) - hood.set_material(new_material) - -/obj/item/clothing/suit/set_color(new_color) - . = ..() - if(. && istype(hood)) - hood.set_color(new_color) - /obj/item/clothing/suit/Initialize() if(ispath(hood)) hood = new hood(src) @@ -41,6 +26,26 @@ QDEL_NULL(hood) return ..() +/obj/item/clothing/suit/get_contained_external_atoms() + . = ..() + if(hood && .) + LAZYREMOVE(., hood) + +/obj/item/clothing/suit/get_hood() + if(istype(hood)) + return hood + return ..() + +/obj/item/clothing/suit/set_material(new_material) + . = ..() + if(. && istype(hood)) + hood.set_material(new_material) + +/obj/item/clothing/suit/set_color(new_color) + . = ..() + if(. && istype(hood)) + hood.set_color(new_color) + /obj/item/clothing/suit/equipped(mob/user, slot) if(slot != slot_wear_suit_str) remove_hood() diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm index 39f8dc118d92..b32701c519b4 100644 --- a/code/modules/error_handler/error_handler.dm +++ b/code/modules/error_handler/error_handler.dm @@ -8,6 +8,37 @@ var/global/regex/actual_error_file_line log_world("\[[time_stamp()]] Uncaught exception: [E]") return ..() + //this is snowflake because of a byond bug (ID:2306577), do not attempt to call non-builtin procs in this block OR BEFORE IT + if(copytext(E.name, 1, 32) == "Maximum recursion level reached")//32 == length() of that string + 1 + var/list/proc_path_to_count = list() + var/crashed = FALSE + try + var/callee/stack_entry = caller + while(!isnull(stack_entry)) + proc_path_to_count[stack_entry.proc] += 1 + stack_entry = stack_entry.caller + catch + //union job. avoids crashing the stack again + //I just do not trust this construct to work reliably + crashed = TRUE + + var/list/split = splittext(E.desc, "\n") + for (var/i in 1 to split.len) + if (split[i] != "" || copytext(split[1], 1, 2) != " ") + split[i] = " [split[i]]" + split += "--Stack Info [crashed ? "(Crashed, may be missing info)" : ""]:" + for(var/path in proc_path_to_count) + split += " [path] = [proc_path_to_count[path]]" + E.desc = jointext(split, "\n") + // expanding the first line of log_world to avoid hitting the stack limit again + to_file(world.log, "\[[time2text(station_time_in_ticks, "hh:mm:ss")]] Runtime Error: [E.name]\n[E.desc]") + //log to world while intentionally triggering the byond bug. this does not DO anything, it just errors + //(seemingly because log_info_line is deep enough to hit the raised limit 516.1667 introduced) + log_world("runtime error: [E.name]\n[E.desc]\n[log_info_line(usr)]\n[log_info_line(src)]") + //if we got to here without silently ending, the byond bug has been fixed. + log_world("The \"bug\" with recursion runtimes has been fixed. Please remove the snowflake check from world/Error in [__FILE__]:[__LINE__]") + return //this will never happen. + if (!global.actual_error_file_line) global.actual_error_file_line = regex("^%% (.*?),(.*?) %% ") diff --git a/code/modules/fabrication/designs/general/designs_arms_ammo.dm b/code/modules/fabrication/designs/general/designs_arms_ammo.dm index eb92c1ef0f3f..2fd82b739386 100644 --- a/code/modules/fabrication/designs/general/designs_arms_ammo.dm +++ b/code/modules/fabrication/designs/general/designs_arms_ammo.dm @@ -81,4 +81,8 @@ /datum/fabricator_recipe/arms_ammo/hidden/speedloader_laser name = "ammunition (speedloader, laserbulb)" - path = /obj/item/ammo_magazine/speedloader/laser_revolver \ No newline at end of file + path = /obj/item/ammo_magazine/speedloader/laser_revolver + +/datum/fabricator_recipe/arms_ammo/hidden/mine_assembly + name = "mine assembly" + path = /obj/item/mine/assembly diff --git a/code/modules/fabrication/designs/general/designs_devices_components.dm b/code/modules/fabrication/designs/general/designs_devices_components.dm index aa961aef563e..dd732867b353 100644 --- a/code/modules/fabrication/designs/general/designs_devices_components.dm +++ b/code/modules/fabrication/designs/general/designs_devices_components.dm @@ -5,9 +5,6 @@ /datum/fabricator_recipe/device_component/keyboard path = /obj/item/stock_parts/keyboard -/datum/fabricator_recipe/device_component/cataloguer - path = /obj/item/cataloguer - /datum/fabricator_recipe/device_component/pda path = /obj/item/modular_computer/pda diff --git a/code/modules/item_effects/item_effect_charges.dm b/code/modules/item_effects/item_effect_charges.dm index 06e8126b16fe..ff68fdb348eb 100644 --- a/code/modules/item_effects/item_effect_charges.dm +++ b/code/modules/item_effects/item_effect_charges.dm @@ -12,52 +12,6 @@ /decl/item_effect/charges/on_examined(obj/item/item, mob/user) to_chat(user, SPAN_NOTICE("\The [item] has [item.get_item_effect_parameter(src, IE_CAT_RANGED, IE_PAR_USES) || 0] charge\s of [effect_descriptor] left.")) -/obj/item/projectile/fireball - name = "fireball" - icon_state = "fireball" - fire_sound = 'sound/effects/bamf.ogg' - damage = 20 - atom_damage_type = BURN - damage_flags = DAM_DISPERSED // burn all over - var/fire_lifetime = 2 SECONDS - var/fire_temperature = (288 CELSIUS) / 0.9 + 1 // hot enough to ignite wood! divided by 0.9 and plus one to ensure we can light firepits - -/obj/effect/fake_fire/variable - name = "fire" - anchored = TRUE - mouse_opacity = MOUSE_OPACITY_UNCLICKABLE - firelevel = 1 - pressure = ONE_ATMOSPHERE - -/obj/effect/fake_fire/variable/Initialize(ml, new_temperature, new_lifetime) - lifetime = new_lifetime - last_temperature = new_temperature - return ..() - -// we deal our damage via fire_act, not via direct burn damage. our burn damage is specifically for mobs -/obj/item/projectile/fireball/get_structure_damage() - return 0 - -/obj/item/projectile/fireball/on_impact(var/atom/A) - . = ..() - var/obj/effect/fake_fire/fire = new /obj/effect/fake_fire/variable(get_turf(A), fire_temperature, fire_lifetime) - fire.Process() // process at least once! - qdel_self() - -/obj/item/projectile/fireball/after_move() - . = ..() - if(!loc) - return - for(var/mob/living/victim in loc) - if(!victim.simulated) - continue - victim.FireBurn(1, fire_temperature, ONE_ATMOSPHERE) - loc.fire_act(1, fire_temperature, ONE_ATMOSPHERE) - for(var/atom/burned in loc) - if(!burned.simulated || burned == src) - continue - burned.fire_act(1, fire_temperature, ONE_ATMOSPHERE) - // Example effect; casts a fireball n times. /decl/item_effect/charges/fireball effect_descriptor = "fireball" diff --git a/code/modules/lighting/_lighting_defs.dm b/code/modules/lighting/_lighting_defs.dm index f43ef762a6b3..a49d409fba9a 100644 --- a/code/modules/lighting/_lighting_defs.dm +++ b/code/modules/lighting/_lighting_defs.dm @@ -7,27 +7,39 @@ // As such this all gets counted as a single line. // The braces and semicolons are there to be able to do this on a single line. -#define APPLY_CORNER(C,now,Tx,Ty,hdiff) \ - . = LUM_FALLOFF(C.x, C.y, Tx, Ty, hdiff) * light_power; \ - var/OLD = effect_str[C]; \ - effect_str[C] = .; \ - C.update_lumcount \ - ( \ - (. * lum_r) - (OLD * applied_lum_r), \ - (. * lum_g) - (OLD * applied_lum_g), \ - (. * lum_b) - (OLD * applied_lum_b), \ - now \ +#define APPLY_CORNER(C,now,Tx,Ty,hdiff) \ + . = LUM_FALLOFF(C.x, C.y, Tx, Ty, hdiff) * light_power; \ + var/OLD = effect_str[C]; \ + effect_str[C] = .; \ + C.update_lumcount \ + ( \ + (. * lum_r) - (OLD * applied_lum_r), \ + (. * lum_g) - (OLD * applied_lum_g), \ + (. * lum_b) - (OLD * applied_lum_b), \ + now \ + ); + +// Like APPLY_CORNER, but very slightly faster, for the cases where we know there's no previous value. +#define INIT_CORNER(C,now,Tx,Ty,hdiff) \ + . = LUM_FALLOFF(C.x, C.y, Tx, Ty, hdiff) * light_power; \ + effect_str[C] = .; \ + C.update_lumcount \ + ( \ + . * lum_r, \ + . * lum_g, \ + . * lum_b, \ + now \ ); // I don't need to explain what this does, do I? -#define REMOVE_CORNER(C,now) \ - . = -effect_str[C]; \ - C.update_lumcount \ - ( \ - . * applied_lum_r, \ - . * applied_lum_g, \ - . * applied_lum_b, \ - now \ +#define REMOVE_CORNER(C,now) \ + . = -effect_str[C]; \ + C.update_lumcount \ + ( \ + . * applied_lum_r, \ + . * applied_lum_g, \ + . * applied_lum_b, \ + now \ ); // Converts two Z levels into a height value for LUM_FALLOFF or HEIGHT_FALLOFF. @@ -41,3 +53,12 @@ corner_height = LIGHTING_HEIGHT; \ } \ APPLY_CORNER(C, now, Sx, Sy, corner_height); + +#define INIT_CORNER_BY_HEIGHT(now) \ + if (C.z != Sz) { \ + corner_height = CALCULATE_CORNER_HEIGHT(C.z, Sz); \ + } \ + else { \ + corner_height = LIGHTING_HEIGHT; \ + } \ + INIT_CORNER(C, now, Sx, Sy, corner_height); \ No newline at end of file diff --git a/code/modules/lighting/ambient_turf.dm b/code/modules/lighting/ambient_turf.dm index 4b34c863ed90..60992f1fcb5d 100644 --- a/code/modules/lighting/ambient_turf.dm +++ b/code/modules/lighting/ambient_turf.dm @@ -4,9 +4,6 @@ /// The power of the above is multiplied by this. Setting too high may drown out normal lights on the same turf. var/ambient_light_multiplier = 0.3 - /// If this is TRUE, an above turf's ambient light is affecting this turf. - var/tmp/ambient_has_indirect = FALSE - // Record-keeping, do not touch -- that means you, admins. var/tmp/ambient_active = FALSE //! Do we have non-zero ambient light? Use [TURF_IS_AMBIENT_LIT] instead of reading this directly. var/tmp/ambient_light_old_r = 0 diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 639b0a023872..ea6f99e9a915 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -272,8 +272,6 @@ var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, // We init before Z-Mimic, cannot rely on above/below. while ((T = GET_BELOW(T)) && ((below.t1?.z_flags | below.t2?.z_flags | below.t3?.z_flags | below.t4?.z_flags) & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) - T.ambient_has_indirect = TRUE - if (!T.corners || !T.corners[Ti]) T.generate_missing_corners() diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 5ebc210383ea..84bccc897573 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -225,7 +225,7 @@ REMOVE_CORNER(C,now) effect_str[C] = 0 - var/actual_range = light_range + var/actual_range = (light_angle && facing_opaque) ? light_range * LIGHTING_BLOCKED_FACTOR : light_range var/Sx = pixel_turf.x var/Sy = pixel_turf.y @@ -335,7 +335,6 @@ var/list/datum/lighting_corner/corners = list() var/list/turf/turfs = list() - var/thing var/datum/lighting_corner/C var/turf/T var/list/Tcorners @@ -358,8 +357,9 @@ if ((DETERMINANT(limit_a_x, limit_a_y, test_x, test_y) > 0) || DETERMINANT(test_x, test_y, limit_b_x, limit_b_y) > 0) continue - if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) || T.light_source_solo || T.light_source_multi) - Tcorners = T.corners + Tcorners = T.corners + // These checks are inlined from generate_missing_corners. They must be kept in sync. + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) || T.light_source_solo || T.light_source_multi || (T.z_flags & ZM_ALLOW_LIGHTING)) if (!T.lighting_corners_initialised) T.lighting_corners_initialised = TRUE @@ -373,11 +373,11 @@ Tcorners[i] = new /datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[i], i) - if (!T.has_opaque_atom) - for (var/v in 1 to 4) - var/val = Tcorners[v] - if (val) - corners[val] = 0 + if (Tcorners && !T.has_opaque_atom) + for (var/v in 1 to 4) + var/val = Tcorners[v] + if (val) + corners[val] = 0 turfs += T @@ -390,39 +390,34 @@ var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them. affecting_turfs += L - for (thing in L) - T = thing + for (T as anything in L) LAZYADD(T.affecting_lights, src) L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights. affecting_turfs -= L - for (thing in L) - T = thing + for (T as anything in L) LAZYREMOVE(T.affecting_lights, src) LAZYINITLIST(effect_str) if (needs_update == LIGHTING_VIS_UPDATE) - for (thing in corners - effect_str) - C = thing + for (C as anything in corners - effect_str) // newly added corners LAZYADD(C.affecting, src) if (!C.active) effect_str[C] = 0 continue - APPLY_CORNER_BY_HEIGHT(now) + INIT_CORNER_BY_HEIGHT(now) else L = corners - effect_str - for (thing in L) - C = thing + for (C as anything in L) LAZYADD(C.affecting, src) if (!C.active) effect_str[C] = 0 continue - APPLY_CORNER_BY_HEIGHT(now) + INIT_CORNER_BY_HEIGHT(now) - for (thing in corners - L) - C = thing + for (C as anything in corners - L) if (!C.active) effect_str[C] = 0 continue @@ -430,8 +425,7 @@ APPLY_CORNER_BY_HEIGHT(now) L = effect_str - corners - for (thing in L) - C = thing + for (C as anything in L) REMOVE_CORNER(C, now) LAZYREMOVE(C.affecting, src) diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 08d7fadca5c6..5543d1166c1b 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -143,7 +143,8 @@ // This is inlined in lighting_source.dm. // Update it too if you change this. /turf/proc/generate_missing_corners() - if (!TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && !light_source_solo && !light_source_multi && !(z_flags & ZM_ALLOW_LIGHTING) && !ambient_light && !ambient_has_indirect) + // If a turf is dynamically lit, has a light source, or mimics lighting, it needs to have corners created. + if (!TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && !light_source_solo && !light_source_multi && !(z_flags & ZM_ALLOW_LIGHTING)) return lighting_corners_initialised = TRUE diff --git a/code/modules/maps/template_types/random_exoplanet/fauna_generator.dm b/code/modules/maps/template_types/random_exoplanet/fauna_generator.dm index c3b05683b86b..08cbad00e174 100644 --- a/code/modules/maps/template_types/random_exoplanet/fauna_generator.dm +++ b/code/modules/maps/template_types/random_exoplanet/fauna_generator.dm @@ -126,7 +126,7 @@ /datum/fauna_generator/proc/generate_breathable_gases(var/datum/gas_mixture/atmosphere, var/list/breath_gases, var/list/toxic_gases) //Set up gases for living things - var/list/all_gasses = decls_repository.get_decl_paths_of_subtype(/decl/material/gas) + var/list/all_gasses = get_filterable_material_types(as_list = TRUE) if(!length(breath_gases)) var/list/goodgases = all_gasses.Copy() var/gasnum = min(rand(1,3), goodgases.len) diff --git a/code/modules/maps/template_types/random_exoplanet/planet_themes/robotic_guardians.dm b/code/modules/maps/template_types/random_exoplanet/planet_themes/robotic_guardians.dm index 3e8c60a5d1a7..d9d6608eac8a 100644 --- a/code/modules/maps/template_types/random_exoplanet/planet_themes/robotic_guardians.dm +++ b/code/modules/maps/template_types/random_exoplanet/planet_themes/robotic_guardians.dm @@ -2,7 +2,7 @@ name = "Robotic Guardians" var/list/guardian_types = list( /mob/living/simple_animal/hostile/hivebot, - /mob/living/simple_animal/hostile/hivebot/range, + /mob/living/simple_animal/hostile/hivebot/ranged, /mob/living/simple_animal/hostile/viscerator/hive ) var/list/mega_guardian_types = list( @@ -24,4 +24,4 @@ A.faction = "Ancient Guardian" /datum/exoplanet_theme/robotic_guardians/get_sensor_data() - return "Movement without corresponding lifesigns detected on the surface." \ No newline at end of file + return "Movement without corresponding lifesigns detected on the surface." diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index 2ed880fd3b89..920e63862dc2 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -1,3 +1,20 @@ +var/global/alist/_filterable_mats_alist +var/global/list/_filterable_mats_list + +/proc/get_filterable_material_types(as_list = FALSE) + + if(isnull(_filterable_mats_alist)) + _filterable_mats_alist = alist() + for(var/decl/material/mat in decls_repository.get_decls_of_subtype_unassociated(/decl/material)) + if(!isnull(mat.boiling_point)) + _filterable_mats_alist[mat.type] = mat + + if(as_list) + if(isnull(_filterable_mats_list)) + _filterable_mats_list = alist2list(_filterable_mats_alist) + return _filterable_mats_list + return _filterable_mats_alist + /* MATERIAL DECLS This data is used by various parts of the game for basic physical properties and behaviors diff --git a/code/modules/materials/material_debris_fragment.dm b/code/modules/materials/material_debris_fragment.dm new file mode 100644 index 000000000000..d34e944b6dc3 --- /dev/null +++ b/code/modules/materials/material_debris_fragment.dm @@ -0,0 +1,47 @@ +/obj/item/debris/salvage + abstract_type = /obj/item/debris/salvage + icon_state = ICON_STATE_WORLD + material_alteration = MAT_FLAG_ALTERATION_NONE + is_spawnable_type = TRUE + +/obj/item/debris/salvage/metal + name = "fragment" + desc = "A large, ragged chunk of some worked material." + icon = 'icons/obj/debris_metal.dmi' + material_alteration = MAT_FLAG_ALTERATION_ALL + material = /decl/material/solid/metal/steel + +/obj/item/debris/salvage/metal/Initialize(ml, material_key) + . = ..() + icon_state = "[icon_state][rand(0, 4)]" + +/obj/item/debris/salvage/metal/plasteel + material = /decl/material/solid/metal/plasteel + +/obj/item/debris/salvage/circuit + name = "broken circuit" + desc = "A burned-out circuitboard. Only good for the base materials now." + icon = 'icons/obj/debris_circuit.dmi' + material = /decl/material/solid/fiberglass + matter = list( + /decl/material/solid/organic/plastic = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/metal/gold = MATTER_AMOUNT_TRACE + ) + +/obj/item/debris/salvage/circuit/Initialize(ml, material_key) + . = ..() + icon_state = "[icon_state][rand(0, 3)]" + +/obj/item/debris/salvage/device + name = "broken device" + desc = "A destroyed device of some kind. Only good for recycling now." + icon = 'icons/obj/debris_device.dmi' + material = /decl/material/solid/metal/aluminium + matter = list( + /decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/metal/gold = MATTER_AMOUNT_TRACE + ) + +/obj/item/debris/salvage/device/Initialize(ml, material_key) + . = ..() + icon_state = "[icon_state][rand(0, 3)]" diff --git a/code/modules/mob/living/human/human.dm b/code/modules/mob/living/human/human.dm index f83f689f0392..ee819f1252e6 100644 --- a/code/modules/mob/living/human/human.dm +++ b/code/modules/mob/living/human/human.dm @@ -1145,3 +1145,7 @@ robolimb_count++ full_prosthetic = robolimb_count > 0 && (robolimb_count == LAZYLEN(limbs)) //If no organs, no way to tell return full_prosthetic + +// Don't tag your crewmates please. +/mob/living/human/is_tagging_suitable() + return FALSE diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 956ae903000c..b6f121f4403b 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2001,3 +2001,17 @@ default behaviour is: /mob/living/is_cloaked() return has_mob_modifier(/decl/mob_modifier/cloaked) + +/mob/living/proc/apply_random_mutation(radiation_amount) + set_unique_enzymes(num2text(random_id(/mob, 1000000, 9999999))) + if(prob(98)) + add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/disability))) + else + add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/superpower))) + if(radiation_amount) + apply_damage(radiation_amount, IRRADIATE, armor_pen = 100) + +// Used by specimen taggers to avoid tagging/overwriting players or named mobs like Runtime. +/mob/living/proc/is_tagging_suitable() + return !key && !client + diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index a870b6c2c263..e94c963c06fd 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -28,7 +28,7 @@ visible_message(SPAN_WARNING("[src] triggers their deadman's switch!")) signaler.signal() //Armor - var/damage = P.damage + var/damage = P.get_projectile_damage(src) var/flags = P.damage_flags() var/damaged if(!P.nodamage) @@ -42,7 +42,8 @@ // For visuals and blood splatters etc /mob/living/proc/bullet_impact_visuals(var/obj/item/projectile/P, var/def_zone, var/damage) - var/list/impact_sounds = LAZYACCESS(P.impact_sounds, get_bullet_impact_effect_type(def_zone)) + var/list/all_impact_sounds = P.get_impact_sounds() + var/list/impact_sounds = LAZYACCESS(all_impact_sounds, get_bullet_impact_effect_type(def_zone)) if(length(impact_sounds)) playsound(src, pick(impact_sounds), 75) if(get_bullet_impact_effect_type(def_zone) != BULLET_IMPACT_MEAT) diff --git a/code/modules/mob/living/silicon/ai/ai_radio.dm b/code/modules/mob/living/silicon/ai/ai_radio.dm index 7eff03a67438..7a195abc9bac 100644 --- a/code/modules/mob/living/silicon/ai/ai_radio.dm +++ b/code/modules/mob/living/silicon/ai/ai_radio.dm @@ -23,6 +23,5 @@ access_medical, access_cargo, access_bar, - access_ai_upload, - access_explorer + access_ai_upload ) diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index c8da4e16f144..de977e45c639 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -193,7 +193,7 @@ name = "personal shielding" desc = "A powerful experimental module that turns aside or absorbs incoming attacks at the cost of charge." icon = 'icons/obj/signs/warnings.dmi' - icon_state = "shock" + icon_state = "shock-large" var/shield_level = 0.5 //Percentage of damage absorbed by the shield. /obj/item/borg/combat/shield/verb/set_shield_level() @@ -209,7 +209,7 @@ name = "mobility module" desc = "By retracting limbs and tucking in its head, a combat android can roll at high speeds." icon = 'icons/obj/signs/warnings.dmi' - icon_state = "shock" + icon_state = "shock-large" /obj/item/inflatable_dispenser name = "inflatables dispenser" diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 36f1ba370c4c..b5508d6d2217 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -127,9 +127,9 @@ if(!Proj.nodamage) switch(Proj.atom_damage_type) if(BRUTE) - take_damage(Proj.damage) + take_damage(Proj.get_projectile_damage(src)) if(BURN) - take_damage(Proj.damage, BURN) + take_damage(Proj.get_projectile_damage(src), BURN) Proj.on_hit(src,100) //wow this is a terrible hack return 100 diff --git a/code/modules/mob/living/simple_animal/_simple_animal.dm b/code/modules/mob/living/simple_animal/_simple_animal.dm index a099671239ce..da62a72958eb 100644 --- a/code/modules/mob/living/simple_animal/_simple_animal.dm +++ b/code/modules/mob/living/simple_animal/_simple_animal.dm @@ -1,4 +1,3 @@ - /mob/living/simple_animal name = "animal" max_health = 20 @@ -72,9 +71,6 @@ var/bleed_colour = COLOR_BLOOD_HUMAN var/can_bleed = TRUE - // contained in a cage - var/in_stasis = 0 - //for simple animals with abilities, mostly megafauna var/ability_cooldown diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 99e38a6baa01..cfaaab05eb04 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -194,6 +194,9 @@ butchery_data = /decl/butchery_data/animal/cat/black holder_type = /obj/item/holder/runtime +/mob/living/simple_animal/passive/cat/fluff/runtime/is_tagging_suitable() + return FALSE + /obj/item/holder/runtime origin_tech = @'{"programming":1,"biotech":1}' diff --git a/code/modules/mob/living/simple_animal/friendly/possum.dm b/code/modules/mob/living/simple_animal/friendly/possum.dm index ef8c318ade27..9b77858189e3 100644 --- a/code/modules/mob/living/simple_animal/friendly/possum.dm +++ b/code/modules/mob/living/simple_animal/friendly/possum.dm @@ -106,6 +106,9 @@ can_buckle = TRUE var/aaa_words = list("delaminat", "meteor", "fire", "breach") +/mob/living/simple_animal/opossum/poppy/is_tagging_suitable() + return FALSE + /mob/living/simple_animal/opossum/poppy/hear_broadcast(decl/language/language, mob/speaker, speaker_name, message) . = ..() addtimer(CALLBACK(src, PROC_REF(check_keywords), message), rand(1 SECOND, 3 SECONDS)) diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm index d958928874ef..6566f241e2f0 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/_hivebot.dm @@ -1,11 +1,9 @@ /mob/living/simple_animal/hostile/hivebot name = "hivebot" desc = "A junky looking robot with four spiky legs." - icon = 'icons/mob/simple_animal/hivebot.dmi' + icon = 'icons/mob/simple_animal/hivebots/hivebot_green.dmi' max_health = 55 natural_weapon = /obj/item/natural_weapon/drone_slicer - projectilesound = 'sound/weapons/gunshot/gunshot_pistol.ogg' - projectiletype = /obj/item/projectile/beam/smalllaser faction = "hivebot" min_gas = null max_gas = null diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm index ead786ab340c..4dec711741ca 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/megabot.dm @@ -5,7 +5,7 @@ /mob/living/simple_animal/hostile/hivebot/mega name = "hivemind" desc = "A huge quadruped robot equipped with a myriad of weaponry." - icon = 'icons/mob/simple_animal/megabot.dmi' + icon = 'icons/mob/simple_animal/hivebots/megabot.dmi' max_health = 440 natural_weapon = /obj/item/natural_weapon/circular_saw natural_armor = list( diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/melee/_melee.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/melee/_melee.dm new file mode 100644 index 000000000000..42d3f588fbec --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/melee/_melee.dm @@ -0,0 +1,20 @@ +/mob/living/simple_animal/hostile/hivebot/melee + natural_weapon = /obj/item/natural_weapon/drone_slicer/prod + projectiletype = null // To force the AI to melee. + base_movement_delay = 10 + +/obj/item/natural_weapon/drone_slicer/prod + name = "hivebot prod" + attack_verb = list("prodded") + hitsound = 'sound/weapons/Egloves.ogg' + +/mob/living/simple_animal/hostile/hivebot/melee/has_ranged_attack() + return FALSE + +// This one is tanky by having a massive amount of health. +/mob/living/simple_animal/hostile/hivebot/melee/meatshield + name = "bulky hivebot" + desc = "A large robot." + max_health = 300 + icon_scale_x = 1.1 + icon_scale_y = 1.1 diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/melee/armored.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/melee/armored.dm new file mode 100644 index 000000000000..6556736ec9c0 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/melee/armored.dm @@ -0,0 +1,61 @@ +// This one is tanky by having armor. +/mob/living/simple_animal/hostile/hivebot/melee/armored + name = "armored hivebot" + desc = "A robot clad in heavy armor." + icon = 'icons/mob/simple_animal/hivebots/hivebot_yellow.dmi' + max_health = 150 + icon_scale_x = 1.1 + icon_scale_y = 1.1 + natural_armor = list( + (ARMOR_MELEE) = ARMOR_MELEE_MAJOR, + (ARMOR_BULLET) = ARMOR_BALLISTIC_PISTOL, + (ARMOR_LASER) = ARMOR_LASER_HANDGUNS, + (ARMOR_ENERGY) = ARMOR_ENERGY_RESISTANT, + (ARMOR_BOMB) = ARMOR_BOMB_PADDED, + (ARMOR_BIO) = ARMOR_BIO_SHIELDED, + (ARMOR_RAD) = ARMOR_RAD_SHIELDED + ) + +/mob/living/simple_animal/hostile/hivebot/melee/armored/anti_melee + name = "riot hivebot" + desc = "A robot specialized in close quarters combat." + natural_armor = list( + (ARMOR_MELEE) = ARMOR_MELEE_VERY_HIGH, + (ARMOR_BIO) = ARMOR_BIO_SHIELDED, + (ARMOR_RAD) = ARMOR_RAD_SHIELDED + ) + +/mob/living/simple_animal/hostile/hivebot/melee/armored/anti_bullet + name = "bulletproof hivebot" + desc = "A robot specialized in ballistic defense." + natural_armor = list( + (ARMOR_BULLET) = ARMOR_BALLISTIC_RIFLE, + (ARMOR_BIO) = ARMOR_BIO_SHIELDED, + (ARMOR_RAD) = ARMOR_RAD_SHIELDED + ) + +/mob/living/simple_animal/hostile/hivebot/melee/armored/anti_laser + name = "ablative hivebot" + desc = "A robot specialized in photonic defense." + natural_armor = list( + (ARMOR_LASER) = ARMOR_LASER_RIFLES, + (ARMOR_BIO) = ARMOR_BIO_SHIELDED, + (ARMOR_RAD) = ARMOR_RAD_SHIELDED + ) + var/reflect_chance = 40 + +// Ablative Hivebots can reflect lasers just like humans. +/mob/living/simple_animal/hostile/hivebot/melee/armored/anti_laser/bullet_act(obj/item/projectile/P) + if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) + var/reflect_prob = reflect_chance - round(P.damage/3) + if(prob(reflect_prob)) + visible_message( + SPAN_DANGER("\The [P] is reflected by \the [src]'s armor!"), + SPAN_DANGER("\The [P] gets reflected by \the [src]'s armor!") + ) + if(P.starting) + var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + P.redirect(new_x, new_y, get_turf(src), src) + return PROJECTILE_CONTINUE + return (..(P)) diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/range.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/range.dm deleted file mode 100644 index 3d1ecb678aab..000000000000 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/range.dm +++ /dev/null @@ -1,6 +0,0 @@ -/mob/living/simple_animal/hostile/hivebot/range - desc = "A junky looking robot with four spiky legs. It's equipped with some kind of small-bore gun." - base_movement_delay = 2 - -/mob/living/simple_animal/hostile/hivebot/range/has_ranged_attack() - return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/ranged/_ranged.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/ranged/_ranged.dm new file mode 100644 index 000000000000..800b7b0fe90f --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/ranged/_ranged.dm @@ -0,0 +1,29 @@ +/mob/living/simple_animal/hostile/hivebot/ranged + desc = "A junky looking robot with four spiky legs. It's equipped with some kind of small-bore gun." + base_movement_delay = 2 + icon = 'icons/mob/simple_animal/hivebots/hivebot_white.dmi' + projectiletype = /obj/item/projectile/bullet/pellet + projectilesound = 'sound/weapons/gunshot/gunshot_pistol.ogg' + +/mob/living/simple_animal/hostile/hivebot/ranged/has_ranged_attack() + return TRUE + +/mob/living/simple_animal/hostile/hivebot/ranged/rapid + name = "rapid hivebot" + desc = "A robot with a crude but deadly integrated rifle." + attack_delay = 5 // Two attacks a second or so. + burst_projectile = TRUE + +/mob/living/simple_animal/hostile/hivebot/ranged/laser + name = "laser hivebot" + desc = "A robot with a photonic weapon integrated into itself." + projectiletype = /obj/item/projectile/beam/blue + projectilesound = 'sound/weapons/Laser.ogg' + +/mob/living/simple_animal/hostile/hivebot/ranged/heat + name = "ember hivebot" + desc = "A robot that appears to utilize fire to cook their enemies." + icon_state = "red" + icon = 'icons/mob/simple_animal/hivebots/hivebot_red.dmi' + projectiletype = /obj/item/projectile/fireball + projectilesound = 'sound/effects/bamf.ogg' diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/rapid.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/rapid.dm deleted file mode 100644 index 6cdc6ffbd322..000000000000 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/rapid.dm +++ /dev/null @@ -1,5 +0,0 @@ -/mob/living/simple_animal/hostile/hivebot/rapid - burst_projectile = TRUE - -/mob/living/simple_animal/hostile/hivebot/rapid/has_ranged_attack() - return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/strong.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/strong.dm deleted file mode 100644 index 4105339c6bd1..000000000000 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/strong.dm +++ /dev/null @@ -1,13 +0,0 @@ -/mob/living/simple_animal/hostile/hivebot/strong - desc = "A junky looking robot with four spiky legs - this one has thick armour plating." - max_health = 120 - natural_armor = list( - ARMOR_MELEE = ARMOR_MELEE_RESISTANT - ) - ai = /datum/mob_controller/aggressive/hivebot_strong - -/datum/mob_controller/aggressive/hivebot_strong - can_escape_buckles = TRUE - -/mob/living/simple_animal/hostile/hivebot/strong/has_ranged_attack() - return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index 3e450fd82b2c..97345aeccd78 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -4,7 +4,6 @@ name = "combat drone" desc = "An automated combat drone armed with state of the art weaponry and shielding." icon = 'icons/mob/simple_animal/drone_combat.dmi' - burst_projectile = 0 max_health = 300 move_intents = list( /decl/move_intent/walk/animal_slow, diff --git a/code/modules/mob/living/simple_animal/passive/mouse.dm b/code/modules/mob/living/simple_animal/passive/mouse.dm index 72d3815596e2..5139242d27ed 100644 --- a/code/modules/mob/living/simple_animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_animal/passive/mouse.dm @@ -117,6 +117,9 @@ SetName(initial(name)) real_name = name +/mob/living/simple_animal/passive/mouse/brown/Tom/is_tagging_suitable() + return FALSE + // rats, they're the rats (from Polaris) /mob/living/simple_animal/passive/mouse/rat name = "rat" diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index dadb2de1b99d..e0952f1222c5 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -713,7 +713,7 @@ var/global/const/ACTION_DANGER_ALL = 2 return TRUE if(!anchored && istype(over, /obj/vehicle/train)) var/obj/vehicle/train/beep = over - if(!beep.load(src)) + if(!beep.load_onto_vehicle(src)) to_chat(user, SPAN_WARNING("You were unable to load \the [src] onto \the [over].")) return TRUE . = ..() diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 27ae2d426d93..360f26b75557 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -5,6 +5,16 @@ if(DoMove(direction, src) & MOVEMENT_HANDLED) return TRUE // Doesn't necessarily mean the atom physically moved +/mob/living/SelfMove(var/direction) + // If on walk intent, don't willingly step into hazardous tiles. + // Unless the walker is confused. + var/turf/destination = get_step(src, direction) + if(istype(destination) && MOVING_DELIBERATELY(src) && !HAS_STATUS(src, STAT_CONFUSE)) + if(!destination.is_safe_to_enter(src)) + to_chat(src, SPAN_WARNING("\The [destination] is dangerous to move into.")) + return FALSE // In case any code wants to know if movement happened. + return ..() // Parent call should make the mob move. + /mob/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) . = current_posture.prone || ..() || !mover.density diff --git a/code/modules/modular_computers/networking/network_cable.dm b/code/modules/modular_computers/networking/network_cable.dm index 9522183087de..4ea853d32197 100644 --- a/code/modules/modular_computers/networking/network_cable.dm +++ b/code/modules/modular_computers/networking/network_cable.dm @@ -156,7 +156,8 @@ randpixel = 2 amount = 20 max_amount = 20 - singular_name = "length" + singular_name = "length of network cable" + plural_name = "lengths of network cable" w_class = ITEM_SIZE_NORMAL throw_speed = 2 throw_range = 5 diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 4240ed507b22..923bfe97a847 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -48,29 +48,27 @@ /mob/proc/can_overcome_gravity() return FALSE -/mob/living/human/can_overcome_gravity() +/mob/living/can_overcome_gravity() //First do species check - if(species && species.can_overcome_gravity(src)) - return 1 - else - var/turf/T = loc - if(((T.get_physical_height() + T.get_fluid_depth()) >= FLUID_DEEP) || T.get_fluid_depth() >= FLUID_MAX_DEPTH) - if(can_float()) - return 1 - - for(var/atom/a in src.loc) - if(a.atom_flags & ATOM_FLAG_CLIMBABLE) - return 1 - - //Last check, list of items that could plausibly be used to climb but aren't climbable themselves - var/list/objects_to_stand_on = list( - /obj/item/stool, - /obj/structure/bed, - ) - for(var/type in objects_to_stand_on) - if(locate(type) in src.loc) - return 1 - return 0 + var/decl/species/my_species = get_species() + if(my_species?.can_overcome_gravity(src)) + return TRUE + var/turf/T = loc + if(((T.get_physical_height() + T.get_fluid_depth()) >= FLUID_DEEP) || T.get_fluid_depth() >= FLUID_MAX_DEPTH) + if(can_float()) + return TRUE + for(var/atom/climbable in src.loc) + if((climbable.atom_flags & ATOM_FLAG_CLIMBABLE) && climbable.can_climb(src, silent = TRUE)) + return TRUE + //Last check, list of items that could plausibly be used to climb but aren't climbable themselves + var/static/list/objects_to_stand_on = list( + /obj/item/stool, + /obj/structure/bed, + ) + for(var/type in objects_to_stand_on) + if(locate(type) in src.loc) + return TRUE + return FALSE //FALLING STUFF diff --git a/code/modules/persistence/persistence_datum.dm b/code/modules/persistence/persistence_datum.dm index 08acd039c57a..fe95bf8248f3 100644 --- a/code/modules/persistence/persistence_datum.dm +++ b/code/modules/persistence/persistence_datum.dm @@ -11,6 +11,8 @@ var/has_admin_data // If set, shows up on the admin persistence panel. var/ignore_area_flags = FALSE // Set to TRUE to skip area flag checks such as nonpersistent areas. var/ignore_invalid_loc = FALSE // Set to TRUE to skip checking for a non-null station turf for the entry. + var/area_restricted = TRUE // Can this item persist outside of a flagged area? + var/station_restricted = TRUE // Can this item persist outside of a station level? /decl/persistence_handler/proc/SetFilename() if(name) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index c1ad6452b563..7067ecc7974b 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -515,7 +515,8 @@ var/global/list/obj/structure/cable/all_cables = list() color = COLOR_MAROON paint_color = COLOR_MAROON desc = "A coil of wiring, suitable for both delicate electronics and heavy-duty power supply." - singular_name = "length" + singular_name = "length of cable" + plural_name = "lengths of cable" w_class = ITEM_SIZE_NORMAL throw_speed = 2 throw_range = 5 @@ -620,11 +621,11 @@ var/global/list/obj/structure/cable/all_cables = list() if(distance > 1) return if(get_amount() == 1) - . += "\A [singular_name] of cable." + . += "\A [singular_name]." else if(get_amount() == 2) - . += "Two [plural_name] of cable." + . += "Two [plural_name]." else - . += "A coil of power cable. There are [get_amount()] [plural_name] of cable in the coil." + . += "A coil of power cable. There are [get_amount()] [plural_name] in the coil." /obj/item/stack/cable_coil/verb/make_restraint() set name = "Make Cable Restraints" @@ -634,11 +635,11 @@ var/global/list/obj/structure/cable/all_cables = list() if(ishuman(M) && !M.incapacitated()) if(!isturf(usr.loc)) return if(!src.use(15)) - to_chat(usr, SPAN_WARNING("You need at least 15 [plural_name] of cable to make restraints!")) + to_chat(usr, SPAN_WARNING("You need at least 15 [plural_name] to make restraints!")) return var/obj/item/handcuffs/cable/B = new /obj/item/handcuffs/cable(usr.loc) B.set_color(color) - to_chat(usr, SPAN_NOTICE("You wind some [plural_name] of cable together to make some restraints.")) + to_chat(usr, SPAN_NOTICE("You wind some [plural_name] together to make some restraints.")) else to_chat(usr, SPAN_NOTICE("You cannot do that.")) @@ -676,7 +677,7 @@ var/global/list/obj/structure/cable/all_cables = list() return if(get_amount() < 1) // Out of cable - to_chat(user, SPAN_WARNING("There is no [plural_name] of cable left.")) + to_chat(user, SPAN_WARNING("There is no [plural_name] left.")) return if(get_dist(F,user) > 1) // Too far @@ -696,7 +697,7 @@ var/global/list/obj/structure/cable/all_cables = list() var/end_dir = 0 if(istype(F) && F.is_open()) if(!can_use(2)) - to_chat(user, SPAN_WARNING("You don't have enough [plural_name] of cable to do this!")) + to_chat(user, SPAN_WARNING("You don't have enough [plural_name] to do this!")) return end_dir = DOWN @@ -840,6 +841,8 @@ var/global/list/obj/structure/cable/all_cables = list() ////////////////////////////// // Misc. ///////////////////////////// +/obj/item/stack/cable_coil/five + amount = 5 /obj/item/stack/cable_coil/cut item_state = "coil2" diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 91c1085e4327..13b739cf041a 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -46,6 +46,8 @@ pickup_sound = 'sound/foley/pickup2.ogg' can_be_twohanded = TRUE // also checks one_hand_penalty needs_attack_dexterity = DEXTERITY_WEAPONS + wieldsound = 'sound/weapons/TargetOn.ogg' + unwieldsound = 'sound/weapons/TargetOff.ogg' var/fire_verb = "fire" var/waterproof = FALSE @@ -94,6 +96,7 @@ /obj/item/gun/Initialize() // must have firemodes initialized prior to any update_icon_calls // including reconsider_single_icon(), which is done in ..() + LAZYINITLIST(firemodes) for(var/i in 1 to firemodes.len) firemodes[i] = new /datum/firemode(src, firemodes[i]) . = ..() @@ -544,7 +547,7 @@ shot_sound = P.fire_sound shot_sound_vol = P.fire_sound_vol if(silencer) - shot_sound_vol = 10 + shot_sound_vol = P.fire_sound_vol_silenced playsound(firer, shot_sound, shot_sound_vol, 1) diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index f7d2efa622be..7671a3a759ca 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -5,7 +5,7 @@ var/global/list/registered_cyborg_weapons = list() name = "energy gun" desc = "A basic energy-based gun." icon = 'icons/obj/guns/basic_energy.dmi' - icon_state = "energy" + icon_state = ICON_STATE_WORLD fire_sound = 'sound/weapons/Taser.ogg' fire_sound_text = "laser blast" accuracy = 1 diff --git a/code/modules/projectiles/guns/launcher/grenade_launcher.dm b/code/modules/projectiles/guns/launcher/grenade_launcher.dm index 1e1e7f0cda25..2fc0015f2667 100644 --- a/code/modules/projectiles/guns/launcher/grenade_launcher.dm +++ b/code/modules/projectiles/guns/launcher/grenade_launcher.dm @@ -108,9 +108,38 @@ return FALSE return TRUE -// For uplink purchase, comes loaded with a random assortment of grenades +/obj/item/gun/launcher/grenade/loaded + var/initial_load_type + /obj/item/gun/launcher/grenade/loaded/Initialize() . = ..() + if(initial_load_type) + chambered = new initial_load_type(src) + LAZYINITLIST(grenades) + for(var/i = 1 to max_grenades) + grenades += new initial_load_type(src) + +/obj/item/gun/launcher/grenade/loaded/anti_photon + initial_load_type = /obj/item/grenade/anti_photon + +/obj/item/gun/launcher/grenade/loaded/smoke + initial_load_type = /obj/item/grenade/smokebomb + +/obj/item/gun/launcher/grenade/loaded/teargas + initial_load_type = /obj/item/grenade/chem_grenade/teargas + +/obj/item/gun/launcher/grenade/loaded/flashbang + initial_load_type = /obj/item/grenade/flashbang + +/obj/item/gun/launcher/grenade/loaded/emp + initial_load_type = /obj/item/grenade/empgrenade + +/obj/item/gun/launcher/grenade/loaded/frag + initial_load_type = /obj/item/grenade/frag/shell + +// For uplink purchase, comes loaded with a random assortment of grenades +/obj/item/gun/launcher/grenade/random/Initialize() + . = ..() var/list/grenade_types = list( /obj/item/grenade/anti_photon = 2, diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 4feed2fd06c9..d36ec1783cc9 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -25,6 +25,10 @@ var/proj_trail_icon_state = "trail" /// Any extant trail effects. var/list/proj_trails + /// An effect to spawn when a non-hitscan projectile collides with a target. + var/impact_effect_type + /// A sound to play when striking a non-mob (hitsound is used for mobs) + var/hitsound_non_mob var/bumped = 0 //Prevents it from hitting more than one guy at once var/def_zone = "" //Aiming at @@ -73,6 +77,7 @@ var/fire_sound var/fire_sound_vol = 50 + var/fire_sound_vol_silenced = 10 var/miss_sounds var/ricochet_sounds var/list/impact_sounds //for different categories, IMPACT_MEAT etc @@ -131,6 +136,10 @@ //called when the projectile stops flying because it collided with something /obj/item/projectile/proc/on_impact(var/atom/A) + + impact_sounds(A) + impact_visuals(A) + if(damage && atom_damage_type == BURN) var/turf/T = get_turf(A) if(T) @@ -208,6 +217,7 @@ //Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying. /obj/item/projectile/proc/attack_mob(var/mob/living/target_mob, var/distance, var/special_miss_modifier=0) + SHOULD_CALL_PARENT(TRUE) if(!istype(target_mob)) return @@ -234,13 +244,18 @@ if(result == PROJECTILE_FORCE_MISS) if(!silenced) target_mob.visible_message("\The [src] misses [target_mob] narrowly!") + var/list/miss_sounds = get_miss_sounds() if(LAZYLEN(miss_sounds)) playsound(target_mob.loc, pick(miss_sounds), 60, 1) return FALSE //hit messages if(silenced) - to_chat(target_mob, "You've been hit in the [parse_zone(def_zone)] by \the [src]!") + to_chat(target_mob, SPAN_DANGER("You've been hit in the [parse_zone(def_zone)] by \the [src]!")) + if(hitsound) + var/impact_volume = get_impact_volume_by_damage() + if(impact_volume) + playsound(target_mob, hitsound, impact_volume, 1, -1) else target_mob.visible_message("\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter @@ -672,3 +687,61 @@ /obj/item/projectile/proc/update_effect(var/obj/effect/projectile/effect) return + +/obj/item/projectile/proc/get_projectile_damage(mob/living/target) + return damage + +// Makes a brief effect sprite appear when the projectile hits something solid. +/obj/item/projectile/proc/impact_visuals(atom/A, hit_x, hit_y) + // Hitscan things have their own impact sprite. + if(!impact_effect_type || hitscan) + return + if(isnull(hit_x) && isnull(hit_y)) + if(trajectory) + // Effect goes where the projectile 'stopped'. + hit_x = A.pixel_x + trajectory.return_px() + hit_y = A.pixel_y + trajectory.return_py() + else if(A == original) + // Otherwise it goes where the person who fired clicked. + hit_x = A.pixel_x + p_x - 16 + hit_y = A.pixel_y + p_y - 16 + else + // Otherwise it'll be random. + hit_x = A.pixel_x + rand(-8, 8) + hit_y = A.pixel_y + rand(-8, 8) + new impact_effect_type(get_turf(A), src, hit_x, hit_y) + +/obj/item/projectile/proc/get_impact_volume_by_damage() + if(damage || agony) + var/value_to_use = damage > agony ? damage : agony + // Multiply projectile damage by 1.2, then CLAMP the value between 30 and 100. + // This was 0.67 but in practice it made all projectiles that did 45 or less damage play at 30, + // which is hard to hear over the gunshots, and is rather rare for a projectile to do that much. + return clamp((value_to_use) * 1.2, 30, 100) + return 50 //if the projectile doesn't do damage or agony, play its hitsound at 50% volume. + +/obj/item/projectile/proc/impact_sounds(atom/A) + + var/play_volume = clamp(get_impact_volume_by_damage() + 20, 0, 100) + if(play_volume <= 0) + return + if(silenced) + play_volume = min(play_volume, 5) + + var/play_sound + if(ismob(A)) // Mob sounds are handled in attack_mob(). + play_sound = hitsound + else + play_sound = hitsound_non_mob + if(!play_sound) + return + playsound(A, play_sound, play_volume, 1, -1) + +/obj/item/projectile/proc/get_miss_sounds() + return + +/obj/item/projectile/proc/get_ricochet_sounds() + return + +/obj/item/projectile/proc/get_impact_sounds() + return diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 8df181e9195e..1c8b74eeb566 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -3,7 +3,6 @@ icon_state = "laser" temperature = T0C + 300 fire_sound='sound/weapons/Laser.ogg' - impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_LASER_MEAT, BULLET_IMPACT_METAL = SOUNDS_LASER_METAL) pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GLASS | PASS_FLAG_GRILLE damage = 40 atom_damage_type = BURN @@ -14,11 +13,26 @@ invisibility = INVISIBILITY_ABSTRACT //beam projectiles are invisible as they are rendered by the effect engine penetration_modifier = 0.3 distance_falloff = 2.5 + hitsound = 'sound/weapons/sear.ogg' + hitsound_non_mob = 'sound/weapons/searwall.ogg' muzzle_type = /obj/effect/projectile/muzzle/laser tracer_type = /obj/effect/projectile/tracer/laser impact_type = /obj/effect/projectile/impact/laser +/obj/item/projectile/beam/get_impact_sounds() + var/static/list/impact_sounds = list( + (BULLET_IMPACT_MEAT) = SOUNDS_LASER_MEAT, + (BULLET_IMPACT_METAL) = SOUNDS_LASER_METAL + ) + return impact_sounds + +/obj/item/projectile/beam/blue + damage = 30 + muzzle_type = /obj/effect/projectile/muzzle/laser/blue + tracer_type = /obj/effect/projectile/tracer/laser/blue + impact_type = /obj/effect/projectile/impact/laser/blue + /obj/item/projectile/beam/megabot damage = 45 distance_falloff = 0.5 diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index b80940577088..e94f5926a344 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -9,14 +9,31 @@ embed = 1 space_knockback = 1 penetration_modifier = 1.0 + impact_effect_type = /obj/effect/temp_visual/impact_effect + muzzle_type = /obj/effect/projectile/muzzle/bullet + hitsound_non_mob = "ricochet" + var/mob_passthrough_check = 0 var/caliber - muzzle_type = /obj/effect/projectile/muzzle/bullet - miss_sounds = list('sound/weapons/guns/miss1.ogg','sound/weapons/guns/miss2.ogg','sound/weapons/guns/miss3.ogg','sound/weapons/guns/miss4.ogg') - ricochet_sounds = list('sound/weapons/guns/ricochet1.ogg', 'sound/weapons/guns/ricochet2.ogg', - 'sound/weapons/guns/ricochet3.ogg', 'sound/weapons/guns/ricochet4.ogg') - impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_BULLET_MEAT, BULLET_IMPACT_METAL = SOUNDS_BULLET_METAL) +/obj/item/projectile/bullet/get_miss_sounds() + var/static/list/miss_sounds = list( + 'sound/weapons/guns/miss1.ogg', + 'sound/weapons/guns/miss2.ogg', + 'sound/weapons/guns/miss3.ogg', + 'sound/weapons/guns/miss4.ogg' + ) + +/obj/item/projectile/bullet/get_ricochet_sounds() + return global.ricochet_sound + +/obj/item/projectile/bullet/get_impact_sounds() + + var/static/list/impact_sounds = list( + (BULLET_IMPACT_MEAT) = SOUNDS_BULLET_MEAT, + (BULLET_IMPACT_METAL) = SOUNDS_BULLET_METAL + ) + return impact_sounds /obj/item/projectile/bullet/get_autopsy_descriptors() . = ..() @@ -34,7 +51,6 @@ else mob_passthrough_check = 0 . = ..() - if(. == 1 && isliving(target_mob)) var/mob/living/squish = target_mob if(!squish.isSynthetic()) diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index eb0cc1835e48..e08ffc8d1f01 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -6,6 +6,9 @@ atom_damage_type = BURN damage_flags = 0 distance_falloff = 2.5 + impact_effect_type = /obj/effect/temp_visual/impact_effect + hitsound_non_mob = 'sound/weapons/searwall.ogg' + hitsound = 'sound/weapons/zapbang.ogg' //releases a burst of light on impact or after travelling a distance /obj/item/projectile/energy/flash @@ -16,6 +19,7 @@ agony = 20 life_span = 15 //if the shell hasn't hit anything after travelling this far it just explodes. muzzle_type = /obj/effect/projectile/muzzle/bullet + hitsound_non_mob = null var/flash_range = 1 var/brightness = 7 var/light_flash_color = COLOR_WHITE @@ -106,6 +110,7 @@ damage = 30 atom_damage_type = CLONE irradiate = 40 + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/dart name = "dart" @@ -142,6 +147,7 @@ damage = 20 atom_damage_type = TOX irradiate = 20 + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/plasmastun name = "plasma pulse" diff --git a/code/modules/projectiles/projectile/fireball.dm b/code/modules/projectiles/projectile/fireball.dm new file mode 100644 index 000000000000..6221b8bf1348 --- /dev/null +++ b/code/modules/projectiles/projectile/fireball.dm @@ -0,0 +1,45 @@ +/obj/item/projectile/fireball + name = "fireball" + icon_state = "fireball" + fire_sound = 'sound/effects/bamf.ogg' + damage = 20 + atom_damage_type = BURN + damage_flags = DAM_DISPERSED // burn all over + var/fire_lifetime = 2 SECONDS + var/fire_temperature = (288 CELSIUS) / 0.9 + 1 // hot enough to ignite wood! divided by 0.9 and plus one to ensure we can light firepits + +/obj/effect/fake_fire/variable + name = "fire" + anchored = TRUE + mouse_opacity = MOUSE_OPACITY_UNCLICKABLE + firelevel = 1 + pressure = ONE_ATMOSPHERE + +/obj/effect/fake_fire/variable/Initialize(ml, new_temperature, new_lifetime) + lifetime = new_lifetime + last_temperature = new_temperature + return ..() + +// we deal our damage via fire_act, not via direct burn damage. our burn damage is specifically for mobs +/obj/item/projectile/fireball/get_structure_damage() + return 0 + +/obj/item/projectile/fireball/on_impact(var/atom/A) + . = ..() + var/obj/effect/fake_fire/fire = new /obj/effect/fake_fire/variable(get_turf(A), fire_temperature, fire_lifetime) + fire.Process() // process at least once! + qdel_self() + +/obj/item/projectile/fireball/after_move() + . = ..() + if(!loc) + return + for(var/mob/living/victim in loc) + if(!victim.simulated) + continue + victim.FireBurn(1, fire_temperature, ONE_ATMOSPHERE) + loc.fire_act(1, fire_temperature, ONE_ATMOSPHERE) + for(var/atom/burned in loc) + if(!burned.simulated || burned == src) + continue + burned.fire_act(1, fire_temperature, ONE_ATMOSPHERE) diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/force.dm index a3c2e748d0b9..e0b8ea2745f9 100644 --- a/code/modules/projectiles/projectile/force.dm +++ b/code/modules/projectiles/projectile/force.dm @@ -5,6 +5,8 @@ damage = 20 atom_damage_type = BURN damage_flags = 0 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_non_mob = 'sound/weapons/searwall.ogg' /obj/item/projectile/forcebolt/strong name = "force bolt" diff --git a/code/modules/projectiles/projectile/pellets.dm b/code/modules/projectiles/projectile/pellets.dm index c4a244c802ac..5c19804a9c2f 100644 --- a/code/modules/projectiles/projectile/pellets.dm +++ b/code/modules/projectiles/projectile/pellets.dm @@ -17,7 +17,9 @@ return max(pellets - pellet_loss, 1) /obj/item/projectile/bullet/pellet/attack_mob(var/mob/target_mob, var/distance, var/miss_modifier) - if (pellets < 0) return 1 + SHOULD_CALL_PARENT(FALSE) + if (pellets < 0) + return 1 var/total_pellets = get_pellets(distance) var/spread = max(base_spread - (spread_step*distance), 0) @@ -39,7 +41,9 @@ if (..()) hits++ def_zone = old_zone //restore the original zone the projectile was aimed at - pellets -= hits //each hit reduces the number of pellets left + if(hits > 0) + pellets -= hits //each hit reduces the number of pellets left + if (hits >= total_pellets || pellets <= 0) return 1 return 0 diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index d648f6d70c3f..d7ee296a8f14 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -6,6 +6,9 @@ atom_damage_type = BURN damage_flags = 0 nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/ion + hitsound_non_mob = 'sound/weapons/searwall.ogg' + hitsound = 'sound/weapons/ionrifle.ogg' var/heavy_effect_range = 1 var/light_effect_range = 2 @@ -51,6 +54,7 @@ atom_damage_type = BURN damage_flags = 0 nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser var/firing_temperature = 300 /obj/item/projectile/temp/on_hit(var/atom/target, var/blocked = 0)//These two could likely check temp protection on the mob @@ -88,6 +92,7 @@ damage = 0 atom_damage_type = TOX nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/floramut/on_hit(var/atom/target, var/blocked = 0) if(!isliving(target)) @@ -131,6 +136,7 @@ damage = 0 atom_damage_type = TOX nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) if(!isliving(target)) diff --git a/code/modules/projectiles/projectile/trace.dm b/code/modules/projectiles/projectile/trace.dm index a7e1f58c34fa..6abad14dbe3d 100644 --- a/code/modules/projectiles/projectile/trace.dm +++ b/code/modules/projectiles/projectile/trace.dm @@ -32,4 +32,5 @@ return ..() /obj/item/projectile/test/attack_mob() - return \ No newline at end of file + SHOULD_CALL_PARENT(FALSE) + return diff --git a/code/modules/random_map/dungeon/predefined.dm b/code/modules/random_map/dungeon/predefined.dm index c6b536cc2e54..91b22c5c42eb 100644 --- a/code/modules/random_map/dungeon/predefined.dm +++ b/code/modules/random_map/dungeon/predefined.dm @@ -5,7 +5,7 @@ room_theme_rare = list(/datum/room_theme/metal = 1, /datum/room_theme = 3, /datum/room_theme/metal/secure = 1) monsters_common = list(/mob/living/simple_animal/hostile/carp = 50, /mob/living/simple_animal/hostile/carp/pike = 1) - monsters_uncommon = list(/mob/living/simple_animal/hostile/hivebot = 10, /mob/living/simple_animal/hostile/hivebot/strong = 1) + monsters_uncommon = list(/mob/living/simple_animal/hostile/hivebot = 10, /mob/living/simple_animal/hostile/hivebot/melee/armored = 1) /datum/random_map/winding_dungeon/premade/New(var/tx, var/ty, var/tz, var/tlx, var/tly, var/do_not_apply, var/do_not_announce, var/used_area) loot_common = subtypesof(/obj/item/energy_blade/sword) + subtypesof(/obj/item/baton) + subtypesof(/obj/item/food) + subtypesof(/obj/item/chems/condiment) + subtypesof(/obj/item/chems/drinks) diff --git a/code/modules/reagents/chems/chems_compounds.dm b/code/modules/reagents/chems/chems_compounds.dm index cb305d78e23c..f080406738f4 100644 --- a/code/modules/reagents/chems/chems_compounds.dm +++ b/code/modules/reagents/chems/chems_compounds.dm @@ -263,12 +263,7 @@ if(!M.has_genetic_information()) return if(prob(removed * 0.1)) // Approx. one mutation per 10 injected/20 ingested/30 touching units - M.set_unique_enzymes(num2text(random_id(/mob, 1000000, 9999999))) - if(prob(98)) - M.add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/disability))) - else - M.add_genetic_condition(pick(decls_repository.get_decls_of_type(/decl/genetic_condition/superpower))) - M.apply_damage(10 * removed, IRRADIATE, armor_pen = 100) + M.apply_random_mutation(10 * removed) /decl/material/liquid/lactate name = "lactate" diff --git a/code/modules/salvage/salvage.dm b/code/modules/salvage/salvage.dm new file mode 100644 index 000000000000..ecf3196f8834 --- /dev/null +++ b/code/modules/salvage/salvage.dm @@ -0,0 +1,131 @@ +/obj/item/salvage + desc = "The remains of an unfortunate device." + transform_animate_time = 0 + icon = 'icons/obj/modules/module_id.dmi' + icon_state = ICON_STATE_WORLD + abstract_type = /obj/item/salvage + var/work_skill = SKILL_DEVICES + var/obj/item/salvaged_type + var/list/repairs_required = list() + var/do_rotation = TRUE + +/obj/item/salvage/Initialize(var/ml, var/path) + . = ..(ml) + if(!ispath(salvaged_type, /obj/item)) + return INITIALIZE_HINT_QDEL + // TODO: grab partial initial matter from the salvage type. + icon_rotation = rand(-45, 45) + name = "[pick("busted", "broken", "shattered", "scrapped")] [salvaged_type::name]" + w_class = salvaged_type::w_class + + var/list/all_repair_options = get_repair_options() + var/list/selected_options = list() + for(var/opt_type in all_repair_options) + var/decl/salvage_repair_option/opt = RESOLVE_TO_DECL(opt_type) + if(istype(opt) && prob(opt.selection_prob)) + selected_options += opt + if(!length(selected_options)) + selected_options += RESOLVE_TO_DECL(pick(all_repair_options)) + for(var/decl/salvage_repair_option/opt in selected_options) + repairs_required += opt.create_salvage_requirement() + + update_icon() + +/obj/item/salvage/proc/get_repair_options() + return subtypesof(/decl/salvage_repair_option/material_sheet) + +/obj/item/salvage/attackby(obj/item/used_item, mob/user) + + // Find an appropriate repair (finished or not) + var/datum/salvage_repair_requirement/opt + for(var/datum/salvage_repair_requirement/rep in repairs_required) + if(!istype(used_item, rep.repair_type)) + continue + if(istype(used_item, /obj/item/stack/material) && !istype(used_item.material, rep.repair_material)) + continue + if(!opt || (opt.repair_amount == 0 && rep.repair_amount > 0)) + opt = rep + + // Apply the repair. + if(opt) + if(opt.repair_amount <= 0) + to_chat(user, SPAN_WARNING("\The [src] does not need further repair with \the [used_item].")) + else if(user.do_skilled((5 + rand(10)) SECONDS, work_skill, src) && !QDELETED(opt) && opt.repair_amount > 0) + if(istype(used_item, /obj/item/stack)) + var/obj/item/stack/stack = used_item + var/use_amt = min(opt.repair_amount, stack.get_amount()) + stack.use(use_amt) + opt.repair_amount -= use_amt + else if(user.try_unequip(used_item)) + qdel(used_item) + opt.repair_amount-- + // TODO: transfer material from the donor item to this item. + check_repair_completion(user) + return TRUE + + . = ..() + +/obj/item/salvage/get_examine_hints(mob/user, distance, infix, suffix) + . = ..() + . += SPAN_NOTICE("It requires the following items to be fully repaired:") + for(var/datum/salvage_repair_requirement/rep in repairs_required) + if(rep.repair_amount <= 0) + continue + var/is_mat_stack = ispath(rep.repair_type, /obj/item/stack/material) && rep.repair_material + var/obj/item/repair_type = rep.repair_type + var/repair_thing = is_mat_stack ? atom_info_repository.get_name_for(repair_type, rep.repair_material) : atom_info_repository.get_name_for(repair_type) + if(ispath(rep.repair_type, /obj/item/stack)) + var/obj/item/stack/stack = rep.repair_type + repair_thing = rep.repair_amount == 1 ? stack::singular_name : stack::plural_name + if(is_mat_stack) + var/decl/material/repair_mat = GET_DECL(rep.repair_material) + repair_thing = "[repair_mat.solid_name] [repair_thing]" + else if(rep.repair_amount > 1) + repair_thing = text_make_plural(repair_thing) + . += SPAN_NOTICE("- [rep.repair_amount] [repair_thing]") + +/obj/item/salvage/proc/check_repair_completion(mob/user) + + for(var/datum/salvage_repair_requirement/rep in repairs_required) + if(rep.repair_amount > 0) + if(user) + to_chat(user, SPAN_NOTICE("You mend some of the damage to \the [src], but further repair is required.")) + return + + var/obj/item/created = new salvaged_type(get_turf(src)) + if(isitem(created)) + if(created.name_prefix) + created.name_prefix = "[created.name_prefix] [pick("salvaged", "restored", "old", "worn")]" // enormous salvaged pipe wrench + else + created.name_prefix = pick("salvaged", "restored", "old", "worn") // salvaged laser rifle + created.update_name() + + var/atom/created_loc = loc + qdel(src) + + if(user) + to_chat(user, SPAN_NOTICE("You finish repairing \the [created]!")) + + if(user && created_loc == user) + user.put_in_hands(created) + else + created.forceMove(created_loc) + +/obj/item/salvage/on_update_icon() + SHOULD_CALL_PARENT(FALSE) + if(!salvaged_type) + return + var/old_name = name + var/old_desc = desc + var/old_pixel_x = pixel_x + var/old_pixel_y = pixel_y + var/old_plane = plane + var/old_layer = layer + appearance = atom_info_repository.get_appearance_of(salvaged_type) + name = old_name + desc = old_desc + pixel_x = old_pixel_x + pixel_y = old_pixel_y + plane = old_plane + layer = old_layer + update_transform() diff --git a/code/modules/salvage/salvage_ballistic.dm b/code/modules/salvage/salvage_ballistic.dm new file mode 100644 index 000000000000..904f6d0276c9 --- /dev/null +++ b/code/modules/salvage/salvage_ballistic.dm @@ -0,0 +1,24 @@ +/obj/item/salvage/ballistic + name = "broken ballistic weapon" + icon = /obj/item/gun/projectile/automatic/assault_rifle::icon + icon_state = /obj/item/gun/projectile/automatic/assault_rifle::icon_state + abstract_type = /obj/item/salvage/ballistic + +/obj/item/salvage/ballistic/get_repair_options() + return ..() + /decl/salvage_repair_option/component + +/obj/item/salvage/ballistic/assault + salvaged_type = /obj/item/gun/projectile/automatic/assault_rifle + +/obj/item/salvage/ballistic/pistol + salvaged_type = /obj/item/gun/projectile/pistol + +/obj/item/salvage/ballistic/smg + salvaged_type = /obj/item/gun/projectile/automatic/smg + +/obj/item/salvage/ballistic/shotgun_pump + salvaged_type = /obj/item/gun/projectile/shotgun/pump + +/obj/item/salvage/ballistic/shotgun_doublebarrel + salvaged_type = /obj/item/gun/projectile/shotgun/doublebarrel + diff --git a/code/modules/salvage/salvage_energy.dm b/code/modules/salvage/salvage_energy.dm new file mode 100644 index 000000000000..3075a04bd8fa --- /dev/null +++ b/code/modules/salvage/salvage_energy.dm @@ -0,0 +1,17 @@ +/obj/item/salvage/energy + name = "broken energy weapon" + icon = /obj/item/gun/energy/laser::icon + icon_state = /obj/item/gun/energy/laser::icon_state + abstract_type = /obj/item/salvage/energy + +/obj/item/salvage/energy/get_repair_options() + return ..() + /decl/salvage_repair_option/energy + +/obj/item/salvage/energy/ionrifle + salvaged_type = /obj/item/gun/energy/ionrifle + +/obj/item/salvage/energy/laserrifle + salvaged_type = /obj/item/gun/energy/laser + +/obj/item/salvage/energy/laser_retro + salvaged_type = /obj/item/gun/energy/captain // TODO: swap to retro laser when merged diff --git a/code/modules/salvage/salvage_launcher.dm b/code/modules/salvage/salvage_launcher.dm new file mode 100644 index 000000000000..571f69b6a136 --- /dev/null +++ b/code/modules/salvage/salvage_launcher.dm @@ -0,0 +1,14 @@ +/obj/item/salvage/launcher + name = "broken grenade launcher" + icon = /obj/item/gun/launcher/grenade::icon + icon_state = /obj/item/gun/launcher/grenade::icon_state + abstract_type = /obj/item/salvage/launcher + +/obj/item/salvage/launcher/get_repair_options() + return ..() + /decl/salvage_repair_option/launcher + +/obj/item/salvage/launcher/grenade + salvaged_type = /obj/item/gun/launcher/grenade + +/obj/item/salvage/launcher/dartgun + salvaged_type = /obj/item/gun/projectile/dartgun diff --git a/code/modules/salvage/salvage_magnetic.dm b/code/modules/salvage/salvage_magnetic.dm new file mode 100644 index 000000000000..78b8bcf47baa --- /dev/null +++ b/code/modules/salvage/salvage_magnetic.dm @@ -0,0 +1,12 @@ +/obj/item/salvage/magnetic + name = "broken magnetic weapon" + icon = /obj/item/gun/magnetic/railgun/flechette::icon + icon_state = /obj/item/gun/magnetic/railgun/flechette::icon_state + abstract_type = /obj/item/salvage/magnetic + +/obj/item/salvage/magnetic/get_repair_options() + return ..() + /decl/salvage_repair_option/magnetic + +/obj/item/salvage/magnetic/flechette + salvaged_type = /obj/item/gun/magnetic/railgun/flechette + diff --git a/code/modules/salvage/salvage_repair_option.dm b/code/modules/salvage/salvage_repair_option.dm new file mode 100644 index 000000000000..f63d88afc611 --- /dev/null +++ b/code/modules/salvage/salvage_repair_option.dm @@ -0,0 +1,90 @@ +/decl/salvage_repair_option + abstract_type = /decl/salvage_repair_option + var/selection_prob = 0 + var/list/selection_types + var/list/selection_materials + var/selection_min_amount = 1 + var/selection_max_amount = 3 + +/decl/salvage_repair_option/validate() + . = ..() + if(!islist(selection_types) || !length(selection_types)) + . += "null, empty or malformed selection_types list" + else + for(var/selection_type in selection_types) + if(!ispath(selection_type)) + . += "non-path selection type: '[selection_type]'" + else if(ispath(selection_type, /obj/item/stack/material) && (!islist(selection_materials) || !length(selection_materials))) + . += "material stack '[selection_type]' in selection_types, but selection_materials is empty/malformed" + for(var/selection_material in selection_materials) + if(!ispath(selection_material, /decl/material)) + . += "non-material path '[selection_material]' in selection_materials" + +/decl/salvage_repair_option/proc/create_salvage_requirement() + var/use_type = pick(selection_types) + var/use_mat = ispath(use_type, /obj/item/stack/material) ? pick(selection_materials) : null + return new /datum/salvage_repair_requirement(use_type, use_mat, rand(selection_min_amount, selection_max_amount)) + +/decl/salvage_repair_option/component + selection_prob = 30 + selection_types = list( + /obj/item/stock_parts/manipulator + ) + +/decl/salvage_repair_option/material_sheet + selection_prob = 40 + abstract_type = /decl/salvage_repair_option/material_sheet + +/decl/salvage_repair_option/material_sheet/plastic + selection_types = list( + /obj/item/stack/material/panel + ) + selection_materials = list( + /decl/material/solid/organic/plastic + ) + +/decl/salvage_repair_option/material_sheet/glass + selection_types = list( + /obj/item/stack/material/pane + ) + selection_materials = list( + /decl/material/solid/glass + ) + +/decl/salvage_repair_option/material_sheet/plasteel + selection_types = list( + /obj/item/stack/material/sheet/reinforced + ) + selection_materials = list( + /decl/material/solid/metal/plasteel + ) + +/decl/salvage_repair_option/launcher + selection_prob = 50 + selection_materials = list( + /decl/material/solid/metal/steel + ) + selection_types = list( + /obj/item/stack/tape_roll, + /obj/item/stack/material/rods, + /obj/item/handcuffs/cable + ) + selection_max_amount = 1 + +/decl/salvage_repair_option/energy + selection_prob = 25 + selection_types = list( + /obj/item/stack/cable_coil, + /obj/item/stock_parts/scanning_module, + /obj/item/stock_parts/capacitor + ) + selection_max_amount = 1 + +/decl/salvage_repair_option/magnetic + selection_prob = 70 + selection_types = list( + /obj/item/stock_parts/smes_coil, + /obj/item/assembly/prox_sensor, + /obj/item/stock_parts/circuitboard/apc + ) + selection_max_amount = 1 diff --git a/code/modules/salvage/salvage_repair_requirement.dm b/code/modules/salvage/salvage_repair_requirement.dm new file mode 100644 index 000000000000..fdf524bb61e4 --- /dev/null +++ b/code/modules/salvage/salvage_repair_requirement.dm @@ -0,0 +1,9 @@ +/datum/salvage_repair_requirement + var/repair_type + var/repair_material + var/repair_amount + +/datum/salvage_repair_requirement/New(_type, _mat, _amt) + repair_amount = _amt + repair_material = _mat + repair_type = _type diff --git a/code/modules/salvage/structure.dm b/code/modules/salvage/structure.dm new file mode 100644 index 000000000000..1d71f206157a --- /dev/null +++ b/code/modules/salvage/structure.dm @@ -0,0 +1,23 @@ +/obj/structure/salvage + icon = 'icons/obj/structures/salvage.dmi' + abstract_type = /obj/structure/salvage + tool_interaction_flags = TOOL_INTERACTION_DECONSTRUCT + material = /decl/material/solid/metal/steel + var/frame_type = /obj/machinery/constructable_frame/machine_frame + var/work_skill = SKILL_CONSTRUCTION + +/obj/structure/salvage/proc/get_salvageable_components() + return + +/obj/structure/salvage/Initialize(ml, _mat, _reinf_mat) + . = ..() + name_prefix = pick("broken", "ruined", "destroyed", "slagged", "damaged") + +/obj/structure/salvage/create_dismantled_products(turf/T) + . = ..() + if(frame_type) + new frame_type(T) + var/list/salvageable_components = get_salvageable_components() + for(var/comp in salvageable_components) + if(!salvageable_components[comp] || prob(salvageable_components[comp])) + new comp(T) diff --git a/code/modules/salvage/structure_computer.dm b/code/modules/salvage/structure_computer.dm new file mode 100644 index 000000000000..12b9f700eee3 --- /dev/null +++ b/code/modules/salvage/structure_computer.dm @@ -0,0 +1,116 @@ +/obj/structure/salvage/computer + name = "computer" + desc = "A defunct computer. There might still be useful components inside." + icon_state = "computer0" + +/obj/structure/salvage/computer/Initialize() + . = ..() + icon_state = "computer[rand(0,7)]" + +/obj/structure/salvage/computer/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 90, + /obj/item/stack/material/pane/mapped/glass/five = 90, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/network_card/advanced = 20, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/capacitor/adv = 30, + ) + return salvageable_parts + +/obj/structure/salvage/server + name = "server" + desc = "A damaged, broken server. There might still be useful components inside." + icon_state = "server0" + +/obj/structure/salvage/server/Initialize(ml, _mat, _reinf_mat) + . = ..() + +/obj/structure/salvage/server/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 90, + /obj/item/stack/material/pane/mapped/glass/five = 90, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/subspace/amplifier = 40, + /obj/item/stock_parts/subspace/amplifier = 40, + /obj/item/stock_parts/subspace/analyzer = 40, + /obj/item/stock_parts/subspace/analyzer = 40, + /obj/item/stock_parts/subspace/ansible = 40, + /obj/item/stock_parts/subspace/ansible = 40, + /obj/item/stock_parts/subspace/transmitter = 40, + /obj/item/stock_parts/subspace/transmitter = 40, + /obj/item/stock_parts/subspace/crystal = 30, + /obj/item/stock_parts/subspace/crystal = 30, + /obj/item/stock_parts/computer/network_card/advanced = 20 + ) + return salvageable_parts + +/obj/structure/salvage/computer_old + name = "computer" + desc = "A defunct computer. There might still be useful components inside." + icon_state = "os-computer" + +/obj/structure/salvage/computer_old/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 90, + /obj/item/stack/material/pane/mapped/glass/five = 90, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/computer/processor_unit/photonic = 40, + /obj/item/stock_parts/computer/processor_unit/photonic = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/network_card/advanced = 40 + ) + return salvageable_parts + +/obj/structure/salvage/data + name = "data storage" + desc = "An old, battered, broken data storage rack. There might still be useful components inside." + icon_state = "data0" + +/obj/structure/salvage/data/Initialize() + . = ..() + icon_state = "data[rand(0,1)]" + +/obj/structure/salvage/data/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 90, + /obj/item/stack/material/pane/mapped/glass/five = 90, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive = 50, + /obj/item/stock_parts/computer/hard_drive/advanced = 30, + /obj/item/stock_parts/computer/hard_drive/advanced = 30, + /obj/item/stock_parts/computer/network_card/advanced = 20 + ) + return salvageable_parts diff --git a/code/modules/salvage/structure_console.dm b/code/modules/salvage/structure_console.dm new file mode 100644 index 000000000000..7f31e5be6ad6 --- /dev/null +++ b/code/modules/salvage/structure_console.dm @@ -0,0 +1,35 @@ +/obj/structure/salvage/console + name = "console" + desc = "A beat-up old console. Might still have some useful components inside." + icon_state = "os_console" + +/obj/structure/salvage/console/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stack/cable_coil/five = 90, + /obj/item/stock_parts/console_screen = 80, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/computer/processor_unit/small = 40, + /obj/item/stock_parts/computer/processor_unit/photonic = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/network_card/advanced = 40 + ) + return salvageable_parts + +/obj/structure/salvage/console/broken + icon_state = "os_console_broken" + +/obj/structure/salvage/console/broken/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stack/cable_coil/five = 90, + /obj/item/stock_parts/console_screen = 80, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/capacitor = 60, + /obj/item/stock_parts/computer/processor_unit = 40, + /obj/item/stock_parts/computer/processor_unit/photonic = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/network_card/advanced = 40 + ) + return salvageable_parts diff --git a/code/modules/salvage/structure_machine.dm b/code/modules/salvage/structure_machine.dm new file mode 100644 index 000000000000..62fa97c6bbfc --- /dev/null +++ b/code/modules/salvage/structure_machine.dm @@ -0,0 +1,92 @@ + +/obj/structure/salvage/fabricator + name = "fabricator" + desc = "A busted, defunct fabricator. There might still be useful components or materials inside." + icon_state = "autolathe" + +/obj/structure/salvage/fabricator/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 80, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/capacitor = 40, + /obj/item/stock_parts/scanning_module = 40, + /obj/item/stock_parts/manipulator = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/capacitor/adv = 20, + /obj/item/stock_parts/micro_laser/high = 20, + /obj/item/stock_parts/micro_laser/high = 20, + /obj/item/stock_parts/matter_bin/adv = 20, + /obj/item/stock_parts/matter_bin/adv = 20, + /obj/item/stack/material/sheet/mapped/steel/twenty = 40, + /obj/item/stack/material/pane/mapped/glass/twenty = 40, + /obj/item/stack/material/panel/mapped/plastic/twenty = 40, + /obj/item/stack/material/sheet/reinforced/mapped/plasteel/ten = 40, + /obj/item/stack/material/ingot/mapped/silver/ten = 20, + /obj/item/stack/material/ingot/mapped/gold/ten = 20 + ) + return salvageable_parts + +/obj/structure/salvage/machine + name = "machine" + desc = "A badly-damaged machine of some kind. There might still be some usable components inside." + icon_state = "machine1" + +/obj/structure/salvage/machine/Initialize() + . = ..() + icon_state = "machine[rand(0,6)]" + +/obj/structure/salvage/machine/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 80, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/capacitor = 40, + /obj/item/stock_parts/capacitor = 40, + /obj/item/stock_parts/scanning_module = 40, + /obj/item/stock_parts/scanning_module = 40, + /obj/item/stock_parts/manipulator = 40, + /obj/item/stock_parts/manipulator = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/capacitor/adv = 20, + /obj/item/stock_parts/scanning_module/adv = 20, + /obj/item/stock_parts/manipulator/nano = 20, + /obj/item/stock_parts/micro_laser/high = 20, + /obj/item/stock_parts/matter_bin/adv = 20 + ) + return salvageable_parts + +/obj/structure/salvage/machine_old + name = "machine" + desc = "A badly-damaged machine of some kind. There might still be some usable components inside." + icon_state = "os-machine" + +/obj/structure/salvage/machine_old/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 80, + /obj/item/stock_parts/capacitor = 40, + /obj/item/stock_parts/capacitor = 40, + /obj/item/stock_parts/scanning_module = 40, + /obj/item/stock_parts/scanning_module = 40, + /obj/item/stock_parts/manipulator = 40, + /obj/item/stock_parts/manipulator = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/micro_laser = 40, + /obj/item/stock_parts/matter_bin = 40, + /obj/item/stock_parts/matter_bin = 40 + ) + return salvageable_parts diff --git a/code/modules/salvage/structure_misc.dm b/code/modules/salvage/structure_misc.dm new file mode 100644 index 000000000000..e1515b69c93b --- /dev/null +++ b/code/modules/salvage/structure_misc.dm @@ -0,0 +1,27 @@ +/obj/structure/salvage/implant_container + name = "old container" + icon_state = "implant_container0" + +/obj/structure/salvage/implant_container/Initialize() + . = ..() + icon_state = "implant_container[rand(0,1)]" + +/obj/structure/salvage/implant_container/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil/five = 80, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/implant/death_alarm = 15, + /obj/item/implant/explosive = 10, + /obj/item/implant/freedom = 5, + /obj/item/implant/tracking = 10, + /obj/item/implant/chem = 10, + /obj/item/implantcase = 30, + /obj/item/implanter = 30, + /obj/item/stack/material/sheet/mapped/steel/ten = 30, + /obj/item/stack/material/pane/mapped/glass/ten = 30, + /obj/item/stack/material/ingot/mapped/silver/ten = 30 + ) + return salvageable_parts diff --git a/code/modules/salvage/structure_terminal.dm b/code/modules/salvage/structure_terminal.dm new file mode 100644 index 000000000000..6e2ef10be5c5 --- /dev/null +++ b/code/modules/salvage/structure_terminal.dm @@ -0,0 +1,29 @@ +/obj/structure/salvage/personal + name = "personal terminal" + desc = "An unusable personal terminal. There might still be useful components inside." + icon_state = "personal0" + +/obj/structure/salvage/personal/Initialize(ml, _mat, _reinf_mat) + . = ..() + icon_state = "personal[rand(0,12)]" + +/obj/structure/salvage/personal/get_salvageable_components() + var/static/list/salvageable_parts = list( + /obj/item/stock_parts/console_screen = 90, + /obj/item/stack/cable_coil/five = 90, + /obj/item/stack/material/pane/mapped/glass/five = 70, + /obj/item/debris/salvage/circuit = 60, + /obj/item/debris/salvage/metal = 60, + /obj/item/debris/salvage/metal/plasteel = 15, + /obj/item/stock_parts/computer/network_card = 60, + /obj/item/stock_parts/computer/network_card = 40, + /obj/item/stock_parts/computer/network_card/advanced = 40, + /obj/item/stock_parts/computer/card_slot = 40, + /obj/item/stock_parts/computer/processor_unit = 60, + /obj/item/stock_parts/computer/processor_unit/small = 50, + /obj/item/stock_parts/computer/processor_unit/photonic = 40, + /obj/item/stock_parts/computer/processor_unit/photonic/small = 30, + /obj/item/stock_parts/computer/hard_drive = 60, + /obj/item/stock_parts/computer/hard_drive/advanced = 40 + ) + return salvageable_parts diff --git a/code/modules/scanners/mining.dm b/code/modules/scanners/mining.dm index 2b9095692b27..9da785d77ced 100644 --- a/code/modules/scanners/mining.dm +++ b/code/modules/scanners/mining.dm @@ -15,13 +15,12 @@ origin_tech = @'{"magnets":1,"engineering":1}' use_delay = 50 printout_color = "#fff7f0" - var/survey_data = 0 - scan_sound = 'sound/effects/ping.ogg' + var/survey_data = 0 /obj/item/scanner/mining/get_examine_strings(mob/user, distance, infix, suffix) . = ..() - . += "A tiny indicator on \the [src] shows it holds [survey_data] good explorer point\s." + . += "A tiny indicator on \the [src] shows it holds [survey_data] survey point\s." /obj/item/scanner/mining/is_valid_scan_target(turf/T) return istype(T) @@ -39,13 +38,13 @@ if(scan_results[2]) survey_data += scan_results[2] playsound(loc, 'sound/machines/ping.ogg', 40, 1) - to_chat(user, SPAN_NOTICE("New survey data stored - earned [scan_results[2]] GEP.")) + to_chat(user, SPAN_NOTICE("New survey data stored - earned [scan_results[2]] survey points.")) /obj/item/scanner/mining/proc/put_disk_in_hand(var/mob/M) if(!survey_data) to_chat(M, SPAN_WARNING("There is no survey data stored on \the [src].")) return FALSE - visible_message(SPAN_NOTICE("\The [src] spits out a disk containing [survey_data] GEP.")) + visible_message(SPAN_NOTICE("\The [src] spits out a disk containing [survey_data] survey points.")) var/obj/item/disk/survey/D = new(get_turf(src)) D.data = survey_data survey_data = 0 diff --git a/code/modules/scanners/xenobio.dm b/code/modules/scanners/xenobio.dm index 3fc507c50dec..b7afc80b7a63 100644 --- a/code/modules/scanners/xenobio.dm +++ b/code/modules/scanners/xenobio.dm @@ -20,9 +20,6 @@ /obj/item/scanner/xenobio/is_valid_scan_target(atom/O) if(is_type_in_list(O, valid_targets)) return TRUE - if(istype(O, /obj/structure/stasis_cage)) - var/obj/structure/stasis_cage/cagie = O - return !!cagie.contained return FALSE /obj/item/scanner/xenobio/scan(mob/O, mob/user) diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index 157801fcf74e..8224ac158376 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -404,23 +404,14 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 if(taste_sensitivity < 0) . += "taste_sensitivity ([taste_sensitivity]) was negative" -/decl/species/proc/equip_survival_gear(mob/living/wearer, extended) - - var/box_type - var/decl/survival_box_option/chosen_survival_box = wearer?.client?.prefs.survival_box_choice - if(chosen_survival_box?.box_type) - box_type = chosen_survival_box?.box_type - else if(extended) - box_type = /obj/item/box/engineer +/decl/species/proc/equip_survival_gear(mob/living/wearer, box_type = /obj/item/box/survival) + if(!box_type) + return + var/obj/item/backpack/backpack = wearer.get_equipped_item(slot_back_str) + if(istype(backpack)) + wearer.equip_to_slot_or_del(new box_type(backpack), slot_in_backpack_str) else - box_type = /obj/item/box/survival - - if(box_type) - var/obj/item/backpack/backpack = wearer.get_equipped_item(slot_back_str) - if(istype(backpack)) - wearer.equip_to_slot_or_del(new box_type(backpack), slot_in_backpack_str) - else - wearer.put_in_hands_or_del(new box_type(wearer)) + wearer.put_in_hands_or_del(new box_type(wearer)) /decl/species/proc/get_manual_dexterity(var/mob/living/human/H) . = manual_dexterity diff --git a/code/modules/vehicles/bike.dm b/code/modules/vehicles/bike.dm index 7522827dc86f..49e6274920d1 100644 --- a/code/modules/vehicles/bike.dm +++ b/code/modules/vehicles/bike.dm @@ -32,7 +32,7 @@ update_icon() /obj/vehicle/bike/user_buckle_mob(mob/living/M, mob/user) - return load(M) + return load_onto_vehicle(M) /obj/vehicle/bike/verb/toggle() set name = "Toggle Engine" @@ -89,11 +89,12 @@ qdel(trail) trail = null -/obj/vehicle/bike/load(var/atom/movable/loading) +/obj/vehicle/bike/load_onto_vehicle(var/atom/movable/loading) + if(!isliving(loading)) + return FALSE var/mob/living/M = loading - if(!istype(M)) return 0 if(M.buckled || M.anchored || M.restrained() || !Adjacent(M) || !M.Adjacent(src)) - return 0 + return FALSE return ..(M) /obj/vehicle/bike/emp_act(var/severity) @@ -124,14 +125,14 @@ /obj/vehicle/bike/receive_mouse_drop(atom/dropping, mob/user, params) . = ..() if(!. && istype(dropping, /atom/movable)) - if(!load(dropping)) + if(!load_onto_vehicle(dropping)) to_chat(user, SPAN_WARNING("You were unable to load \the [dropping] onto \the [src].")) return TRUE /obj/vehicle/bike/attack_hand(var/mob/user) if(user != load) return ..() - unload(load) + unload_from_vehicle(load) to_chat(user, "You unbuckle yourself from \the [src].") return TRUE @@ -139,7 +140,7 @@ if(user != load || !on) return if(user.incapacitated()) - unload(user) + unload_from_vehicle(user) visible_message("\The [user] falls off \the [src]!") return return Move(get_step(src, direction)) diff --git a/code/modules/vehicles/cargo_train.dm b/code/modules/vehicles/cargo_train.dm index f828d2f4e1c5..4fcb9ee46ce8 100644 --- a/code/modules/vehicles/cargo_train.dm +++ b/code/modules/vehicles/cargo_train.dm @@ -1,18 +1,21 @@ -/obj/vehicle/train/cargo/engine +/obj/vehicle/train/engine name = "cargo train tug" - desc = "A rideable electric car designed for pulling cargo trolleys." + desc = "A ridable electric car designed for pulling cargo trolleys." icon = 'icons/obj/vehicles.dmi' icon_state = "cargo_engine" on = 0 powered = 1 locked = 0 + load_item_visible = 1 load_offset_x = 0 buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 7) charge_use = 1 KILOWATTS active_engines = 1 + var/car_limit = 3 //how many cars an engine can pull before performance degrades - var/obj/item/key/cargo_train/key + var/obj/item/key/key + var/key_type = /obj/item/key/cargo_train /obj/item/key/cargo_train desc = "A small key on a yellow fob reading \"Choo Choo!\"." @@ -24,34 +27,24 @@ icon_state = "train_keys" w_class = ITEM_SIZE_TINY -/obj/vehicle/train/cargo/trolley - name = "cargo train trolley" - icon = 'icons/obj/vehicles.dmi' - icon_state = "cargo_trailer" - anchored = FALSE - passenger_allowed = 0 - locked = 0 - buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 8) - - load_item_visible = 1 - load_offset_x = 0 - load_offset_y = 4 - - //------------------------------------------- // Standard procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/Initialize() +/obj/vehicle/train/engine/Initialize() . = ..() cell = new /obj/item/cell/high(src) - key = new(src) - var/image/I = new(icon = icon, icon_state = "cargo_engine_overlay") + key = new key_type(src) + update_icon() + turn_off() //so engine verbs are correctly set + +/obj/vehicle/train/engine/on_update_icon() + . = ..() + var/image/I = image(icon, "cargo_engine_overlay") I.plane = plane I.layer = layer - overlays += I - turn_off() //so engine verbs are correctly set + set_overlays(I) -/obj/vehicle/train/cargo/engine/Move(var/turf/destination) +/obj/vehicle/train/engine/Move(var/turf/destination) if(on && cell.charge < (charge_use * CELLRATE)) turn_off() update_stats() @@ -67,46 +60,23 @@ return ..() -/obj/vehicle/train/cargo/trolley/attackby(obj/item/used_item, mob/user) - if(open && IS_WIRECUTTER(used_item)) - passenger_allowed = !passenger_allowed - user.visible_message("[user] [passenger_allowed ? "cuts" : "mends"] a cable in [src].","You [passenger_allowed ? "cut" : "mend"] the load limiter cable.") - return TRUE - return ..() - -/obj/vehicle/train/cargo/engine/attackby(obj/item/used_item, mob/user) - if(istype(used_item, /obj/item/key/cargo_train)) +/obj/vehicle/train/engine/attackby(obj/item/used_item, mob/user) + if(istype(used_item, key_type)) if(!key && user.try_unequip(used_item, src)) key = used_item - verbs += /obj/vehicle/train/cargo/engine/verb/remove_key + verbs |= /obj/vehicle/train/engine/verb/remove_key return TRUE return ..() -//cargo trains are open topped, so there is a chance the projectile will hit the mob ridding the train instead -/obj/vehicle/train/cargo/bullet_act(var/obj/item/projectile/Proj) - if(buckled_mob && prob(70)) - buckled_mob.bullet_act(Proj) - return - ..() - -/obj/vehicle/train/cargo/on_update_icon() - if(open) - icon_state = initial(icon_state) + "_open" - else - icon_state = initial(icon_state) - -/obj/vehicle/train/cargo/trolley/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) - return - -/obj/vehicle/train/cargo/engine/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) +/obj/vehicle/train/engine/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) ..() update_stats() -/obj/vehicle/train/cargo/engine/remove_cell(var/mob/living/human/H) +/obj/vehicle/train/engine/remove_cell(var/mob/living/human/H) ..() update_stats() -/obj/vehicle/train/cargo/engine/Bump(atom/Obstacle) +/obj/vehicle/train/engine/Bump(atom/Obstacle) var/obj/machinery/door/D = Obstacle var/mob/living/human/H = load if(istype(D) && istype(H)) @@ -114,52 +84,47 @@ ..() -/obj/vehicle/train/cargo/trolley/Bump(atom/Obstacle) - if(!lead) - return //so people can't knock others over by pushing a trolley around - ..() - //------------------------------------------- // Train procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/turn_on() +/obj/vehicle/train/engine/turn_on() if(!key) return + if(!cell) + return + ..() + update_stats() + if(on) + verbs |= /obj/vehicle/train/engine/verb/stop_engine + verbs -= /obj/vehicle/train/engine/verb/start_engine else - ..() - update_stats() - - verbs -= /obj/vehicle/train/cargo/engine/verb/stop_engine - verbs -= /obj/vehicle/train/cargo/engine/verb/start_engine - - if(on) - verbs += /obj/vehicle/train/cargo/engine/verb/stop_engine - else - verbs += /obj/vehicle/train/cargo/engine/verb/start_engine + verbs |= /obj/vehicle/train/engine/verb/start_engine + verbs -= /obj/vehicle/train/engine/verb/stop_engine -/obj/vehicle/train/cargo/engine/turn_off() +/obj/vehicle/train/engine/turn_off() ..() + if(!on) + verbs |= /obj/vehicle/train/engine/verb/start_engine + verbs -= /obj/vehicle/train/engine/verb/stop_engine + else + verbs |= /obj/vehicle/train/engine/verb/stop_engine + verbs -= /obj/vehicle/train/engine/verb/start_engine - verbs -= /obj/vehicle/train/cargo/engine/verb/stop_engine - verbs -= /obj/vehicle/train/cargo/engine/verb/start_engine - if(!on) - verbs += /obj/vehicle/train/cargo/engine/verb/start_engine +/obj/vehicle/train/engine/on_update_icon() + if(open) + icon_state = initial(icon_state) + "_open" else - verbs += /obj/vehicle/train/cargo/engine/verb/stop_engine + icon_state = initial(icon_state) -/obj/vehicle/train/cargo/crossed_mob(var/mob/living/victim) +/obj/vehicle/train/engine/crossed_mob(var/mob/living/victim) victim.apply_effects(5, 5) for(var/i = 1 to rand(1,5)) var/obj/item/organ/external/E = pick(victim.get_external_organs()) if(E) victim.apply_damage(rand(5,10), BRUTE, E.organ_tag) -/obj/vehicle/train/cargo/trolley/crossed_mob(var/mob/living/victim) - ..() - attack_log += text("\[[time_stamp()]\] ran over [victim.name] ([victim.ckey])") - -/obj/vehicle/train/cargo/engine/crossed_mob(var/mob/living/victim) +/obj/vehicle/train/engine/crossed_mob(var/mob/living/victim) ..() if(is_train_head() && ishuman(load)) var/mob/living/human/D = load @@ -170,161 +135,96 @@ else attack_log += text("\[[time_stamp()]\] ran over [victim.name] ([victim.ckey])") - //------------------------------------------- // Interaction procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/relaymove(mob/user, direction) +/obj/vehicle/train/engine/relaymove(mob/user, direction) if(user != load || user.incapacitated()) - return 0 - + return FALSE if(is_train_head()) if(direction == global.reverse_dir[dir] && tow) - return 0 + return FALSE if(Move(get_step(src, direction))) - return 1 - return 0 - else - return ..() + return TRUE + return FALSE + return ..() -/obj/vehicle/train/cargo/engine/get_examine_strings(mob/user, distance, infix, suffix) +/obj/vehicle/train/engine/get_examine_strings(mob/user, distance, infix, suffix) . = ..() if(distance <= 1) . += "The power light is [on ? "on" : "off"].\nThere are[key ? "" : " no"] keys in the ignition." . += "The charge meter reads [cell? round(cell.percent(), 0.01) : 0]%" -/obj/vehicle/train/cargo/engine/verb/start_engine() +/obj/vehicle/train/engine/verb/start_engine() set name = "Start engine" - set category = "Object" + set category = "Vehicle" set src in view(0) if(!ishuman(usr)) return if(on) - to_chat(usr, "The engine is already running.") + to_chat(usr, SPAN_WARNING("The engine is already running.")) return turn_on() if (on) - to_chat(usr, "You start [src]'s engine.") + to_chat(usr, SPAN_NOTICE("You start \the [src]'s engine.")) else - if(cell.charge < charge_use) - to_chat(usr, "[src] is out of power.") + if(!cell) + to_chat(usr, SPAN_NOTICE("\The [src] doesn't appear to have a power cell!")) + else if(cell.charge < charge_use) + to_chat(usr, SPAN_NOTICE("\The [src] is out of power.")) else - to_chat(usr, "[src]'s engine won't start.") + to_chat(usr, SPAN_NOTICE("\The [src]'s engine won't start.")) -/obj/vehicle/train/cargo/engine/verb/stop_engine() +/obj/vehicle/train/engine/verb/stop_engine() set name = "Stop engine" - set category = "Object" + set category = "Vehicle" set src in view(0) if(!ishuman(usr)) return if(!on) - to_chat(usr, "The engine is already stopped.") + to_chat(usr, SPAN_WARNING("The engine is already stopped.")) return turn_off() if (!on) - to_chat(usr, "You stop [src]'s engine.") + to_chat(usr, SPAN_NOTICE("You stop [src]'s engine.")) -/obj/vehicle/train/cargo/engine/verb/remove_key() +/obj/vehicle/train/engine/verb/remove_key() set name = "Remove key" - set category = "Object" + set category = "Vehicle" set src in view(0) - if(!ishuman(usr)) - return - - if(!key || (load && load != usr)) + if(!isliving(usr) || !key || (load && load != usr)) return if(on) turn_off() + var/mob/living/user = usr + key.dropInto(get_turf(user)) usr.put_in_hands(key) key = null + verbs -= /obj/vehicle/train/engine/verb/remove_key - verbs -= /obj/vehicle/train/cargo/engine/verb/remove_key - -//------------------------------------------- -// Loading/unloading procs -//------------------------------------------- -/obj/vehicle/train/cargo/trolley/load(var/atom/movable/loading) - if(ismob(loading) && !passenger_allowed) - return 0 - if(!istype(loading,/obj/machinery) && !istype(loading,/obj/structure/closet) && !istype(loading,/obj/structure/largecrate) && !istype(loading,/obj/structure/reagent_dispensers) && !istype(loading,/obj/structure/ore_box) && !ishuman(loading)) - return 0 - - //if there are any items you don't want to be able to interact with, add them to this check - // ~no more shielded, emitter armed death trains - if(istype(loading, /obj/machinery)) - load_object(loading) - else - ..() - - if(load) - return 1 - -/obj/vehicle/train/cargo/engine/load(var/atom/movable/loading) - if(!ishuman(loading)) - return 0 - - return ..() - -//Load the object "inside" the trolley and add an overlay of it. -//This prevents the object from being interacted with until it has -// been unloaded. A dummy object is loaded instead so the loading -// code knows to handle it correctly. -/obj/vehicle/train/cargo/trolley/proc/load_object(var/atom/movable/loading) - if(!isturf(loading.loc)) //To prevent loading things from someone's inventory, which wouldn't get handled properly. - return 0 - if(load || loading.anchored) - return 0 - - var/datum/vehicle_dummy_load/dummy_load = new() - load = dummy_load - - if(!load) - return - dummy_load.actual_load = loading - loading.forceMove(src) - - if(load_item_visible) - loading.pixel_x += load_offset_x - loading.pixel_y += load_offset_y - loading.plane = plane - loading.layer = VEHICLE_LOAD_LAYER - - overlays += loading - - //we can set these back now since we have already cloned the icon into the overlay - loading.pixel_x = initial(loading.pixel_x) - loading.pixel_y = initial(loading.pixel_y) - loading.reset_plane_and_layer() - -/obj/vehicle/train/cargo/trolley/unload(var/mob/user, var/direction) - if(istype(load, /datum/vehicle_dummy_load)) - var/datum/vehicle_dummy_load/dummy_load = load - load = dummy_load.actual_load - dummy_load.actual_load = null - qdel(dummy_load) - overlays.Cut() - ..() +/obj/vehicle/train/engine/load_onto_vehicle(var/atom/movable/loading, var/mob/user) + return istype(loading, /mob/living/human) && ..() //------------------------------------------- // Latching/unlatching procs //------------------------------------------- -/obj/vehicle/train/cargo/engine/latch(obj/vehicle/train/T, mob/user) +/obj/vehicle/train/engine/latch(obj/vehicle/train/T, mob/user) if(!istype(T) || !Adjacent(T)) return 0 //if we are attaching a trolley to an engine we don't care what direction // it is in and it should probably be attached with the engine in the lead - if(istype(T, /obj/vehicle/train/cargo/trolley)) + if(istype(T, /obj/vehicle/train/trolley)) T.attach_to(src, user) else var/T_dir = get_dir(src, T) //figure out where T is wrt src @@ -345,24 +245,40 @@ // more engines increases this limit by car_limit per // engine. //------------------------------------------------------- -/obj/vehicle/train/cargo/engine/update_car(var/train_length, var/active_engines) - src.train_length = train_length - src.active_engines = active_engines +/obj/vehicle/train/engine/update_vehicle_move_delay(atom/prev_loc) + ..() + if(is_train_head() && on) + move_delay = max(move_delay, (-car_limit * active_engines) + train_length - active_engines) //limits base overweight so you can't overspeed trains + move_delay *= (1 / max(1, active_engines)) * 2 //overweight penalty (scaled by the number of engines) - //Update move delay - if(!is_train_head() || !on) - move_delay = initial(move_delay) //so that engines that have been turned off don't lag behind - else - move_delay = max(0, (-car_limit * active_engines) + train_length - active_engines) // limits base overweight so you cant overspeed trains - move_delay *= (1 / max(1, active_engines)) * 2 // overweight penalty (scaled by the number of engines) - move_delay += get_config_value(/decl/config/num/movement_run) // base reference speed - move_delay *= 1.1 // makes cargo trains 10% slower than running when not overweight +/obj/vehicle/train/engine/get_alt_interactions(mob/user) + . = ..() + LAZYADD(., /decl/interaction_handler/train/toggle_ignition) + if(key) + LAZYADD(., /decl/interaction_handler/train/remove_key) -/obj/vehicle/train/cargo/trolley/update_car(var/train_length, var/active_engines) - src.train_length = train_length - src.active_engines = active_engines +/decl/interaction_handler/train + abstract_type = /decl/interaction_handler/train + expected_target_type = /obj/vehicle/train/engine - if(!lead && !tow) - anchored = FALSE +/decl/interaction_handler/train/toggle_ignition + name = "Toggle Ignition" + +/decl/interaction_handler/train/toggle_ignition/invoked(atom/target, mob/user, obj/item/prop) + var/obj/vehicle/train/engine/train = target + if(train.on) + train.stop_engine() else - anchored = TRUE + train.start_engine() + +/decl/interaction_handler/train/remove_key + name = "Remove Key" + +/decl/interaction_handler/train/remove_key/is_possible(atom/target, mob/user, obj/item/prop) + if((. = ..())) + var/obj/vehicle/train/engine/train = target + return train.key + +/decl/interaction_handler/train/remove_key/invoked(atom/target, mob/user, obj/item/prop) + var/obj/vehicle/train/engine/train = target + train.remove_key() diff --git a/code/modules/vehicles/cargo_trolley.dm b/code/modules/vehicles/cargo_trolley.dm new file mode 100644 index 000000000000..f41875d4cd96 --- /dev/null +++ b/code/modules/vehicles/cargo_trolley.dm @@ -0,0 +1,103 @@ +/obj/vehicle/train/trolley + name = "cargo train trolley" + desc = "A large, flat platform made for putting things on." + icon = 'icons/obj/vehicles.dmi' + icon_state = "cargo_trailer" + anchored = FALSE + passenger_allowed = 0 + locked = 0 + buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 8) + + load_item_visible = 1 + load_offset_x = 0 + load_offset_y = 4 + + var/static/list/can_load_types = list( + /obj/machinery, + /obj/structure/closet, + /obj/structure/largecrate, + /obj/structure/reagent_dispensers, + /obj/structure/ore_box, + /mob/living/human + ) + +/obj/vehicle/train/trolley/insert_cell(var/obj/item/cell/cell, var/mob/living/human/H) + return + +//------------------------------------------- +// Loading/unloading procs +//------------------------------------------- +/obj/vehicle/train/trolley/load_onto_vehicle(var/atom/movable/loading, var/mob/user) + if(ismob(loading) && !passenger_allowed) + return 0 + if(!is_type_in_list(loading, can_load_types)) + return 0 + //if there are any items you don't want to be able to interact with, add them to this check + // ~no more shielded, emitter armed death trains + if(istype(loading, /obj/machinery)) + load_object(loading) + else + ..(loading, user) + return !!load + +//Load the object "inside" the trolley and add an overlay of it. +//This prevents the object from being interacted with until it has +// been unloaded. A dummy object is loaded instead so the loading +// code knows to handle it correctly. +/obj/vehicle/train/trolley/proc/load_object(var/atom/movable/loading) + //To prevent loading things from someone's inventory, which wouldn't get handled properly. + if(!isturf(loading.loc) || load || loading.anchored) + return 0 + var/datum/vehicle_dummy_load/dummy_load = new + dummy_load.actual_load = loading + load = dummy_load + loading.forceMove(src) + update_icon() + +/obj/vehicle/train/trolley/unload_from_vehicle(var/mob/user, var/direction) + if(istype(load, /datum/vehicle_dummy_load)) + var/datum/vehicle_dummy_load/dummy_load = load + load = dummy_load.actual_load + dummy_load.actual_load = null + qdel(dummy_load) + update_icon() + ..() + +/obj/vehicle/train/trolley/on_update_icon() + cut_overlays() + var/datum/vehicle_dummy_load/dummy_load = load + if(istype(dummy_load) && dummy_load.actual_load && load_item_visible) + var/atom/movable/loading = dummy_load.actual_load + loading.pixel_x += load_offset_x + loading.pixel_y += load_offset_y + loading.plane = plane + loading.layer = VEHICLE_LOAD_LAYER + add_overlay(loading) + compile_overlays() // We want to reset the pixel values on our load after this. + //we can set these back now since we have already cloned the icon into the overlay + loading.pixel_x = initial(loading.pixel_x) + loading.pixel_y = initial(loading.pixel_y) + loading.layer = initial(loading.layer) + +/obj/vehicle/train/trolley/update_car(var/train_length, var/active_engines) + ..() + anchored = lead || tow + +/obj/vehicle/train/trolley/Bump(atom/Obstacle) + if(!lead) + return //so people can't knock others over by pushing a trolley around + ..() + +/obj/vehicle/train/trolley/attackby(obj/item/used_item, mob/user) + if(open && IS_WIRECUTTER(used_item)) + passenger_allowed = !passenger_allowed + user.visible_message( + SPAN_NOTICE("\The [user] [passenger_allowed ? "cuts" : "mends"] a cable in [src]."), + SPAN_NOTICE("You [passenger_allowed ? "cut" : "mend"] the load limiter cable.") + ) + return TRUE + return ..() + +/obj/vehicle/train/trolley/crossed_mob(var/mob/living/victim) + ..() + attack_log += text("\[[time_stamp()]\] ran over [victim.name] ([victim.ckey])") diff --git a/code/modules/vehicles/quad_bike.dm b/code/modules/vehicles/quad_bike.dm new file mode 100644 index 000000000000..3d01876bc161 --- /dev/null +++ b/code/modules/vehicles/quad_bike.dm @@ -0,0 +1,198 @@ +/obj/vehicle/train/engine/quadbike //It's a train engine, so it can tow trailers. + name = "electric all terrain vehicle" + desc = "A ridable electric ATV designed for all terrain. Except space." + icon = 'icons/obj/vehicles_64x64.dmi' + icon_state = "quad" + on = 0 + powered = 1 + locked = 0 + load_item_visible = 1 + load_offset_x = 0 + buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 5) + pixel_x = -16 + base_speed = 0.45 + car_limit = 1 //It gets a trailer. That's about it. + active_engines = 1 + key_type = /obj/item/key/quadbike + paint_color = "#ffffff" + layer = OBJ_LAYER + vehicle_transit_type = VEHICLE_QUADBIKE + + var/frame_state = "quad" //Custom-item proofing! + var/paint_base = 'icons/obj/vehicles_64x64.dmi' + var/custom_frame = FALSE + var/datum/composite_sound/vehicle_engine/soundloop + +/obj/vehicle/train/engine/quadbike/Initialize() + cell = new /obj/item/cell/high(src) + key = new key_type(src) + soundloop = new(list(src), FALSE) + . = ..() + turn_off() + update_icon() + +/obj/vehicle/train/engine/quadbike/built/Initialize() + key = new key_type(src) + . = ..() + turn_off() + +/obj/vehicle/train/engine/quadbike/random/Initialize() + paint_color = rgb(rand(1,255),rand(1,255),rand(1,255)) + . = ..() + +/obj/vehicle/train/engine/quadbike/Destroy() + QDEL_NULL(soundloop) + return ..() + +/obj/item/key/quadbike + name = "key" + desc = "A keyring with a small steel key, and a blue fob reading \"ZOOM!\"." + icon = 'icons/obj/vehicles.dmi' + icon_state = "quad_keys" + w_class = ITEM_SIZE_TINY + +/obj/vehicle/train/engine/quadbike/forceMove(turf/destination) + var/atom/old_loc = loc + if((. = ..())) + update_vehicle_move_delay(old_loc) + handle_vehicle_icon() + +/obj/vehicle/train/engine/quadbike/Move(turf/destination) + var/atom/old_loc = loc + if((. = ..())) + update_vehicle_move_delay(old_loc) + handle_vehicle_icon() + +/obj/vehicle/train/engine/quadbike/update_vehicle_move_delay(atom/prev_loc) + ..() + update_car(train_length, active_engines) + +/obj/vehicle/train/engine/quadbike/proc/handle_vehicle_icon() + switch(dir) //Due to being a Big Boy sprite, it has to have special pixel shifting to look 'normal' when being driven. + if(1) + pixel_y = -6 + if(2) + pixel_y = -6 + if(4) + pixel_y = 0 + if(8) + pixel_y = 0 + +/obj/vehicle/train/engine/quadbike/attackby(obj/item/used_item, mob/user) + if(istype(used_item, /obj/item/multitool) && open) + var/new_paint = input("Please select a paint color.", "Trailer Color", paint_color) as color|null + if(new_paint && !QDELETED(src) && !QDELETED(used_item) && !QDELETED(user) && !user.incapacitated() && user.get_active_held_item() == used_item) + paint_color = new_paint + update_icon() + return TRUE + return ..() + +/obj/vehicle/train/engine/quadbike/on_update_icon() + ..() + cut_overlays() + + if(custom_frame) + var/image/Bodypaint = new(icon = 'icons/obj/custom_items_vehicle.dmi', icon_state = "[frame_state]_a") + Bodypaint.layer = layer + Bodypaint.color = paint_color + add_overlay(Bodypaint) + + var/image/Overmob = new(icon = 'icons/obj/custom_items_vehicle.dmi', icon_state = "[frame_state]_overlay") //over mobs + var/image/Overmob_color = new(icon = 'icons/obj/custom_items_vehicle.dmi', icon_state = "[frame_state]_overlay_a") //over the over mobs, gives the color. + Overmob.layer = layer + 0.2 + Overmob_color.layer = layer + 0.2 + Overmob_color.color = paint_color + add_overlay(Overmob) + add_overlay(Overmob_color) + return + + var/image/Bodypaint = new(icon = paint_base, icon_state = "[frame_state]_a", layer = src.layer) + Bodypaint.color = paint_color + add_overlay(Bodypaint) + + var/image/Overmob = new(icon = paint_base, icon_state = "[frame_state]_overlay", layer = src.layer + 0.2) //over mobs + var/image/Overmob_color = new(icon = paint_base, icon_state = "[frame_state]_overlay_a", layer = src.layer + 0.2) //over the over mobs, gives the color. + Overmob.layer = ABOVE_HUMAN_LAYER + Overmob_color.layer = ABOVE_HUMAN_LAYER + Overmob_color.color = paint_color + + add_overlay(Overmob) + add_overlay(Overmob_color) + +/obj/vehicle/train/engine/quadbike/Bump(atom/Obstacle) + if(!istype(Obstacle, /atom/movable)) + return + var/atom/movable/A = Obstacle + + if(!A.anchored) + var/turf/T = get_step(A, dir) + if(isturf(T)) + A.Move(T) //bump things away when hit + + if(istype(A, /mob/living)) + var/mob/living/M = A + visible_message(SPAN_DANGER("\The [src] knocks over \the [M]!")) + M.apply_effects(2, 2) // Knock people down for a short moment + M.apply_damages(8 / move_delay) // Smaller amount of damage than a tug, since this will always be possible because Quads don't have safeties. + var/list/throw_dirs = all_throw_dirs.Copy() + if(!emagged) // By the power of Bumpers TM, it won't throw them ahead of the quad's path unless it's emagged or the person turns. + take_damage(round(M.mob_size / 2)) + throw_dirs -= dir + throw_dirs -= get_dir(M, src) //Don't throw it AT the quad either. + else + take_damage(round(M.mob_size / 4)) // Less damage if they actually put the point in to emag it. + var/turf/T2 = get_step(A, pick(throw_dirs)) + M.throw_at(T2, 1, 1, src) + if(isliving(load)) + var/mob/living/D = load + to_chat(D, SPAN_DANGER("You hit \the [M]!")) + admin_attack_log(D, M, "Ran over with [src.name]") + +/obj/vehicle/train/engine/quadbike/crossed_mob(mob/living/victim) + . = ..() + var/list/throw_dirs = all_throw_dirs.Copy() + if(!emagged) + throw_dirs -= dir + if(tow) + throw_dirs -= get_dir(victim, tow) //Don't throw it at the trailer either. + var/turf/T = get_step(victim, pick(throw_dirs)) + victim.throw_at(T, 1, 1, src) + +/obj/vehicle/train/engine/quadbike/turn_on() + ..() + if(on) + visible_message(SPAN_NOTICE("\The [src] rumbles to life."), "You hear something rumble deeply.") + soundloop.start() + +/obj/vehicle/train/engine/quadbike/turn_off() + if(on) + visible_message(SPAN_NOTICE("\The [src] putters before turning off."), "You hear something putter slowly.") + soundloop.stop() + ..() + +/obj/vehicle/train/engine/quadbike/snowmobile + name = "snowmobile" + desc = "An electric snowmobile for traversing snow and ice with ease! Other terrain, not so much." + icon = 'icons/obj/vehicles.dmi' + icon_state = "snowmobile" + load_item_visible = 1 + base_speed = 0.6 + car_limit = 0 + key_type = /obj/item/key/snowmobile + frame_state = "snowmobile" + paint_base = 'icons/obj/vehicles.dmi' + pixel_x = 0 + water_delay = 6 + +/obj/item/key/snowmobile + name = "key" + desc = "A keyring with an ice-blue fob reading \"CHILL\"." + icon = 'icons/obj/vehicles.dmi' + icon_state = "sno_keys" + +/obj/vehicle/train/engine/quadbike/snowmobile/random/Initialize() + paint_color = rgb(rand(1,255),rand(1,255),rand(1,255)) + . = ..() + +/obj/vehicle/train/engine/quadbike/snowmobile/handle_vehicle_icon() + return diff --git a/code/modules/vehicles/quad_trailer.dm b/code/modules/vehicles/quad_trailer.dm new file mode 100644 index 000000000000..a730ab54b656 --- /dev/null +++ b/code/modules/vehicles/quad_trailer.dm @@ -0,0 +1,108 @@ +/* + * Trailer bits and bobs. + */ +/obj/vehicle/train/trolley/trailer + name = "all terrain trailer" + icon = 'icons/obj/vehicles_64x64.dmi' + icon_state = "quadtrailer" + anchored = FALSE + passenger_allowed = 1 + buckle_lying = 1 + locked = 0 + load_item_visible = 1 + load_offset_x = 0 + load_offset_y = 13 + buckle_pixel_shift = list("x" = 0, "y" = 0, "z" = 16) + pixel_x = -16 + paint_color = "#ffffff" + var/mob_offset_y = 16 + +/obj/vehicle/train/trolley/trailer/random/Initialize() + paint_color = rgb(rand(1,255),rand(1,255),rand(1,255)) + . = ..() + +/obj/vehicle/train/trolley/trailer/proc/update_load() + if(load) + var/y_offset = load_offset_y + if(istype(load, /mob/living)) + y_offset = mob_offset_y + load.pixel_x = (initial(load.pixel_x) + 16 + load_offset_x + pixel_x) //Base location for the sprite, plus 16 to center it on the 'base' sprite of the trailer, plus the x shift of the trailer, then shift it by the same pixel_x as the trailer to track it. + load.pixel_y = (initial(load.pixel_y) + y_offset + pixel_y) //Same as the above. + return 1 + return 0 +/obj/vehicle/train/trolley/trailer/Initialize() + . = ..() + update_icon() + +/obj/vehicle/train/trolley/trailer/Move() + var/atom/old_loc = loc + if((. = ..())) + update_trolley_offset(old_loc) + +/obj/vehicle/train/trolley/trailer/forceMove() + var/atom/old_loc = loc + if((. = ..())) + update_trolley_offset(old_loc) + +/obj/vehicle/train/trolley/trailer/proc/update_trolley_offset(var/atom/old_loc) + if(lead) + switch(dir) //Due to being a Big Boy sprite, it has to have special pixel shifting to look 'normal'. + if(1) + default_pixel_y = -10 + default_pixel_x = -16 + if(2) + default_pixel_y = 0 + default_pixel_x = -16 + if(4) + default_pixel_y = 0 + default_pixel_x = -25 + if(8) + default_pixel_y = 0 + default_pixel_x = -5 + else + default_pixel_x = initial(default_pixel_x) + default_pixel_y = initial(default_pixel_y) + reset_offsets(0) + update_load() + +/obj/vehicle/train/trolley/trailer/Bump(atom/Obstacle) + if(!istype(Obstacle, /atom/movable)) + return + + var/atom/movable/A = Obstacle + if(!A.anchored) + var/turf/T = get_step(A, dir) + if(isturf(T)) + A.Move(T) //bump things away when hit + + if(istype(A, /mob/living)) + var/mob/living/M = A + visible_message(SPAN_DANGER("\The [src] knocks over \the [M]!")) + M.apply_effects(1, 1) + M.apply_damages(8 / move_delay) + if(load) + M.apply_damages(4/move_delay) + var/list/throw_dirs = all_throw_dirs.Copy() + if(!emagged) + throw_dirs -= dir + var/turf/T2 = get_step(A, pick(throw_dirs)) + M.throw_at(T2, 1, 1, src) + if(isliving(load)) + var/mob/living/D = load + to_chat(D, SPAN_DANGER("You hit \the [M]!")) + admin_attack_log(D, M, "Ran over with \the [src]") + +/obj/vehicle/train/trolley/trailer/on_update_icon() + ..() + var/image/Bodypaint = new(icon = icon, icon_state = "[initial(icon_state)]_a", layer = src.layer) + Bodypaint.color = paint_color + set_overlays(Bodypaint) + +/obj/vehicle/train/trolley/trailer/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/multitool) && open) + var/new_paint = input("Please select paint color.", "Paint Color", paint_color) as color|null + if(new_paint) + paint_color = new_paint + update_icon() + return + ..() diff --git a/code/modules/vehicles/train.dm b/code/modules/vehicles/train.dm index 0cb43501e1e8..59665c6b326f 100644 --- a/code/modules/vehicles/train.dm +++ b/code/modules/vehicles/train.dm @@ -5,17 +5,18 @@ max_health = 100 fire_dam_coeff = 0.7 brute_dam_coeff = 0.5 + layer = ABOVE_HUMAN_LAYER var/passenger_allowed = 1 - var/active_engines = 0 var/train_length = 0 - var/obj/vehicle/train/lead var/obj/vehicle/train/tow + var/static/list/all_throw_dirs = list(NORTH, SOUTH, EAST, WEST, NORTHWEST, NORTHEAST, SOUTHWEST, SOUTHEAST) + /obj/vehicle/train/user_buckle_mob(mob/living/M, mob/user) - return load(M) + return load_onto_vehicle(M) //------------------------------------------- // Standard procs @@ -95,7 +96,7 @@ return 1 return 0 - unload(user, direction) + unload_from_vehicle(user, direction) to_chat(user, "You climb down from [src].") return 1 @@ -110,7 +111,7 @@ /obj/vehicle/train/receive_mouse_drop(atom/dropping, mob/user, params) . = ..() if(!. && istype(dropping, /atom/movable)) - if(!load(dropping)) + if(!load_onto_vehicle(dropping)) to_chat(user, SPAN_WARNING("You were unable to load \the [dropping] onto \the [src].")) return TRUE @@ -121,9 +122,9 @@ if(user != load && (user in src)) user.forceMove(loc) else if(load) - unload(user) + unload_from_vehicle(user) else if(!load && !user.buckled) - load(user) + load_onto_vehicle(user) return TRUE /obj/vehicle/train/verb/unlatch_v() @@ -235,5 +236,7 @@ T.update_car(train_length, active_engines) T = T.lead -/obj/vehicle/train/proc/update_car(var/train_length, var/active_engines) - return +/obj/vehicle/train/proc/update_car(var/_train_length, var/_active_engines) + SHOULD_CALL_PARENT(TRUE) + train_length = _train_length + active_engines = _active_engines diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index 111eb94818e2..6086aa1fab26 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -6,7 +6,7 @@ /obj/vehicle name = "vehicle" icon = 'icons/obj/vehicles.dmi' - layer = ABOVE_HUMAN_LAYER + layer = OBJ_LAYER density = TRUE anchored = TRUE animate_movement=1 @@ -17,6 +17,11 @@ buckle_movable = 1 buckle_lying = 0 + var/const/VEHICLE_GENERIC = 1 + var/const/VEHICLE_QUADBIKE = 2 + var/const/VEHICLE_SNOWMOBILE = 3 + + var/vehicle_transit_type = VEHICLE_GENERIC var/attack_log = null var/on = 0 var/fire_dam_coeff = 1.0 @@ -26,7 +31,13 @@ var/stat = 0 var/emagged = 0 var/powered = 0 //set if vehicle is powered and should use fuel when moving - var/move_delay = 1 //set this to limit the speed of the vehicle + + /// How long a single move takes with this vehicle. + var/move_delay = 1 + /// The base delay of a move with this vehicle, assuming no terrain modifiers. If null, uses default running + var/base_speed + /// Speed when a location is flooded. + var/water_delay = 4 var/obj/item/cell/cell var/charge_use = 200 // W @@ -35,10 +46,31 @@ var/load_item_visible = 1 //set if the loaded item should be overlayed on the vehicle sprite var/load_offset_x = 0 //pixel_x offset for item overlay var/load_offset_y = 0 //pixel_y offset for item overlay - //------------------------------------------- // Standard procs //------------------------------------------- +/obj/vehicle/Initialize(mapload) + update_vehicle_move_delay(null) + base_speed ||= get_config_value(/decl/config/num/movement_run) + . = ..() + +/obj/vehicle/proc/update_vehicle_move_delay(atom/prev_loc) + + var/turf/floor/prev_turf = prev_loc + var/turf/floor/this_turf = loc + if(istype(prev_turf) && istype(this_turf) && this_turf.get_topmost_flooring() == prev_turf.get_topmost_flooring() && this_turf.check_fluid_depth(FLUID_SHALLOW) == prev_turf.check_fluid_depth(FLUID_SHALLOW)) + return // Same speed if terrain type doesn't change + + var/terrain_mod + if(loc?.check_fluid_depth(FLUID_SHALLOW)) + terrain_mod = water_delay + else if(istype(this_turf)) + terrain_mod = this_turf.get_vehicle_transit_delay(src) + + if(isnull(terrain_mod)) + move_delay = base_speed + else + move_delay = base_speed * terrain_mod /obj/vehicle/Move() if(world.time > l_move_time + move_delay) @@ -162,7 +194,7 @@ /obj/vehicle/unbuckle_mob(mob/user) . = ..(user) if(load == .) - unload(.) + unload_from_vehicle(.) //------------------------------------------- // Vehicle procs @@ -207,7 +239,7 @@ var/mob/living/M = load M.apply_effects(5, 5) - unload() + unload_from_vehicle() new /obj/effect/gibspawner/robot(my_turf) new /obj/effect/decal/cleanable/blood/oil(src.loc) @@ -261,7 +293,7 @@ // the vehicle load() definition before // calling this parent proc. //------------------------------------------- -/obj/vehicle/proc/load(var/atom/movable/loading) +/obj/vehicle/proc/load_onto_vehicle(var/atom/movable/loading) //This loads objects onto the vehicle so they can still be interacted with. //Define allowed items for loading in specific vehicle definitions. if(!isturf(loading.loc)) //To prevent loading things from someone's inventory, which wouldn't get handled properly. @@ -293,7 +325,7 @@ return 1 -/obj/vehicle/proc/unload(var/mob/user, var/direction) +/obj/vehicle/proc/unload_from_vehicle(var/mob/user, var/direction) if(!load) return diff --git a/code/modules/xenoarcheaology/artifacts/effects/gas_generation.dm b/code/modules/xenoarcheaology/artifacts/effects/gas_generation.dm index 33ba58783526..95569c0bf38a 100644 --- a/code/modules/xenoarcheaology/artifacts/effects/gas_generation.dm +++ b/code/modules/xenoarcheaology/artifacts/effects/gas_generation.dm @@ -5,7 +5,7 @@ /datum/artifact_effect/gas/New() ..() if(!spawned_gas) - spawned_gas = pick(decls_repository.get_decl_paths_of_subtype(/decl/material/gas)) + spawned_gas = pick(get_filterable_material_types(as_list = TRUE)) operation_type = pick((XA_EFFECT_TOUCH), (XA_EFFECT_AURA)) origin_type = XA_EFFECT_SYNTH diff --git a/code/modules/xenoarcheaology/artifacts/triggers/gas.dm b/code/modules/xenoarcheaology/artifacts/triggers/gas.dm index 678dcc16af07..1dfa739951ba 100644 --- a/code/modules/xenoarcheaology/artifacts/triggers/gas.dm +++ b/code/modules/xenoarcheaology/artifacts/triggers/gas.dm @@ -5,7 +5,7 @@ /datum/artifact_trigger/gas/New() if(!gas_needed) - gas_needed = list(pick(decls_repository.get_decl_paths_of_subtype(/decl/material/gas)) = rand(1,10)) + gas_needed = list(pick(get_filterable_material_types(as_list = TRUE)) = rand(1,10)) var/decl/material/gas/gas = GET_DECL(gas_needed[1]) name = "concentration of [gas.name]" diff --git a/code/modules/xenoarcheaology/finds/find_types/mundane.dm b/code/modules/xenoarcheaology/finds/find_types/mundane.dm index 7d1ea51ec777..211c5733dc4d 100644 --- a/code/modules/xenoarcheaology/finds/find_types/mundane.dm +++ b/code/modules/xenoarcheaology/finds/find_types/mundane.dm @@ -22,7 +22,7 @@ "It's like no [item_type] you've ever seen before.", "It's a mystery how anyone is supposed to eat with this.", "You wonder what the creator's mouth was shaped like.") - + // Coin /decl/archaeological_find/coin item_type = "coin" @@ -56,7 +56,7 @@ /decl/archaeological_find/tank/spawn_item(atom/loc) var/obj/item/tank/new_item = ..() new_item.air_contents.gas.Cut() - new_item.air_contents.adjust_gas(pick(decls_repository.get_decl_paths_of_subtype(/decl/material/gas)),15) + new_item.air_contents.adjust_gas(pick(get_filterable_material_types(as_list = TRUE)),15) return new_item /decl/archaeological_find/tank/generate_name() diff --git a/code/unit_tests/atmospherics_tests.dm b/code/unit_tests/atmospherics_tests.dm index 0fa4febe7d55..e28fcc06a378 100644 --- a/code/unit_tests/atmospherics_tests.dm +++ b/code/unit_tests/atmospherics_tests.dm @@ -39,7 +39,7 @@ /datum/unit_test/atmos_machinery/proc/check_moles_conserved(var/case_name, var/list/before_gas_mixes, var/list/after_gas_mixes) var/failed = FALSE - for(var/gasid in decls_repository.get_decl_paths_of_subtype(/decl/material/gas)) + for(var/gasid in get_filterable_material_types()) var/before = 0 for(var/gasmix in before_gas_mixes) var/datum/gas_mixture/G = before_gas_mixes[gasmix] @@ -195,7 +195,7 @@ name = "ATMOS MACHINERY: scrub_gas() Conserves Moles" /datum/unit_test/atmos_machinery/conserve_moles/scrub_gas/start_test() - var/list/filtering = decls_repository.get_decl_paths_of_subtype(/decl/material/gas) + var/list/filtering = get_filterable_material_types(as_list = TRUE) for(var/case_name in test_cases) var/gas_mix_data = test_cases[case_name] var/list/before_gas_mixes = create_gas_mixes(gas_mix_data) @@ -211,7 +211,7 @@ name = "ATMOS MACHINERY: filter_gas() Conserves Moles" /datum/unit_test/atmos_machinery/conserve_moles/filter_gas/start_test() - var/list/filtering = decls_repository.get_decl_paths_of_subtype(/decl/material/gas) + var/list/filtering = get_filterable_material_types(as_list = TRUE) for(var/case_name in test_cases) var/gas_mix_data = test_cases[case_name] var/list/before_gas_mixes = create_gas_mixes(gas_mix_data) @@ -231,7 +231,7 @@ var/list/after_gas_mixes = create_gas_mixes(gas_mix_data) var/list/filtering = list() - for(var/gasid in decls_repository.get_decl_paths_of_subtype(/decl/material/gas)) + for(var/gasid in get_filterable_material_types()) filtering[gasid] = after_gas_mixes["sink"] //just filter everything to sink filter_gas_multi(null, filtering, after_gas_mixes["source"], after_gas_mixes["sink"], null, INFINITY) @@ -250,7 +250,7 @@ var/list/after_gas_mixes = create_gas_mixes(gas_mix_data) var/list/mix_sources = list() - var/list/all_gasses = decls_repository.get_decl_paths_of_subtype(/decl/material/gas) + var/list/all_gasses = get_filterable_material_types(as_list = TRUE) var/gas_count = length(all_gasses) for(var/gasid in all_gasses) var/datum/gas_mixture/mix_source = after_gas_mixes["sink"] diff --git a/code/unit_tests/icon_tests.dm b/code/unit_tests/icon_tests.dm index 279daba8a344..89814527ca65 100644 --- a/code/unit_tests/icon_tests.dm +++ b/code/unit_tests/icon_tests.dm @@ -85,42 +85,59 @@ return 1 /datum/unit_test/icon_test/signs_shall_have_existing_icon_states - name = "ICON STATE: Signs shall have existing icon states" - var/list/skip_types = list( - // Posters use a decl to set their icon and handle their own validation. - /obj/structure/sign/poster - ) + name = "ICON STATE: Sign Subtypes Shall Have Existing Icon States" /datum/unit_test/icon_test/signs_shall_have_existing_icon_states/start_test() var/list/failures = list() - for(var/sign_type in typesof(/obj/structure/sign)) - var/obj/structure/sign/sign = sign_type - if(TYPE_IS_ABSTRACT(sign)) - continue + var/static/list/skip_icon_state_checks = list( + // Posters use a decl to set their icon and handle their own validation. + /obj/structure/sign/poster + ) + + var/list/icon_states_to_find = list() + for(var/obj/structure/sign/sign as anything in typesof(/obj/structure/sign)) var/skip = FALSE - for(var/skip_type in skip_types) - if(ispath(sign_type, skip_type)) + for(var/skip_type in skip_icon_state_checks) + if(ispath(sign, skip_type)) skip = TRUE break if(skip) continue - var/check_state = initial(sign.icon_state) - if(!check_state) - failures += "[sign] - null icon_state" - continue - var/check_icon = initial(sign.icon) - if(!check_icon) - failures += "[sign] - null icon_state" + var/sign_state = sign::icon_state + var/sign_icon = sign::icon + + if(!(sign_icon in icon_states_to_find)) + icon_states_to_find[sign_icon] = icon_states(sign_icon) || list() + icon_states_to_find[sign_icon] -= sign_state + + if(TYPE_IS_ABSTRACT(sign)) continue - if(!check_state_in_icon(check_state, check_icon)) - failures += "[sign] - missing icon_state '[check_state]' in icon '[check_icon]" - if(failures.len) - fail("Signs with missing icon states:\n\t-[jointext(failures, "\n\t-")]") + + if(!sign_icon) + failures += "[sign] - missing icon" + else if(!istext(sign_state)) + failures += "[sign] - missing or invalid icon_state" + else if(!check_state_in_icon(sign_state, sign_icon)) + failures += "[sign] - missing icon_state '[sign_state]' from icon '[sign_icon]'" + + var/static/list/skip_extraneous_state_checks = list( + // Barsign icon_state is set by user, skip testing it here. + 'icons/obj/barsigns.dmi' + ) + + for(var/sign_icon in icon_states_to_find) + var/list/remaining = icon_states_to_find[sign_icon] + if(!(sign_icon in skip_extraneous_state_checks) && length(remaining)) + failures += "[sign_icon] - unused icon_states: [jointext(remaining, ", ")]" + + if(length(failures)) + fail("[length(failures)] issue\s with sign icons or icon states:\n[jointext(failures, "\n")]") else - pass("All signs have valid icon states.") + pass("All signs have valid icon states and no extraneous icon states.") + return 1 /datum/unit_test/icon_test/random_spawners_shall_have_existing_icon_states diff --git a/html/changelog.html b/html/changelog.html index fb311db0cf29..9e0ead821a49 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,6 +52,12 @@ -->
+

10 January 2026

+

Penelope Haze updated:

+ +

06 January 2026

MistakeNot4892 updated: