diff --git a/.vscode/launch.json b/.vscode/launch.json index 42a293a333805..bf3a209531db0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,6 +8,13 @@ "preLaunchTask": "Build All", "dmb": "${workspaceFolder}/${command:CurrentDMB}" }, + { + "type": "byond", + "request": "launch", + "name": "Launch DreamSeeker (low memory mode)", + "preLaunchTask": "Build All (low memory mode)", + "dmb": "${workspaceFolder}/${command:CurrentDMB}" + }, { "type": "byond", "request": "launch", @@ -16,6 +23,14 @@ "dmb": "${workspaceFolder}/${command:CurrentDMB}", "dreamDaemon": true }, + { + "type": "byond", + "request": "launch", + "name": "Launch DreamDaemon (low memory mode)", + "preLaunchTask": "Build All (low memory mode)", + "dmb": "${workspaceFolder}/${command:CurrentDMB}", + "dreamDaemon": true + }, { "name": "Debug External Libraries", "type": "cppvsdbg", @@ -27,6 +42,18 @@ "-trusted" ], "preLaunchTask": "Build All" + }, + { + "name": "Debug External Libraries (low memory mode)", + "type": "cppvsdbg", + "request": "launch", + "program": "${command:dreammaker.returnDreamDaemonPath}", + "cwd": "${workspaceRoot}", + "args": [ + "${command:dreammaker.getFilenameDmb}", + "-trusted" + ], + "preLaunchTask": "Build All (low memory mode)" } ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 711a13c7846c1..18fb2fde15296 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -24,6 +24,30 @@ "dependsOn": "dm: reparse", "label": "Build All" }, + { + "type": "process", + "command": "tools/build/build", + "args": ["-DLOWMEMORYMODE"], + "windows": { + "command": ".\\tools\\build\\build.bat", + "args": ["-DLOWMEMORYMODE"] + }, + "options": { + "env": { + "DM_EXE": "${config:dreammaker.byondPath}" + } + }, + "problemMatcher": [ + "$dreammaker", + "$tsc", + "$eslint-stylish" + ], + "group": { + "kind": "build" + }, + "dependsOn": "dm: reparse", + "label": "Build All (low memory mode)" + }, { "type": "dreammaker", "dme": "tgstation.dme", diff --git a/_maps/gateway_test.json b/_maps/gateway_test.json index 5f4f8eec8a9c8..0b3923162f62c 100644 --- a/_maps/gateway_test.json +++ b/_maps/gateway_test.json @@ -10,6 +10,7 @@ "/datum/unit_test/job_roundstart_spawnpoints", "/datum/unit_test/required_map_items", "/datum/unit_test/space_dragon_expiration", + "/datum/unit_test/spy_bounty", "/datum/unit_test/traitor" ] } diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index 7f2a071eec9bd..eeb946983e820 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -5554,6 +5554,7 @@ "ciT" = ( /obj/structure/table/glass, /obj/item/radio/intercom/directional/east, +/obj/machinery/recharger, /turf/open/floor/iron/white, /area/station/science/auxlab/firing_range) "cjm" = ( @@ -50924,7 +50925,7 @@ /turf/open/misc/sandy_dirt, /area/station/maintenance/starboard/aft) "rLN" = ( -/obj/structure/safe, +/obj/structure/safe/vault, /obj/item/lazarus_injector, /obj/effect/turf_decal/bot_white, /obj/effect/turf_decal/siding/thinplating_new{ diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 30e548835e8be..20e27418cb07f 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -36847,7 +36847,7 @@ /turf/open/floor/iron/large, /area/station/security/processing) "jeZ" = ( -/obj/structure/safe, +/obj/structure/safe/vault, /obj/item/clothing/neck/stethoscope, /obj/item/book{ desc = "An undeniably handy book."; diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 759e4f09d46df..524ffda66ae84 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -25361,7 +25361,7 @@ /turf/open/floor/plating, /area/station/ai_monitored/turret_protected/aisat_interior) "hPf" = ( -/obj/structure/safe, +/obj/structure/safe/vault, /obj/item/clothing/head/costume/bearpelt, /obj/item/reagent_containers/cup/glass/drinkingglass/shotglass, /obj/item/reagent_containers/cup/glass/drinkingglass/shotglass, diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 7ac2807b4801e..aa526f33de7b0 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -41714,7 +41714,7 @@ /turf/open/floor/plating, /area/station/maintenance/solars/starboard/aft) "oXK" = ( -/obj/structure/safe, +/obj/structure/safe/vault, /obj/item/storage/briefcase/secure/riches, /obj/item/storage/backpack/duffelbag/syndie/hitman, /obj/item/card/id/advanced/silver/reaper, diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm index e83a6941c8855..794c008083714 100644 --- a/_maps/map_files/NorthStar/north_star.dmm +++ b/_maps/map_files/NorthStar/north_star.dmm @@ -77446,6 +77446,8 @@ "tVp" = ( /obj/machinery/light_switch/directional/north, /obj/machinery/status_display/ai/directional/east, +/obj/structure/table/reinforced/titaniumglass, +/obj/item/piggy_bank/vault, /turf/open/floor/circuit, /area/station/ai_monitored/command/nuke_storage) "tVq" = ( @@ -80646,6 +80648,10 @@ /obj/machinery/power/apc/auto_name/directional/north, /obj/structure/cable, /obj/machinery/status_display/ai/directional/west, +/obj/structure/table/reinforced/titaniumglass, +/obj/item/maneki_neko{ + pixel_y = 4 + }, /turf/open/floor/circuit, /area/station/ai_monitored/command/nuke_storage) "uNp" = ( diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index ae87f6f63578c..e4edbf305c607 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -44635,7 +44635,7 @@ /turf/open/floor/iron, /area/station/construction/mining/aux_base) "oQO" = ( -/obj/structure/safe, +/obj/structure/safe/vault, /obj/item/clothing/head/costume/bearpelt, /obj/item/gun/ballistic/revolver/russian, /obj/item/ammo_box/a357, diff --git a/_maps/multiz_debug.json b/_maps/multiz_debug.json index 2f07130c1f938..7f44673da3da6 100644 --- a/_maps/multiz_debug.json +++ b/_maps/multiz_debug.json @@ -5,7 +5,8 @@ "map_file": "multiz.dmm", "ignored_unit_tests": [ "/datum/unit_test/job_roundstart_spawnpoints", - "/datum/unit_test/required_map_items" + "/datum/unit_test/required_map_items", + "/datum/unit_test/spy_bounty" ], "traits": [ { diff --git a/_maps/runtimestation.json b/_maps/runtimestation.json index 093592f5c7084..ee042270c0a3e 100644 --- a/_maps/runtimestation.json +++ b/_maps/runtimestation.json @@ -6,7 +6,8 @@ "space_ruin_levels": 1, "ignored_unit_tests": [ "/datum/unit_test/job_roundstart_spawnpoints", - "/datum/unit_test/required_map_items" + "/datum/unit_test/required_map_items", + "/datum/unit_test/spy_bounty" ], "shuttles": { "cargo": "cargo_delta" diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index af1cb68c41cad..0323bc1173512 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -36,6 +36,9 @@ ///How long pirates will wait for a response before attacking #define RESPONSE_MAX_TIME 2 MINUTES +/// How long till a spessman should come back after being captured and sent to the holding facility (which some antags use) +#define COME_BACK_FROM_CAPTURE_TIME 6 MINUTES + //ERT Types #define ERT_BLUE "Blue" #define ERT_RED "Red" diff --git a/code/__DEFINES/basic_mobs.dm b/code/__DEFINES/basic_mobs.dm index c7275f7c423e7..b673d0e7a120b 100644 --- a/code/__DEFINES/basic_mobs.dm +++ b/code/__DEFINES/basic_mobs.dm @@ -12,6 +12,8 @@ #define FLAMMABLE_MOB (1<<3) /// Mob never takes damage from unarmed attacks #define IMMUNE_TO_FISTS (1<<4) +/// Mob is immune to getting wet +#define IMMUNE_TO_GETTING_WET (1<<5) /// Temporary trait applied when an attack forecast animation has completed #define TRAIT_BASIC_ATTACK_FORECAST "trait_basic_attack_forecast" diff --git a/code/__DEFINES/blackmarket.dm b/code/__DEFINES/blackmarket.dm index 5494c371db7b4..c3b8ad0bf4622 100644 --- a/code/__DEFINES/blackmarket.dm +++ b/code/__DEFINES/blackmarket.dm @@ -7,4 +7,5 @@ #define SHIPPING_METHOD_TELEPORT "Teleport" // Throws the item from somewhere at the station. #define SHIPPING_METHOD_LAUNCH "Launch" - +// Sends a supply pod to the buyer's location, showy. +#define SHIPPING_METHOD_SUPPLYPOD "Supply Pod" diff --git a/code/__DEFINES/cargo.dm b/code/__DEFINES/cargo.dm index 63d5682ef0f37..1d34ed6b3cff8 100644 --- a/code/__DEFINES/cargo.dm +++ b/code/__DEFINES/cargo.dm @@ -64,3 +64,10 @@ #define SUPPLY_PACK_UNCOMMON_DISCOUNTABLE "uncommon_discount" ///Discount category for the silly, overpriced, joke content, sometimes useful or plain bad. #define SUPPLY_PACK_RARE_DISCOUNTABLE "rare_discount" + +///Standard export define for not selling the item. +#define EXPORT_NOT_SOLD 0 +///Sell the item +#define EXPORT_SOLD 1 +///Sell the item, but for the love of god, don't delete it, we're handling it in a fancier way. +#define EXPORT_SOLD_DONT_DELETE 2 diff --git a/code/__DEFINES/dcs/signals/signals_blackmarket.dm b/code/__DEFINES/dcs/signals/signals_blackmarket.dm new file mode 100644 index 0000000000000..86e3d9277a1c0 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_blackmarket.dm @@ -0,0 +1,2 @@ +///From /datum/market_item/spawn_item(): (uplink, shipping_method, shipping_loc) +#define COMSIG_MARKET_ITEM_SPAWNED "market_item_spawned" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index b1914cc966bd4..d947e42302ed8 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -171,6 +171,9 @@ ///From obj/item/toy/crayon/spraycan #define COMSIG_LIVING_MOB_PAINTED "living_mob_painted" +///From obj/closet/supplypod/return_victim: (turf/destination) +#define COMSIG_LIVING_RETURN_FROM_CAPTURE "living_return_from_capture" + ///From mob/living/proc/wabbajack(): (randomize_type) #define COMSIG_LIVING_PRE_WABBAJACKED "living_mob_wabbajacked" /// Return to stop the rest of the wabbajack from triggering. diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 91dbba15ff4d6..da4fc0fcb2f36 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -406,6 +406,10 @@ ///sent to the projectile when spawning the item (shrapnel) that may be embedded: (new_item) #define COMSIG_PROJECTILE_ON_SPAWN_EMBEDDED "projectile_on_spawn_embedded" +/// from /obj/projectile/energy/fisher/on_hit() or /obj/item/gun/energy/recharge/fisher when striking a target +#define COMSIG_HIT_BY_SABOTEUR "hit_by_saboteur" + #define COMSIG_SABOTEUR_SUCCESS (1<<0) + // /obj/vehicle/sealed/car/vim signals ///from /datum/action/vehicle/sealed/noise/chime/Trigger(): () diff --git a/code/__DEFINES/dcs/signals/signals_saboteur.dm b/code/__DEFINES/dcs/signals/signals_saboteur.dm deleted file mode 100644 index 5b0fef52aee66..0000000000000 --- a/code/__DEFINES/dcs/signals/signals_saboteur.dm +++ /dev/null @@ -1,5 +0,0 @@ -// Light disruptor. Not to be confused with the light eater, which permanently disables lights. - -/// from /obj/projectile/energy/fisher/on_hit() or /obj/item/gun/energy/recharge/fisher when striking a target -#define COMSIG_HIT_BY_SABOTEUR "HIT_BY_SABOTEUR" - #define COMSIG_SABOTEUR_SUCCESS (1<<0) diff --git a/code/__DEFINES/instruments.dm b/code/__DEFINES/instruments.dm index fa09eee0dd799..bf54ddf1b908e 100644 --- a/code/__DEFINES/instruments.dm +++ b/code/__DEFINES/instruments.dm @@ -6,6 +6,8 @@ /// Max number of playing notes per instrument. #define CHANNELS_PER_INSTRUMENT 128 +/// Minimum length a note should ever go for +#define INSTRUMENT_MIN_TOTAL_SUSTAIN 0.1 /// Maximum length a note should ever go for #define INSTRUMENT_MAX_TOTAL_SUSTAIN (5 SECONDS) @@ -16,8 +18,8 @@ /// Minimum volume for when the sound is considered dead. #define INSTRUMENT_MIN_SUSTAIN_DROPOFF 0 -#define SUSTAIN_LINEAR 1 -#define SUSTAIN_EXPONENTIAL 2 +#define SUSTAIN_LINEAR "Linear" +#define SUSTAIN_EXPONENTIAL "Exponential" // /datum/instrument instrument_flags #define INSTRUMENT_LEGACY (1<<0) //Legacy instrument. Implies INSTRUMENT_DO_NOT_AUTOSAMPLE diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 492a0a06a8850..d4730ce0bb711 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -115,6 +115,7 @@ #define LOG_CATEGORY_TOOL "tool" #define LOG_CATEGORY_TRANSPORT "transport" #define LOG_CATEGORY_VIRUS "virus" +#define LOG_CATEGORY_CAVE_GENERATION "cave-generation" // Admin categories #define LOG_CATEGORY_ADMIN "admin" diff --git a/code/__DEFINES/mining.dm b/code/__DEFINES/mining.dm index 11d150a453db8..4b233a5d4f171 100644 --- a/code/__DEFINES/mining.dm +++ b/code/__DEFINES/mining.dm @@ -39,6 +39,17 @@ /// The chance of ore spawning in a wall that is VENT_PROX_FAR tiles to a vent. #define VENT_CHANCE_FAR 1 +/// The amount of ore that is mined from a wall that is VENT_PROX_VERY_HIGH tiles to a vent. +#define ORE_WALL_VERY_HIGH 5 +/// The amount of ore that is mined from a wall that is VENT_PROX_HIGH tiles to a vent. +#define ORE_WALL_HIGH 4 +/// The amount of ore that is mined from a wall that is VENT_PROX_MEDIUM tiles to a vent. +#define ORE_WALL_MEDIUM 3 +/// The amount of ore that is mined from a wall that is VENT_PROX_LOW tiles to a vent. +#define ORE_WALL_LOW 2 +/// The amount of ore that is mined from a wall that is VENT_PROX_FAR tiles to a vent. +#define ORE_WALL_FAR 1 + /// The number of points a miner gets for discovering a vent, multiplied by BOULDER_SIZE when completing a wave defense minus the discovery bonus. #define MINER_POINT_MULTIPLIER 100 /// The multiplier that gets applied for automatically generated mining points. diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm index e822c0c2a77cd..73351de60f2c4 100644 --- a/code/__DEFINES/robots.dm +++ b/code/__DEFINES/robots.dm @@ -189,6 +189,8 @@ DEFINE_BITFIELD(bot_cover_flags, list( #define JUDGE_IDCHECK (1<<1) #define JUDGE_WEAPONCHECK (1<<2) #define JUDGE_RECORDCHECK (1<<3) +///lowered threat level +#define JUDGE_CHILLOUT (1<<4) /// Above this level of assessed threat, Beepsky will attack you #define THREAT_ASSESS_DANGEROUS 4 @@ -206,6 +208,8 @@ DEFINE_BITFIELD(bot_cover_flags, list( #define SECBOT_CHECK_RECORDS (1<<3) ///Whether we will stun & cuff or endlessly stun #define SECBOT_HANDCUFF_TARGET (1<<4) +///if it's currently affected by a saboteur bolt (lowered perp threat level) +#define SECBOT_SABOTEUR_AFFECTED (1<<5) DEFINE_BITFIELD(security_mode_flags, list( "SECBOT_DECLARE_ARRESTS" = SECBOT_DECLARE_ARRESTS, @@ -213,6 +217,7 @@ DEFINE_BITFIELD(security_mode_flags, list( "SECBOT_CHECK_WEAPONS" = SECBOT_CHECK_WEAPONS, "SECBOT_CHECK_RECORDS" = SECBOT_CHECK_RECORDS, "SECBOT_HANDCUFF_TARGET" = SECBOT_HANDCUFF_TARGET, + "SECBOT_SABOTEUR_AFFECTED" = SECBOT_SABOTEUR_AFFECTED, )) //MedBOT defines diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 47adc0552ba54..df1a5c5029367 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -509,6 +509,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Is the mob standing on an elevated surface? This prevents them from dropping down if not elevated first. #define TRAIT_ON_ELEVATED_SURFACE "on_elevated_surface" +/// Does the mob ignore elevation? (e.g. xeno larvas on hiding) +#define TRAIT_IGNORE_ELEVATION "ignore_elevation" /// Prevents you from twohanding weapons. #define TRAIT_NO_TWOHANDING "no_twohanding" diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm index b77942bad8719..8719f45f18fc0 100644 --- a/code/_globalvars/lists/mapping.dm +++ b/code/_globalvars/lists/mapping.dm @@ -151,3 +151,26 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list( /mob/living/simple_animal/hostile/megafauna/colossus = 2, /mob/living/simple_animal/hostile/megafauna/dragon = 4, )) + +/// List of how many minerals spawned based on proximity to an ore vent. +GLOBAL_LIST_INIT(post_ore_random, list( + "[ORE_WALL_FAR]" = 0, + "[ORE_WALL_LOW]" = 0, + "[ORE_WALL_MEDIUM]" = 0, + "[ORE_WALL_HIGH]" = 0, + "[ORE_WALL_VERY_HIGH]" = 0, +)) +/// List of how many minerals spawned randomly off of mining Z-levels, and at what quantity. +GLOBAL_LIST_INIT(post_ore_manual, list( + "[ORE_WALL_FAR]" = 0, + "[ORE_WALL_LOW]" = 0, + "[ORE_WALL_MEDIUM]" = 0, + "[ORE_WALL_HIGH]" = 0, + "[ORE_WALL_VERY_HIGH]" = 0, +)) +/// List of how many ore vents spawned, and of what size. +GLOBAL_LIST_INIT(ore_vent_sizes, list( + LARGE_VENT_TYPE = 0, + MEDIUM_VENT_TYPE = 0, + SMALL_VENT_TYPE = 0, +)) diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 3fc7e801265e5..5f4208a532ef9 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -255,6 +255,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_HULK" = TRAIT_HULK, "TRAIT_HUSK" = TRAIT_HUSK, "TRAIT_ID_APPRAISER" = TRAIT_ID_APPRAISER, + "TRAIT_IGNORE_ELEVATION" = TRAIT_IGNORE_ELEVATION, "TRAIT_IGNOREDAMAGESLOWDOWN" = TRAIT_IGNOREDAMAGESLOWDOWN, "TRAIT_IGNORESLOWDOWN" = TRAIT_IGNORESLOWDOWN, "TRAIT_IGNORING_GRAVITY" = TRAIT_IGNORING_GRAVITY, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 1d2b5f748c3f1..e424b014b3a76 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -96,6 +96,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_HIDE_EXTERNAL_ORGANS" = TRAIT_HIDE_EXTERNAL_ORGANS, "TRAIT_HOLY" = TRAIT_HOLY, "TRAIT_HUSK" = TRAIT_HUSK, + "TRAIT_IGNORE_ELEVATION" = TRAIT_IGNORE_ELEVATION, "TRAIT_IGNOREDAMAGESLOWDOWN" = TRAIT_IGNOREDAMAGESLOWDOWN, "TRAIT_IGNORESLOWDOWN" = TRAIT_IGNORESLOWDOWN, "TRAIT_ILLITERATE" = TRAIT_ILLITERATE, diff --git a/code/controllers/subsystem/blackmarket.dm b/code/controllers/subsystem/blackmarket.dm index bdd342cbf3d04..ea70b5c722904 100644 --- a/code/controllers/subsystem/blackmarket.dm +++ b/code/controllers/subsystem/blackmarket.dm @@ -5,9 +5,10 @@ SUBSYSTEM_DEF(blackmarket) /// Descriptions for each shipping methods. var/shipping_method_descriptions = list( - SHIPPING_METHOD_LAUNCH="Launches the item at the station from space, cheap but you might not receive your item at all.", - SHIPPING_METHOD_LTSRBT="Long-To-Short-Range-Bluespace-Transceiver, a machine that receives items outside the station and then teleports them to the location of the uplink.", - SHIPPING_METHOD_TELEPORT="Teleports the item in a random area in the station, you get 60 seconds to get there first though." + SHIPPING_METHOD_LAUNCH = "Launches the item at the station from space, cheap but you might not receive your item at all.", + SHIPPING_METHOD_LTSRBT = "Long-To-Short-Range-Bluespace-Transceiver, a machine that receives items outside the station and then teleports them to the location of the uplink.", + SHIPPING_METHOD_TELEPORT = "Teleports the item in a random area in the station, you get 60 seconds to get there first though.", + SHIPPING_METHOD_SUPPLYPOD = "Ships the item inside a supply pod at your exact location. Showy, speedy and expensive.", ) /// List of all existing markets. @@ -42,11 +43,7 @@ SUBSYSTEM_DEF(blackmarket) var/datum/market_purchase/purchase = queued_purchases[1] queued_purchases.Cut(1,2) - // Uh oh, uplink is gone. We will just keep the money and you will not get your order. - if(!purchase.uplink || QDELETED(purchase.uplink)) - queued_purchases -= purchase - qdel(purchase) - continue + var/mob/buyer = recursive_loc_check(purchase.uplink.loc, /mob) switch(purchase.method) // Find a ltsrbt pad and make it handle the shipping. @@ -54,65 +51,71 @@ SUBSYSTEM_DEF(blackmarket) if(!telepads.len) continue // Prioritize pads that don't have a cooldown active. - var/free_pad_found = FALSE - for(var/obj/machinery/ltsrbt/pad in telepads) + var/obj/machinery/ltsrbt/free_pad_found + for(var/obj/machinery/ltsrbt/pad as anything in telepads) if(pad.recharge_cooldown) continue pad.add_to_queue(purchase) - queued_purchases -= purchase - free_pad_found = TRUE + free_pad_found = pad break - if(free_pad_found) + if(isnull(free_pad_found)) continue - var/obj/machinery/ltsrbt/pad = pick(telepads) - - to_chat(recursive_loc_check(purchase.uplink.loc, /mob), span_notice("[purchase.uplink] flashes a message noting that the order is being processed by [pad].")) + to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting that the order is being processed by [free_pad_found].")) - queued_purchases -= purchase - pad.add_to_queue(purchase) // Get random area, throw it somewhere there. if(SHIPPING_METHOD_TELEPORT) var/turf/targetturf = get_safe_random_station_turf() // This shouldn't happen. if (!targetturf) continue + queued_purchases -= purchase - to_chat(recursive_loc_check(purchase.uplink.loc, /mob), span_notice("[purchase.uplink] flashes a message noting that the order is being teleported to [get_area(targetturf)] in 60 seconds.")) + to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting that the order is being teleported to [get_area(targetturf)] in 60 seconds.")) // do_teleport does not want to teleport items from nullspace, so it just forceMoves and does sparks. - addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/controller/subsystem/blackmarket,fake_teleport), purchase.entry.spawn_item(), targetturf), 60 SECONDS) - queued_purchases -= purchase - qdel(purchase) + addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/controller/subsystem/blackmarket,fake_teleport), purchase, targetturf), 60 SECONDS) + // Get the current location of the uplink if it exists, then throws the item from space at the station from a random direction. if(SHIPPING_METHOD_LAUNCH) var/startSide = pick(GLOB.cardinals) var/turf/T = get_turf(purchase.uplink) var/pickedloc = spaceDebrisStartLoc(startSide, T.z) - var/atom/movable/item = purchase.entry.spawn_item(pickedloc) + var/atom/movable/item = purchase.entry.spawn_item(pickedloc, purchase) item.throw_at(purchase.uplink, 3, 3, spin = FALSE) - to_chat(recursive_loc_check(purchase.uplink.loc, /mob), span_notice("[purchase.uplink] flashes a message noting the order is being launched at the station from [dir2text(startSide)].")) + to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting the order is being launched at the station from [dir2text(startSide)].")) + qdel(purchase) - queued_purchases -= purchase + if(SHIPPING_METHOD_SUPPLYPOD) + var/obj/structure/closet/supplypod/back_to_station/pod = new() + purchase.entry.spawn_item(pod, purchase) + new /obj/effect/pod_landingzone(get_turf(purchase.uplink), pod) + + to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting the order is being launched at your location. Right here, right now!")) qdel(purchase) if(MC_TICK_CHECK) break /// Used to make a teleportation effect as do_teleport does not like moving items from nullspace. -/datum/controller/subsystem/blackmarket/proc/fake_teleport(atom/movable/item, turf/target) - item.forceMove(target) +/datum/controller/subsystem/blackmarket/proc/fake_teleport(datum/market_purchase/purchase, turf/target) + // Oopsie, whoopsie, the item is gone. So long, and thanks for all the money. + if(QDELETED(purchase)) + return + var/atom/movable/thing = purchase.entry.spawn_item(target, purchase) var/datum/effect_system/spark_spread/sparks = new sparks.set_up(5, 1, target) - sparks.attach(item) + sparks.attach(thing) sparks.start() + qdel(purchase) /// Used to add /datum/market_purchase to queued_purchases var. Returns TRUE when queued. -/datum/controller/subsystem/blackmarket/proc/queue_item(datum/market_purchase/P) - if(P.method == SHIPPING_METHOD_LTSRBT && !telepads.len) +/datum/controller/subsystem/blackmarket/proc/queue_item(datum/market_purchase/purchase) + if((purchase.method == SHIPPING_METHOD_LTSRBT && !telepads.len) || isnull(purchase.uplink)) + qdel(purchase) return FALSE - queued_purchases += P + queued_purchases += purchase return TRUE diff --git a/code/controllers/subsystem/ore_generation.dm b/code/controllers/subsystem/ore_generation.dm index ca8aa09d6111c..e36dd577794e1 100644 --- a/code/controllers/subsystem/ore_generation.dm +++ b/code/controllers/subsystem/ore_generation.dm @@ -20,27 +20,6 @@ SUBSYSTEM_DEF(ore_generation) var/list/ore_vent_minerals = list() /// A tracker of how many of each ore vent size we have in the game. Useful for tracking purposes. - var/list/ore_vent_sizes = list( - LARGE_VENT_TYPE = 0, - MEDIUM_VENT_TYPE = 0, - SMALL_VENT_TYPE = 0, - ) - /// Ores spawned by proximity to an ore vent. Useful for logging purposes. - var/list/post_ore_random = list( - "1" = 0, - "2" = 0, - "3" = 0, - "4" = 0, - "5" = 0, - ) - /// Ores spawned randomly on the map without proximity to an ore vent. Useful for logging purposes. - var/list/post_ore_manual = list( - "1" = 0, - "2" = 0, - "3" = 0, - "4" = 0, - "5" = 0, - ) /datum/controller/subsystem/ore_generation/Initialize() //Basically, we're going to round robin through the list of ore vents and assign a mineral to them until complete. @@ -56,8 +35,43 @@ SUBSYSTEM_DEF(ore_generation) else stallbreaker++ if(stallbreaker >= length(possible_vents)) - return SS_INIT_SUCCESS //We've done all we can here. + break //We've done all we can here. break inner loop continue + if(stallbreaker >= length(possible_vents)) + break //We've done all we can here. break outer loop + + /// Handles roundstart logging + logger.Log( + LOG_CATEGORY_CAVE_GENERATION, + "Ore Generation spawned the following ores based on vent proximity", + list( + "[ORE_WALL_FAR]" = GLOB.post_ore_random["[ORE_WALL_FAR]"], + "[ORE_WALL_LOW]" = GLOB.post_ore_random["[ORE_WALL_LOW]"], + "[ORE_WALL_MEDIUM]" = GLOB.post_ore_random["[ORE_WALL_MEDIUM]"], + "[ORE_WALL_HIGH]" = GLOB.post_ore_random["[ORE_WALL_HIGH]"], + "[ORE_WALL_VERY_HIGH]" = GLOB.post_ore_random["[ORE_WALL_VERY_HIGH]"], + ), + ) + logger.Log( + LOG_CATEGORY_CAVE_GENERATION, + "Ore Generation spawned the following ores randomly", + list( + "[ORE_WALL_FAR]" = GLOB.post_ore_manual["[ORE_WALL_FAR]"], + "[ORE_WALL_LOW]" = GLOB.post_ore_manual["[ORE_WALL_LOW]"], + "[ORE_WALL_MEDIUM]" = GLOB.post_ore_manual["[ORE_WALL_MEDIUM]"], + "[ORE_WALL_HIGH]" = GLOB.post_ore_manual["[ORE_WALL_HIGH]"], + "[ORE_WALL_VERY_HIGH]" = GLOB.post_ore_manual["[ORE_WALL_VERY_HIGH]"], + ), + ) + logger.Log( + LOG_CATEGORY_CAVE_GENERATION, + "Ore Generation spawned the following vent sizes", + list( + "large" = LAZYACCESS(GLOB.ore_vent_sizes, LARGE_VENT_TYPE), + "medium" = LAZYACCESS(GLOB.ore_vent_sizes, MEDIUM_VENT_TYPE), + "small" = LAZYACCESS(GLOB.ore_vent_sizes, SMALL_VENT_TYPE), + ), + ) return SS_INIT_SUCCESS /datum/controller/subsystem/ore_generation/fire(resumed) diff --git a/code/controllers/subsystem/processing/instruments.dm b/code/controllers/subsystem/processing/instruments.dm index acee4480b946f..1cfbb144e5f8a 100644 --- a/code/controllers/subsystem/processing/instruments.dm +++ b/code/controllers/subsystem/processing/instruments.dm @@ -20,7 +20,10 @@ PROCESSING_SUBSYSTEM_DEF(instruments) var/static/current_instrument_channels = 0 /// Single cached list for synthesizer instrument ids, so you don't have to have a new list with every synthesizer. var/static/list/synthesizer_instrument_ids - var/static/list/note_sustain_modes = list("Linear" = SUSTAIN_LINEAR, "Exponential" = SUSTAIN_EXPONENTIAL) + var/static/list/note_sustain_modes = list( + SUSTAIN_LINEAR, + SUSTAIN_EXPONENTIAL, + ) /datum/controller/subsystem/processing/instruments/Initialize() initialize_instrument_data() diff --git a/code/datums/ai/generic/generic_behaviors.dm b/code/datums/ai/generic/generic_behaviors.dm index 43e37f66e8c8a..b70375ef3933f 100644 --- a/code/datums/ai/generic/generic_behaviors.dm +++ b/code/datums/ai/generic/generic_behaviors.dm @@ -333,7 +333,7 @@ //just in case- it won't do anything if the instrument isn't playing song.stop_playing() - song.ParseSong(song_lines) + song.ParseSong(new_song = song_lines) song.repeat = 10 song.volume = song.max_volume - 10 finish_action(controller, TRUE) diff --git a/code/datums/components/attached_sticker.dm b/code/datums/components/attached_sticker.dm deleted file mode 100644 index 49541a6b37c47..0000000000000 --- a/code/datums/components/attached_sticker.dm +++ /dev/null @@ -1,78 +0,0 @@ -// The attached sticker - -/datum/component/attached_sticker - dupe_mode = COMPONENT_DUPE_ALLOWED - ///The overlay we apply to things we stick to - var/mutable_appearance/sticker_overlay - ///The turf our COMSIG_TURF_EXPOSE is registered to, so we can unregister it later. - var/turf/signal_turf - ///Our physical sticker to drop - var/obj/item/sticker - ///Can we be washed off? - var/washable = TRUE - -/datum/component/attached_sticker/Initialize(px, py, obj/stick, mob/living/user, cleanable=TRUE) - if(!isatom(parent)) - return COMPONENT_INCOMPATIBLE - washable = cleanable - var/atom/atom_parent = parent - sticker = stick - sticker_overlay = mutable_appearance(stick.icon, stick.icon_state , layer = atom_parent.layer + 1, appearance_flags = RESET_COLOR | PIXEL_SCALE) - sticker_overlay.pixel_x = px - sticker_overlay.pixel_y = py - atom_parent.add_overlay(sticker_overlay) - if(isliving(parent) && user) - var/mob/living/victim = parent - if(victim.client) - user.log_message("stuck [sticker] to [key_name(victim)]", LOG_ATTACK) - victim.log_message("had [sticker] stuck to them by [key_name(user)]", LOG_ATTACK) - else if(isturf(parent) && (sticker.resistance_flags & FLAMMABLE)) - //register signals on the users turf instead because we can assume they are on flooring sticking it to a wall so it should burn (otherwise it would fruitlessly check wall temperature) - signal_turf = (user && isclosedturf(parent)) ? get_turf(user) : parent - RegisterSignal(signal_turf, COMSIG_TURF_EXPOSE, PROC_REF(on_turf_expose)) - sticker.moveToNullspace() - RegisterSignal(sticker, COMSIG_QDELETING, PROC_REF(peel)) - -/datum/component/attached_sticker/Destroy() - var/atom/as_atom = parent - as_atom.cut_overlay(sticker_overlay) - sticker_overlay = null - if(sticker) - QDEL_NULL(sticker) - return ..() - -///Move sticker item from nullspace, delete this component, cut overlay -/datum/component/attached_sticker/proc/peel(atom/source) - SIGNAL_HANDLER - if(!QDELETED(sticker)) - var/atom/as_atom = parent - sticker.forceMove(isturf(as_atom) ? as_atom : as_atom.drop_location()) - sticker.pixel_y = rand(-4,1) - sticker.pixel_x = rand(-3,3) - sticker = null - if(!QDELETED(src)) - qdel(src) - -/datum/component/attached_sticker/RegisterWithParent() - if(sticker.resistance_flags & FLAMMABLE) - RegisterSignal(parent, COMSIG_LIVING_IGNITED, PROC_REF(peel)) - if(washable) - RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(peel)) - RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(peel)) - ADD_TRAIT(parent, TRAIT_STICKERED, REF(sticker)) - -/datum/component/attached_sticker/UnregisterFromParent() - UnregisterSignal(parent, list(COMSIG_LIVING_IGNITED, COMSIG_QDELETING)) - if(signal_turf) - UnregisterSignal(signal_turf, COMSIG_TURF_EXPOSE) - signal_turf = null - if(washable) - UnregisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT) - REMOVE_TRAIT(parent, TRAIT_STICKERED, REF(sticker)) - -///Signal handler for COMSIG_TURF_EXPOSE, deletes this sticker if the temperature is above 100C and it is flammable -/datum/component/attached_sticker/proc/on_turf_expose(datum/source, datum/gas_mixture/air, exposed_temperature) - SIGNAL_HANDLER - if(exposed_temperature <= FIRE_MINIMUM_TEMPERATURE_TO_EXIST) - return - peel() diff --git a/code/datums/components/crafting/entertainment.dm b/code/datums/components/crafting/entertainment.dm index 6de554791a25a..370a98a7bfb52 100644 --- a/code/datums/components/crafting/entertainment.dm +++ b/code/datums/components/crafting/entertainment.dm @@ -1,3 +1,22 @@ +/datum/crafting_recipe/moffers + name = "Moffers" + result = /obj/item/clothing/shoes/clown_shoes/moffers + time = 6 SECONDS //opportunity to rethink your life + reqs = list( + /obj/item/stack/sheet/animalhide/mothroach = 2, + /obj/item/clothing/shoes/clown_shoes = 1, + ) + parts = list(/obj/item/clothing/shoes/clown_shoes = 1) + blacklist = list( + /obj/item/clothing/shoes/clown_shoes/combat, + /obj/item/clothing/shoes/clown_shoes/banana_shoes, + /obj/item/clothing/shoes/clown_shoes/banana_shoes/combat, + /obj/item/clothing/shoes/clown_shoes/jester, + /obj/item/clothing/shoes/clown_shoes/meown_shoes, + /obj/item/clothing/shoes/clown_shoes/moffers, + ) + category = CAT_ENTERTAINMENT + /datum/crafting_recipe/mothplush name = "Moth Plushie" result = /obj/item/toy/plush/moth diff --git a/code/datums/components/crank_recharge.dm b/code/datums/components/crank_recharge.dm index 5c3c7aad24ed6..10449a337e140 100644 --- a/code/datums/components/crank_recharge.dm +++ b/code/datums/components/crank_recharge.dm @@ -2,6 +2,8 @@ /datum/component/crank_recharge /// Our cell to charge var/obj/item/stock_parts/cell/charging_cell + /// Whether we spin our gun to reload (and therefore need the relevant trait) + var/spin_to_win = FALSE /// How much charge we give our cell on each crank var/charge_amount /// How long is the cooldown time between each charge @@ -14,13 +16,14 @@ var/is_charging = FALSE COOLDOWN_DECLARE(charge_sound_cooldown) -/datum/component/crank_recharge/Initialize(charging_cell, charge_amount = 500, cooldown_time = 2 SECONDS, charge_sound = 'sound/weapons/laser_crank.ogg', charge_sound_cooldown_time = 1.8 SECONDS) +/datum/component/crank_recharge/Initialize(charging_cell, spin_to_win = FALSE, charge_amount = 500, cooldown_time = 2 SECONDS, charge_sound = 'sound/weapons/laser_crank.ogg', charge_sound_cooldown_time = 1.8 SECONDS) . = ..() if(!isitem(parent)) return COMPONENT_INCOMPATIBLE if(isnull(charging_cell) || !istype(charging_cell, /obj/item/stock_parts/cell)) return COMPONENT_INCOMPATIBLE src.charging_cell = charging_cell + src.spin_to_win = spin_to_win src.charge_amount = charge_amount src.cooldown_time = cooldown_time src.charge_sound = charge_sound @@ -45,6 +48,10 @@ return if(is_charging) return + if(spin_to_win && !HAS_TRAIT(user, TRAIT_GUNFLIP)) + source.balloon_alert(user, "need holster to spin!") + return + is_charging = TRUE if(COOLDOWN_FINISHED(src, charge_sound_cooldown)) COOLDOWN_START(src, charge_sound_cooldown, charge_sound_cooldown_time) @@ -56,4 +63,6 @@ charging_cell.give(charge_amount) source.update_appearance() is_charging = FALSE + if(spin_to_win) + source.SpinAnimation(4, 2) //What a badass source.balloon_alert(user, "charged") diff --git a/code/datums/components/sticker.dm b/code/datums/components/sticker.dm new file mode 100644 index 0000000000000..0562f6048077e --- /dev/null +++ b/code/datums/components/sticker.dm @@ -0,0 +1,111 @@ +/** + * ### Sticker component + * + * Component that draws supplied atom's icon over parent object with specified offset, + * icon centering is handled inside. + */ +/datum/component/sticker + dupe_mode = COMPONENT_DUPE_ALLOWED + + /// Either `turf` or `null`, used to connect to `COMSIG_TURF_EXPOSE` signal when parent is a turf. + var/turf/listening_turf + /// Refernce to a "stickered" atom. + var/atom/movable/our_sticker + /// Reference to the created overlay, used during component deletion. + var/mutable_appearance/sticker_overlay + +/datum/component/sticker/Initialize(atom/stickering_atom, mob/user, dir = NORTH, px = 0, py = 0) + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + + src.our_sticker = our_sticker + + if(isliving(parent) && !isnull(user)) + var/mob/living/victim = parent + + if(!isnull(victim.client)) + user.log_message("stuck [stickering_atom] to [key_name(victim)]", LOG_ATTACK) + victim.log_message("had [stickering_atom] stuck to them by [key_name(user)]", LOG_ATTACK) + + stick(stickering_atom, px, py) + register_turf_signals(dir) + +/datum/component/sticker/Destroy(force) + var/atom/parent_atom = parent + parent_atom.cut_overlay(sticker_overlay) + + unregister_turf_signals() + + REMOVE_TRAIT(parent, TRAIT_STICKERED, REF(src)) + + QDEL_NULL(our_sticker) + QDEL_NULL(sticker_overlay) + return ..() + +/datum/component/sticker/RegisterWithParent() + if(isliving(parent)) + RegisterSignal(parent, COMSIG_LIVING_IGNITED, PROC_REF(on_ignite)) + RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_clean)) + +/datum/component/sticker/UnregisterFromParent() + if(isliving(parent)) + UnregisterSignal(parent, COMSIG_LIVING_IGNITED) + UnregisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT) + +/// Subscribes to `COMSIG_TURF_EXPOSE` if parent atom is a turf. If turf is closed - subscribes to signal +/datum/component/sticker/proc/register_turf_signals(dir) + if(!isturf(parent)) + return + + listening_turf = isclosedturf(parent) ? get_step(parent, dir) : parent + RegisterSignal(listening_turf, COMSIG_TURF_EXPOSE, PROC_REF(on_turf_expose)) + +/// Unsubscribes from `COMSIG_TURF_EXPOSE` if `listening_turf` is not `null`. +/datum/component/sticker/proc/unregister_turf_signals() + if(isnull(listening_turf)) + return + + UnregisterSignal(listening_turf, COMSIG_TURF_EXPOSE) + +/// Handles overlay creation from supplied atom, adds created icon to the parent object, moves source atom to the nullspace. +/datum/component/sticker/proc/stick(atom/movable/stickering_atom, px, py) + our_sticker = stickering_atom + our_sticker.moveToNullspace() + + var/atom/parent_atom = parent + + sticker_overlay = mutable_appearance(icon = our_sticker.icon, icon_state = our_sticker.icon_state, layer = parent_atom.layer + 0.01, appearance_flags = RESET_COLOR) + sticker_overlay.pixel_w = px - world.icon_size / 2 + sticker_overlay.pixel_z = py - world.icon_size / 2 + + parent_atom.add_overlay(sticker_overlay) + + ADD_TRAIT(parent, TRAIT_STICKERED, REF(src)) + +/// Moves stickered atom from the nullspace, deletes component. +/datum/component/sticker/proc/peel() + var/atom/parent_atom = parent + var/turf/drop_location = isnull(listening_turf) ? parent_atom.drop_location() : listening_turf + + our_sticker.forceMove(drop_location) + our_sticker = null + + qdel(src) + +/datum/component/sticker/proc/on_ignite(datum/source) + SIGNAL_HANDLER + + qdel(src) + +/datum/component/sticker/proc/on_clean(datum/source, clean_types) + SIGNAL_HANDLER + + peel() + + return COMPONENT_CLEANED + +/datum/component/sticker/proc/on_turf_expose(datum/source, datum/gas_mixture/air, exposed_temperature) + SIGNAL_HANDLER + + if(exposed_temperature >= FIRE_MINIMUM_TEMPERATURE_TO_EXIST) + qdel(src) diff --git a/code/controllers/subsystem/eigenstate.dm b/code/datums/eigenstate.dm similarity index 66% rename from code/controllers/subsystem/eigenstate.dm rename to code/datums/eigenstate.dm index f167fdf24d1ef..3bba746320997 100644 --- a/code/controllers/subsystem/eigenstate.dm +++ b/code/datums/eigenstate.dm @@ -1,7 +1,7 @@ -///Subsystem used to teleport people to a linked web of itterative entries. If one entry is deleted, the 2 around it will forge a link instead. -SUBSYSTEM_DEF(eigenstates) - name = "Eigenstates" - flags = SS_NO_INIT | SS_NO_FIRE +GLOBAL_DATUM_INIT(eigenstate_manager, /datum/eigenstate_manager, new) + +///A singleton used to teleport people to a linked web of itterative entries. If one entry is deleted, the 2 around it will forge a link instead. +/datum/eigenstate_manager ///The list of objects that something is linked to indexed by UID var/list/eigen_targets = list() ///UID to object reference @@ -12,7 +12,7 @@ SUBSYSTEM_DEF(eigenstates) var/spark_time = 0 ///Creates a new link of targets unique to their own id -/datum/controller/subsystem/eigenstates/proc/create_new_link(targets) +/datum/eigenstate_manager/proc/create_new_link(targets, subtle = TRUE) if(length(targets) <= 1) return FALSE for(var/atom/target as anything in targets) //Clear out any connected @@ -20,40 +20,44 @@ SUBSYSTEM_DEF(eigenstates) if(!already_linked) continue if(length(eigen_targets[already_linked]) > 1) //Eigenstates are notorious for having cliques! - target.visible_message("[target] fizzes, it's already linked to something else!") + if(!subtle) + target.visible_message("[target] fizzes, it's already linked to something else!") targets -= target continue - target.visible_message("[target] fizzes, collapsing it's unique wavefunction into the others!") //If we're in a eigenlink all on our own and are open to new friends + if(!subtle) + target.visible_message("[target] fizzes, collapsing it's unique wavefunction into the others!") //If we're in a eigenlink all on our own and are open to new friends remove_eigen_entry(target) //clearup for new stuff //Do we still have targets? if(!length(targets)) return FALSE var/atom/visible_atom = targets[1] //The object that'll handle the messages if(length(targets) == 1) - visible_atom.visible_message("[targets[1]] fizzes, there's nothing it can link to!") + if(!subtle) + visible_atom.visible_message("[targets[1]] fizzes, there's nothing it can link to!") return FALSE - eigen_targets["[id_counter]"] = list() //Add to the master list + var/subtle_keyword = subtle ? "subtle" : "" + eigen_targets["[id_counter][subtle_keyword]"] = list() //Add to the master list for(var/atom/target as anything in targets) - eigen_targets["[id_counter]"] += target - eigen_id[target] = "[id_counter]" + eigen_targets["[id_counter][subtle_keyword]"] += target + eigen_id[target] = "[id_counter][subtle_keyword]" RegisterSignal(target, COMSIG_CLOSET_INSERT, PROC_REF(use_eigenlinked_atom)) RegisterSignal(target, COMSIG_QDELETING, PROC_REF(remove_eigen_entry)) - RegisterSignal(target, COMSIG_ATOM_TOOL_ACT(TOOL_WELDER), PROC_REF(tool_interact)) + if(!subtle) + RegisterSignal(target, COMSIG_ATOM_TOOL_ACT(TOOL_WELDER), PROC_REF(tool_interact)) target.RegisterSignal(target, COMSIG_EIGENSTATE_ACTIVATE, TYPE_PROC_REF(/obj/structure/closet,bust_open)) ADD_TRAIT(target, TRAIT_BANNED_FROM_CARGO_SHUTTLE, REF(src)) - var/obj/item = target - if(item) - item.color = COLOR_PERIWINKLEE //Tint the locker slightly. - item.alpha = 200 - do_sparks(3, FALSE, item) + if(!subtle) + target.add_atom_colour(COLOR_PERIWINKLEE, FIXED_COLOUR_PRIORITY) //Tint the locker slightly. + target.alpha = 200 + do_sparks(3, FALSE, target) visible_atom.visible_message("The items shimmer and fizzle, turning a shade of violet blue.") id_counter++ return TRUE ///reverts everything back to start -/datum/controller/subsystem/eigenstates/Destroy() +/datum/eigenstate_manager/eigenstates/Destroy() for(var/index in 1 to id_counter) for(var/entry in eigen_targets["[index]"]) remove_eigen_entry(entry) @@ -63,12 +67,12 @@ SUBSYSTEM_DEF(eigenstates) return ..() ///removes an object reference from the master list -/datum/controller/subsystem/eigenstates/proc/remove_eigen_entry(atom/entry) +/datum/eigenstate_manager/proc/remove_eigen_entry(atom/entry) SIGNAL_HANDLER var/id = eigen_id[entry] eigen_targets[id] -= entry eigen_id -= entry - entry.color = COLOR_WHITE + entry.remove_atom_colour(FIXED_COLOUR_PRIORITY, COLOR_PERIWINKLEE) entry.alpha = 255 UnregisterSignal(entry, list( COMSIG_QDELETING, @@ -83,13 +87,14 @@ SUBSYSTEM_DEF(eigenstates) eigen_targets -= targets ///Finds the object within the master list, then sends the thing to the object's location -/datum/controller/subsystem/eigenstates/proc/use_eigenlinked_atom(atom/object_sent_from, atom/movable/thing_to_send) +/datum/eigenstate_manager/proc/use_eigenlinked_atom(atom/object_sent_from, atom/movable/thing_to_send) SIGNAL_HANDLER var/id = eigen_id[object_sent_from] if(!id) stack_trace("[object_sent_from] attempted to eigenlink to something that didn't have a valid id!") return FALSE + var/subtle = findtext(id, "subtle") var/list/items = eigen_targets[id] var/index = (items.Find(object_sent_from))+1 //index + 1 if(!index) @@ -104,19 +109,21 @@ SUBSYSTEM_DEF(eigenstates) if(check_teleport_valid(thing_to_send, eigen_target, TELEPORT_CHANNEL_EIGENSTATE)) thing_to_send.forceMove(get_turf(eigen_target)) else - object_sent_from.balloon_alert(thing_to_send, "nothing happens!") + if(!subtle) + object_sent_from.balloon_alert(thing_to_send, "nothing happens!") return FALSE //Create ONE set of sparks for ALL times in iteration - if(spark_time != world.time) + if(!subtle && spark_time != world.time) do_sparks(5, FALSE, eigen_target) do_sparks(5, FALSE, object_sent_from) - spark_time = world.time + spark_time = world.time //Calls a special proc for the atom if needed (closets use bust_open()) SEND_SIGNAL(eigen_target, COMSIG_EIGENSTATE_ACTIVATE) - return COMPONENT_CLOSET_INSERT_INTERRUPT + if(!subtle) + return COMPONENT_CLOSET_INSERT_INTERRUPT ///Prevents tool use on the item -/datum/controller/subsystem/eigenstates/proc/tool_interact(atom/source, mob/user, obj/item/item) +/datum/eigenstate_manager/proc/tool_interact(atom/source, mob/user, obj/item/item) SIGNAL_HANDLER to_chat(user, span_notice("The unstable nature of [source] makes it impossible to use [item] on [source.p_them()]!")) return ITEM_INTERACT_BLOCKING diff --git a/code/datums/elements/basic_body_temp_sensitive.dm b/code/datums/elements/basic_body_temp_sensitive.dm index 8e11ed92575ea..8bb1cae854631 100644 --- a/code/datums/elements/basic_body_temp_sensitive.dm +++ b/code/datums/elements/basic_body_temp_sensitive.dm @@ -47,14 +47,15 @@ if(basic_mob.bodytemperature < min_body_temp) basic_mob.adjust_health(cold_damage * seconds_per_tick) - switch(cold_damage) - if(1 to 5) - basic_mob.throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 1) - if(5 to 10) - basic_mob.throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 2) - if(10 to INFINITY) - basic_mob.throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 3) - gave_alert = TRUE + if(!basic_mob.has_status_effect(/datum/status_effect/inebriated)) + switch(cold_damage) + if(1 to 5) + basic_mob.throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 1) + if(5 to 10) + basic_mob.throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 2) + if(10 to INFINITY) + basic_mob.throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 3) + gave_alert = TRUE else if(basic_mob.bodytemperature > max_body_temp) basic_mob.adjust_health(heat_damage * seconds_per_tick) diff --git a/code/datums/elements/elevation.dm b/code/datums/elements/elevation.dm index ffa6de398b6e4..92fba97a09414 100644 --- a/code/datums/elements/elevation.dm +++ b/code/datums/elements/elevation.dm @@ -113,6 +113,8 @@ for(var/mob/living/living in target) ADD_TRAIT(living, TRAIT_ON_ELEVATED_SURFACE, REF(src)) RegisterSignal(living, COMSIG_LIVING_SET_BUCKLED, PROC_REF(on_set_buckled)) + RegisterSignal(living, SIGNAL_ADDTRAIT(TRAIT_IGNORE_ELEVATION), PROC_REF(on_ignore_elevation_add)) + RegisterSignal(living, SIGNAL_REMOVETRAIT(TRAIT_IGNORE_ELEVATION), PROC_REF(on_ignore_elevation_remove)) elevate_mob(living) /datum/element/elevation_core/Detach(datum/source) @@ -133,7 +135,7 @@ continue REMOVE_TRAIT(living, TRAIT_ON_ELEVATED_SURFACE, REF(src)) elevate_mob(living, -pixel_shift) - UnregisterSignal(living, COMSIG_LIVING_SET_BUCKLED) + UnregisterSignal(living, list(COMSIG_LIVING_SET_BUCKLED, SIGNAL_ADDTRAIT(TRAIT_IGNORE_ELEVATION), SIGNAL_REMOVETRAIT(TRAIT_IGNORE_ELEVATION))) return ..() /datum/element/elevation_core/proc/on_entered(turf/source, atom/movable/entered, atom/old_loc) @@ -143,6 +145,8 @@ var/elevate_time = isturf(old_loc) && source.Adjacent(old_loc) ? ELEVATE_TIME : 0 elevate_mob(entered, elevate_time = elevate_time) RegisterSignal(entered, COMSIG_LIVING_SET_BUCKLED, PROC_REF(on_set_buckled)) + RegisterSignal(entered, SIGNAL_ADDTRAIT(TRAIT_IGNORE_ELEVATION), PROC_REF(on_ignore_elevation_add)) + RegisterSignal(entered, SIGNAL_REMOVETRAIT(TRAIT_IGNORE_ELEVATION), PROC_REF(on_ignore_elevation_remove)) /datum/element/elevation_core/proc/on_initialized_on(turf/source, atom/movable/spawned) SIGNAL_HANDLER @@ -152,15 +156,17 @@ /datum/element/elevation_core/proc/on_exited(turf/source, atom/movable/gone) SIGNAL_HANDLER if((isnull(gone.loc) || !HAS_TRAIT_FROM(gone.loc, TRAIT_ELEVATED_TURF, REF(src))) && isliving(gone)) - // Always unregister the signal, we're still leaving even if already shifted down. - UnregisterSignal(gone, COMSIG_LIVING_SET_BUCKLED) + // Always unregister the signals, we're still leaving even if not affected by elevation. + UnregisterSignal(gone, list(COMSIG_LIVING_SET_BUCKLED, SIGNAL_ADDTRAIT(TRAIT_IGNORE_ELEVATION), SIGNAL_REMOVETRAIT(TRAIT_IGNORE_ELEVATION))) if(!HAS_TRAIT_FROM(gone, TRAIT_ON_ELEVATED_SURFACE, REF(src))) return REMOVE_TRAIT(gone, TRAIT_ON_ELEVATED_SURFACE, REF(src)) var/elevate_time = isturf(gone.loc) && source.Adjacent(gone.loc) ? ELEVATE_TIME : 0 elevate_mob(gone, -pixel_shift, elevate_time) -/datum/element/elevation_core/proc/elevate_mob(mob/living/target, z_shift = pixel_shift, elevate_time = ELEVATE_TIME) +/datum/element/elevation_core/proc/elevate_mob(mob/living/target, z_shift = pixel_shift, elevate_time = ELEVATE_TIME, force = FALSE) + if(HAS_TRAIT(target, TRAIT_IGNORE_ELEVATION) && !force) + return var/buckled_to_vehicle = FALSE if(target.buckled) if(isvehicle(target.buckled)) @@ -181,6 +187,8 @@ */ /datum/element/elevation_core/proc/on_set_buckled(mob/living/source, atom/movable/new_buckled) SIGNAL_HANDLER + if(HAS_TRAIT(source, TRAIT_IGNORE_ELEVATION)) + return if(source.buckled) if(isvehicle(source.buckled)) animate(source.buckled, pixel_z = -pixel_shift, time = ELEVATE_TIME, flags = ANIMATION_RELATIVE|ANIMATION_PARALLEL) @@ -193,6 +201,14 @@ else if(!isliving(new_buckled)) animate(source, pixel_z = -pixel_shift, time = ELEVATE_TIME, flags = ANIMATION_RELATIVE|ANIMATION_PARALLEL) +/datum/element/elevation_core/proc/on_ignore_elevation_add(mob/living/source, trait) + SIGNAL_HANDLER + elevate_mob(source, -pixel_shift, force = TRUE) + +/datum/element/elevation_core/proc/on_ignore_elevation_remove(mob/living/source, trait) + SIGNAL_HANDLER + elevate_mob(source, pixel_shift) + /datum/element/elevation_core/proc/on_reset_elevation(turf/source, list/current_values) SIGNAL_HANDLER current_values[ELEVATION_CURRENT_PIXEL_SHIFT] = pixel_shift diff --git a/code/datums/elements/sticker.dm b/code/datums/elements/sticker.dm deleted file mode 100644 index 3cc8e977daf20..0000000000000 --- a/code/datums/elements/sticker.dm +++ /dev/null @@ -1,53 +0,0 @@ -#define MAX_ALLOWED_STICKERS 12 - -/datum/element/sticker - ///The typepath for our attached sticker component - var/stick_type = /datum/component/attached_sticker - ///If TRUE, our attached_sticker can be washed off - var/washable = TRUE - -/datum/element/sticker/Attach(datum/target, sticker_type, cleanable=TRUE) - . = ..() - if(!isitem(target)) - return ELEMENT_INCOMPATIBLE - RegisterSignal(target, COMSIG_ITEM_AFTERATTACK, PROC_REF(on_afterattack)) - RegisterSignal(target, COMSIG_MOVABLE_IMPACT, PROC_REF(on_throw_impact)) - if(sticker_type) - stick_type = sticker_type - washable = cleanable - -/datum/element/sticker/Detach(datum/source) - . = ..() - UnregisterSignal(source, list(COMSIG_ITEM_AFTERATTACK, COMSIG_MOVABLE_IMPACT)) - -/datum/element/sticker/proc/on_afterattack(obj/item/source, atom/target, mob/living/user, prox, params) - SIGNAL_HANDLER - if(!prox) - return - if(!isatom(target)) - return - var/list/parameters = params2list(params) - if(!LAZYACCESS(parameters, ICON_X) || !LAZYACCESS(parameters, ICON_Y)) - return - var/divided_size = world.icon_size / 2 - var/px = text2num(LAZYACCESS(parameters, ICON_X)) - divided_size - var/py = text2num(LAZYACCESS(parameters, ICON_Y)) - divided_size - - user.do_attack_animation(target) - if(do_stick(source, target, user, px, py)) - target.balloon_alert_to_viewers("sticker sticked") - -///Add our stick_type to the target with px and py as pixel x and pixel y respectively -/datum/element/sticker/proc/do_stick(obj/item/source, atom/target, mob/living/user, px, py) - if(COUNT_TRAIT_SOURCES(target, TRAIT_STICKERED) >= MAX_ALLOWED_STICKERS) - source.balloon_alert_to_viewers("sticker won't stick!") - return FALSE - target.AddComponent(stick_type, px, py, source, user, washable) - return TRUE - -/datum/element/sticker/proc/on_throw_impact(obj/item/source, atom/hit_atom, datum/thrownthing/throwingdatum) - SIGNAL_HANDLER - if(prob(50) && do_stick(source, hit_atom, null, rand(-7,7), rand(-7,7))) - hit_atom.balloon_alert_to_viewers("sticker landed on sticky side!") - -#undef MAX_ALLOWED_STICKERS diff --git a/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm b/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm index 1f4c8cb997380..3134666eb0370 100644 --- a/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm +++ b/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm @@ -705,6 +705,15 @@ name = "Well-Worn Shirt (Worn)" icon_file = 'icons/mob/clothing/suits/costume.dmi' +/datum/greyscale_config/wellworn_shirt_skub + name = "Well-Worn Shirt (Skub)" + icon_file = 'icons/obj/clothing/suits/costume.dmi' + json_config = 'code/datums/greyscale/json_configs/wellworn_shirt_skub.json' + +/datum/greyscale_config/wellworn_shirt_skub/worn + name = "Well-Worn Shirt (Skub)(Worn)" + icon_file = 'icons/mob/clothing/suits/costume.dmi' + /datum/greyscale_config/wellworn_shirt_graphic name = "Well-Worn Shirt (Graphic)" icon_file = 'icons/obj/clothing/suits/costume.dmi' diff --git a/code/datums/greyscale/json_configs/wellworn_shirt_skub.json b/code/datums/greyscale/json_configs/wellworn_shirt_skub.json new file mode 100644 index 0000000000000..6f6ed3fb1f1dc --- /dev/null +++ b/code/datums/greyscale/json_configs/wellworn_shirt_skub.json @@ -0,0 +1,28 @@ +{ + "wellworn_shirt_pro_skub": [ + { + "type": "icon_state", + "icon_state": "worn_out", + "blend_mode": "overlay", + "color_ids": [ 1 ] + }, + { + "type": "icon_state", + "icon_state": "skub_overlay", + "blend_mode": "overlay" + } + ], + "wellworn_shirt_anti_skub": [ + { + "type": "icon_state", + "icon_state": "worn_out", + "blend_mode": "overlay", + "color_ids": [ 1 ] + }, + { + "type": "icon_state", + "icon_state": "anti_skub_overlay", + "blend_mode": "overlay" + } + ] +} diff --git a/code/datums/station_traits/neutral_traits.dm b/code/datums/station_traits/neutral_traits.dm index 4cb15c6b07e76..7545b2fb39582 100644 --- a/code/datums/station_traits/neutral_traits.dm +++ b/code/datums/station_traits/neutral_traits.dm @@ -104,7 +104,7 @@ /datum/station_trait/glitched_pdas name = "PDA glitch" trait_type = STATION_TRAIT_NEUTRAL - weight = 10 + weight = 5 show_in_report = TRUE cost = STATION_TRAIT_COST_MINIMAL report_message = "Something seems to be wrong with the PDAs issued to you all this shift. Nothing too bad though." @@ -283,7 +283,7 @@ /datum/station_trait/scarves name = "Scarves" trait_type = STATION_TRAIT_NEUTRAL - weight = 10 + weight = 5 cost = STATION_TRAIT_COST_MINIMAL show_in_report = TRUE var/list/scarves @@ -317,7 +317,7 @@ name = "Wallets!" trait_type = STATION_TRAIT_NEUTRAL show_in_report = TRUE - weight = 10 + weight = 5 cost = STATION_TRAIT_COST_MINIMAL report_message = "It has become temporarily fashionable to use a wallet, so everyone on the station has been issued one." @@ -363,6 +363,41 @@ show_in_report = TRUE report_message = "There sure are a lot of trees out there." +/datum/station_trait/linked_closets + name = "Closet Anomaly" + trait_type = STATION_TRAIT_NEUTRAL + show_in_report = TRUE + weight = 1 + report_message = "We've reports of high amount of trace eigenstasium on your station. Ensure that your closets are working correctly." + +/datum/station_trait/linked_closets/on_round_start() + . = ..() + var/list/roundstart_non_secure_closets = GLOB.roundstart_station_closets.Copy() + for(var/obj/structure/closet/closet in roundstart_non_secure_closets) + if(closet.secure) + roundstart_non_secure_closets -= closet + + /** + * The number of links to perform. + * Combined with 50/50 the probability of the link being triangular, the boundaries of any given + * on-station, non-secure closet being linked are as high as 1 in 7/8 and as low as 1 in 16-17, + * nearing an a mean of 1 in 9 to 11/12 the more repetitions are done. + * + * There are more than 220 roundstart closets on meta, around 150 of which aren't secure, + * so, about 13 to 17 closets will be affected by this most of the times. + */ + var/number_of_links = round(length(roundstart_non_secure_closets) * (rand(350, 450)*0.0001), 1) + for(var/repetition in 1 to number_of_links) + var/closets_left = length(roundstart_non_secure_closets) + if(closets_left < 2) + return + var/list/targets = list() + for(var/how_many in 1 to min(closets_left, rand(2,3))) + targets += pick_n_take(roundstart_non_secure_closets) + if(closets_left == 1) //there's only one closet left. Let's not leave it alone. + targets += roundstart_non_secure_closets[1] + GLOB.eigenstate_manager.create_new_link(targets) + /datum/station_trait/triple_ai name = "AI Triumvirate" trait_type = STATION_TRAIT_NEUTRAL @@ -394,3 +429,135 @@ ai_datum.spawn_positions = 3 ai_datum.total_positions = 3 + +#define PRO_SKUB "pro-skub" +#define ANTI_SKUB "anti-skub" +#define SKUB_IDFC "i don't frikkin' care" +#define RANDOM_SKUB null //This means that if you forgot to opt in/against/out, there's a 50/50 chance to be pro or anti + +/// A trait that lets players choose whether they want pro-skub or anti-skub (or neither), and receive the appropriate equipment. +/datum/station_trait/skub + name = "The Great Skub Contention" + trait_type = STATION_TRAIT_NEUTRAL + show_in_report = FALSE + weight = 2 + sign_up_button = TRUE + /// List of people signed up to be either pro_skub or anti_skub + var/list/skubbers = list() + +/datum/station_trait/skub/New() + . = ..() + RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, PROC_REF(on_job_after_spawn)) + +/datum/station_trait/skub/setup_lobby_button(atom/movable/screen/lobby/button/sign_up/lobby_button) + RegisterSignal(lobby_button, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_lobby_button_update_overlays)) + lobby_button.desc = "Are you pro-skub or anti-skub? Click to cycle through pro-skub, anti-skub, random and neutral." + return ..() + +/// Let late-joiners jump on this gimmick too. +/datum/station_trait/skub/can_display_lobby_button(client/player) + return sign_up_button + +/// We don't destroy buttons on round start for those who are still in the lobby. +/datum/station_trait/skub/on_round_start() + return + +/datum/station_trait/skub/on_lobby_button_update_icon(atom/movable/screen/lobby/button/sign_up/lobby_button, location, control, params, mob/dead/new_player/user) + var/mob/player = lobby_button.get_mob() + var/skub_stance = skubbers[player.ckey] + switch(skub_stance) + if(PRO_SKUB) + lobby_button.base_icon_state = "signup_on" + if(ANTI_SKUB) + lobby_button.base_icon_state = "signup" + else + lobby_button.base_icon_state = "signup_neutral" + +/datum/station_trait/skub/on_lobby_button_click(atom/movable/screen/lobby/button/sign_up/lobby_button, updates) + var/mob/player = lobby_button.get_mob() + var/skub_stance = skubbers[player.ckey] + switch(skub_stance) + if(PRO_SKUB) + skubbers[player.ckey] = ANTI_SKUB + lobby_button.balloon_alert(player, "anti-skub") + if(ANTI_SKUB) + skubbers[player.ckey] = SKUB_IDFC + lobby_button.balloon_alert(player, "don't care") + if(SKUB_IDFC) + skubbers[player.ckey] = RANDOM_SKUB + lobby_button.balloon_alert(player, "on the best side") + if(RANDOM_SKUB) + skubbers[player.ckey] = PRO_SKUB + lobby_button.balloon_alert(player, "pro-skub") + +/datum/station_trait/skub/proc/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays) + SIGNAL_HANDLER + var/mob/player = lobby_button.get_mob() + var/skub_stance = skubbers[player.ckey] + switch(skub_stance) + if(PRO_SKUB) + overlays += "pro_skub" + if(ANTI_SKUB) + overlays += "anti_skub" + if(SKUB_IDFC) + overlays += "neutral_skub" + if(RANDOM_SKUB) + overlays += "random_skub" + +/datum/station_trait/skub/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned, client/player_client) + SIGNAL_HANDLER + + var/skub_stance = skubbers[player_client.ckey] + if(skub_stance == SKUB_IDFC) + return + + if((skub_stance == RANDOM_SKUB && prob(50)) || skub_stance == PRO_SKUB) + var/obj/item/storage/box/skub/boxie = new(spawned.loc) + spawned.equip_to_slot_if_possible(boxie, ITEM_SLOT_BACKPACK, indirect_action = TRUE) + if(ishuman(spawned)) + var/obj/item/clothing/suit/costume/wellworn_shirt/skub/shirt = new(spawned.loc) + if(!spawned.equip_to_slot_if_possible(shirt, ITEM_SLOT_OCLOTHING, indirect_action = TRUE)) + shirt.forceMove(boxie) + return + + var/obj/item/storage/box/stickers/anti_skub/boxie = new(spawned.loc) + spawned.equip_to_slot_if_possible(boxie, ITEM_SLOT_BACKPACK, indirect_action = TRUE) + if(!ishuman(spawned)) + return + var/obj/item/clothing/suit/costume/wellworn_shirt/skub/anti/shirt = new(spawned.loc) + if(!spawned.equip_to_slot_if_possible(shirt, ITEM_SLOT_OCLOTHING, indirect_action = TRUE)) + shirt.forceMove(boxie) + +/// A box containing a skub, for easier carry because skub is a bulky item. +/obj/item/storage/box/skub + name = "skub box" + desc = "A box to store your skub and pro-skub shirt in. A label on the back reads: \"Skubtide, Stationwide\"." + icon_state = "hugbox" + illustration = "skub" + +/obj/item/storage/box/skub/Initialize(mapload) + . = ..() + atom_storage.exception_hold = typecacheof(list(/obj/item/skub, /obj/item/clothing/suit/costume/wellworn_shirt/skub)) + +/obj/item/storage/box/skub/PopulateContents() + new /obj/item/skub(src) + new /obj/item/sticker/skub(src) + new /obj/item/sticker/skub(src) + +/obj/item/storage/box/stickers/anti_skub + name = "anti-skub stickers box" + desc = "The enemy may have been given a skub and a shirt, but I've more stickers! Plus the box can hold my anti-skub shirt." + +/obj/item/storage/box/stickers/anti_skub/Initialize(mapload) + . = ..() + atom_storage.exception_hold = typecacheof(list(/obj/item/clothing/suit/costume/wellworn_shirt/skub)) + +/obj/item/storage/box/stickers/anti_skub/PopulateContents() + for(var/i in 1 to 4) + new /obj/item/sticker/anti_skub(src) + +#undef PRO_SKUB +#undef ANTI_SKUB +#undef SKUB_IDFC +#undef RANDOM_SKUB + diff --git a/code/datums/status_effects/_status_effect_helpers.dm b/code/datums/status_effects/_status_effect_helpers.dm index 0ee9522006106..f887afd91428e 100644 --- a/code/datums/status_effects/_status_effect_helpers.dm +++ b/code/datums/status_effects/_status_effect_helpers.dm @@ -56,7 +56,7 @@ . = FALSE for(var/datum/status_effect/existing_effect as anything in status_effects) - if(existing_effect.id == initial(removed_effect.id) && existing_effect.before_remove(arguments)) + if(existing_effect.id == initial(removed_effect.id) && existing_effect.before_remove(arglist(arguments))) qdel(existing_effect) . = TRUE @@ -84,6 +84,17 @@ return null +///Gets every status effect of an ID and returns all of them in a list, rather than the individual 'has_status_effect' +/mob/living/proc/get_all_status_effect_of_id(datum/status_effect/checked_effect) + RETURN_TYPE(/list/datum/status_effect) + + var/list/all_effects_of_type = list() + for(var/datum/status_effect/present_effect as anything in status_effects) + if(present_effect.id == initial(checked_effect.id)) + all_effects_of_type += present_effect + + return all_effects_of_type + /** * Checks if this mob has a status effect that shares the passed effect's ID * and has the passed sources are in its list of sources (ONLY works for grouped efects!) diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 74eb198cfcec9..17963bbb40953 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -343,7 +343,7 @@ /datum/status_effect/crusher_mark id = "crusher_mark" duration = 300 //if you leave for 30 seconds you lose the mark, deal with it - status_type = STATUS_EFFECT_REPLACE + status_type = STATUS_EFFECT_MULTIPLE alert_type = null var/mutable_appearance/marked_underlay var/obj/item/kinetic_crusher/hammer_synced @@ -370,9 +370,9 @@ QDEL_NULL(marked_underlay) return ..() -/datum/status_effect/crusher_mark/be_replaced() - owner.underlays -= marked_underlay //if this is being called, we should have an owner at this point. - ..() +//we will only clear ourselves if the crusher is the one that owns us. +/datum/status_effect/crusher_mark/before_remove(obj/item/kinetic_crusher/attacking_hammer) + return (attacking_hammer == hammer_synced) /datum/status_effect/stacking/saw_bleed id = "saw_bleed" diff --git a/code/datums/status_effects/debuffs/fire_stacks.dm b/code/datums/status_effects/debuffs/fire_stacks.dm index 76d5f8ec896d1..5bf8269bbbfda 100644 --- a/code/datums/status_effects/debuffs/fire_stacks.dm +++ b/code/datums/status_effects/debuffs/fire_stacks.dm @@ -30,8 +30,7 @@ qdel(src) return if(isbasicmob(owner)) - var/mob/living/basic/basic_owner = owner - if(!(basic_owner.basic_mob_flags & FLAMMABLE_MOB)) + if(!check_basic_mob_immunity(owner)) qdel(src) return @@ -94,6 +93,10 @@ stacks = max(0, min(stack_limit, stacks + new_stacks)) cache_stacks() +/// Checks if the applicable basic mob is immune to the status effect we're trying to apply. Returns TRUE if it is, FALSE if it isn't. +/datum/status_effect/fire_handler/proc/check_basic_mob_immunity(mob/living/basic/basic_owner) + return (basic_owner.basic_mob_flags & FLAMMABLE_MOB) + /** * Refresher for mob's fire_stacks */ @@ -287,3 +290,6 @@ if(particle_effect) return particle_effect = new(owner, /particles/droplets) + +/datum/status_effect/fire_handler/wet_stacks/check_basic_mob_immunity(mob/living/basic/basic_owner) + return !(basic_owner.basic_mob_flags & IMMUNE_TO_GETTING_WET) diff --git a/code/game/atom/atom_tool_acts.dm b/code/game/atom/atom_tool_acts.dm index 22aef1a54ae0f..c8dfd36772b14 100644 --- a/code/game/atom/atom_tool_acts.dm +++ b/code/game/atom/atom_tool_acts.dm @@ -25,8 +25,8 @@ return early_sig_return var/interact_return = is_left_clicking \ - ? tool.interact_with_atom(src, user) \ - : tool.interact_with_atom_secondary(src, user) + ? tool.interact_with_atom(src, user, modifiers) \ + : tool.interact_with_atom_secondary(src, user, modifiers) if(interact_return) return interact_return @@ -82,7 +82,7 @@ * Return an ITEM_INTERACT_ flag in the event the interaction was handled, to cancel further interaction code. * Return NONE to allow default interaction / tool handling. */ -/obj/item/proc/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/proc/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) return NONE /** @@ -94,8 +94,8 @@ * Return an ITEM_INTERACT_ flag in the event the interaction was handled, to cancel further interaction code. * Return NONE to allow default interaction / tool handling. */ -/obj/item/proc/interact_with_atom_secondary(atom/interacting_with, mob/living/user) - return interact_with_atom(interacting_with, user) +/obj/item/proc/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) + return interact_with_atom(interacting_with, user, modifiers) /* * Tool-specific behavior procs. diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index a386dec8db2f5..50c75850647f8 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1006,6 +1006,11 @@ GLOBAL_LIST_EMPTY(possible_items) var/payout_bonus = 0 var/area/dropoff = null +/datum/objective/contract/is_valid_target(datum/mind/possible_target) + if(HAS_TRAIT(possible_target, TRAIT_HAS_BEEN_KIDNAPPED)) + return FALSE + return ..() + // Generate a random valid area on the station that the dropoff will happen. /datum/objective/contract/proc/generate_dropoff() var/found = FALSE diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 25dd047a69a9e..e20d53e4d315d 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -95,10 +95,19 @@ /obj/machinery/autolathe/proc/AfterMaterialInsert(container, obj/item/item_inserted, last_inserted_id, mats_consumed, amount_inserted, atom/context) SIGNAL_HANDLER - flick("autolathe_[item_inserted.has_material_type(/datum/material/glass) ? "r" : "o"]", src) - //we use initial(active_power_usage) because higher tier parts will have higher active usage but we have no benifit from it - directly_use_power(ROUND_UP((amount_inserted / (MAX_STACK_SIZE * SHEET_MATERIAL_AMOUNT)) * 0.01 * initial(active_power_usage))) + if(directly_use_power(ROUND_UP((amount_inserted / (MAX_STACK_SIZE * SHEET_MATERIAL_AMOUNT)) * 0.02 * initial(active_power_usage)))) + flick_overlay_view(mutable_appearance('icons/obj/machines/lathes.dmi', "autolathe_mat"), 1 SECONDS) + + var/datum/material/highest_mat_ref + var/highest_mat = 0 + for(var/datum/material/mat as anything in mats_consumed) + var/present_mat = mats_consumed[mat] + if(present_mat > highest_mat) + highest_mat = present_mat + highest_mat_ref = mat + + flick_overlay_view(material_insertion_animation(highest_mat_ref.greyscale_colors), 1 SECONDS) /obj/machinery/autolathe/ui_interact(mob/user, datum/tgui/ui) if(!is_operational) diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index d9f9bc5280b28..ccb21be468c3f 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -21,6 +21,7 @@ armor_type = /datum/armor/machinery_button idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 0.02 resistance_flags = LAVA_PROOF | FIRE_PROOF + interaction_flags_machine = parent_type::interaction_flags_machine | INTERACT_MACHINE_OPEN /obj/machinery/button/indestructible resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 257cce51ab84e..e3a26fb54b68b 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -185,7 +185,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0) /obj/machinery/camera/proc/on_saboteur(datum/source, disrupt_duration) SIGNAL_HANDLER - emp_act(EMP_LIGHT, reset_time = disrupt_duration) + //lasts twice as much so we don't have to constantly shoot cameras just to be S T E A L T H Y + emp_act(EMP_LIGHT, reset_time = disrupt_duration * 2) return COMSIG_SABOTEUR_SUCCESS /obj/machinery/camera/proc/post_emp_reset(thisemp, previous_network) diff --git a/code/game/machinery/computer/orders/order_items/mining/order_pka.dm b/code/game/machinery/computer/orders/order_items/mining/order_pka.dm index 251343e6f8e2c..f239e9f2a7eaf 100644 --- a/code/game/machinery/computer/orders/order_items/mining/order_pka.dm +++ b/code/game/machinery/computer/orders/order_items/mining/order_pka.dm @@ -40,3 +40,7 @@ /datum/orderable_item/accelerator/minebot_passthrough item_path = /obj/item/borg/upgrade/modkit/minebot_passthrough cost_per_order = 800 + +/datum/orderable_item/accelerator/friendly_fire + item_path = /obj/item/borg/upgrade/modkit/human_passthrough + cost_per_order = 750 diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 83702cac9cb66..af38c929fd359 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -239,7 +239,7 @@ var/obj/item/I = AM if(!density || (I.w_class < WEIGHT_CLASS_NORMAL && !LAZYLEN(I.GetAccess()))) return - if(check_access(I)) + if(requiresID() && check_access(I)) open() else do_animate("deny") diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index 809171af1efa7..f5f8327607af3 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -103,6 +103,8 @@ DEFINE_BITFIELD(turret_flags, list( var/datum/action/turret_toggle/toggle_action /// Mob that is remotely controlling the turret var/mob/remote_controller + /// While the cooldown is still going on, it cannot be re-enabled. + COOLDOWN_DECLARE(disabled_time) /datum/armor/machinery_porta_turret melee = 50 @@ -133,18 +135,32 @@ DEFINE_BITFIELD(turret_flags, list( if(!has_cover) INVOKE_ASYNC(src, PROC_REF(popUp)) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) + AddElement(/datum/element/hostile_machine) -/obj/machinery/porta_turret/proc/toggle_on(set_to) - var/current = on - if (!isnull(set_to)) - on = set_to - else - on = !on - if (current != on) - check_should_process() - if (!on) - popDown() +///Toggles the turret on or off depending on the value of the turn_on arg. +/obj/machinery/porta_turret/proc/toggle_on(turn_on = TRUE) + if(on == turn_on) + return + if(on && !COOLDOWN_FINISHED(src, disabled_time)) + return + on = turn_on + check_should_process() + if (!on) + popDown() + +///Prevents turned from being turned on for a duration, then restarts them after that if the second ard is true. +/obj/machinery/porta_turret/proc/set_disabled(duration = 6 SECONDS, will_restart = on) + COOLDOWN_START(src, disabled_time, duration) + if(will_restart) + addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), duration + 1) //the cooldown isn't over until the tick after its end. + toggle_on(FALSE) + +/obj/machinery/porta_turret/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + INVOKE_ASYNC(src, PROC_REF(set_disabled), disrupt_duration) + return COMSIG_SABOTEUR_SUCCESS /obj/machinery/porta_turret/proc/check_should_process() if (datum_flags & DF_ISPROCESSING) @@ -256,7 +272,7 @@ DEFINE_BITFIELD(turret_flags, list( switch(action) if("power") if(anchored) - toggle_on() + toggle_on(!on) return TRUE else to_chat(usr, span_warning("It has to be secured first!")) @@ -364,10 +380,8 @@ DEFINE_BITFIELD(turret_flags, list( audible_message(span_hear("[src] hums oddly...")) obj_flags |= EMAGGED controllock = TRUE - toggle_on(FALSE) //turns off the turret temporarily + set_disabled(6 SECONDS) update_appearance() - //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit - addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), 6 SECONDS) //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here return TRUE @@ -385,11 +399,9 @@ DEFINE_BITFIELD(turret_flags, list( if(prob(20)) turret_flags |= TURRET_FLAG_SHOOT_ALL // Shooting everyone is a pretty big deal, so it's least likely to get turned on - toggle_on(FALSE) + set_disabled(rand(6 SECONDS, 20 SECONDS)) remove_control() - addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), rand(60,600)) - /obj/machinery/porta_turret/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) . = ..() if(. && atom_integrity > 0) //damage received @@ -1186,17 +1198,14 @@ DEFINE_BITFIELD(turret_flags, list( installation = /obj/item/gun/energy/laser/bluetag team_color = "blue" -/obj/machinery/porta_turret/lasertag/bullet_act(obj/projectile/P) +/obj/machinery/porta_turret/lasertag/bullet_act(obj/projectile/projectile) . = ..() - if(on) - if(team_color == "blue") - if(istype(P, /obj/projectile/beam/lasertag/redtag)) - toggle_on(FALSE) - addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), 10 SECONDS) - else if(team_color == "red") - if(istype(P, /obj/projectile/beam/lasertag/bluetag)) - toggle_on(FALSE) - addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), 10 SECONDS) + if(!on) + return + if(team_color == "blue" && istype(projectile, /obj/projectile/beam/lasertag/redtag)) + set_disabled(10 SECONDS) + else if(team_color == "red" && istype(projectile, /obj/projectile/beam/lasertag/bluetag)) + set_disabled(10 SECONDS) #undef TURRET_STUN #undef TURRET_LETHAL diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index 4b23987b97914..f21bfc4788426 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -124,6 +124,22 @@ desc = "You know who to call." light_power = 2 +/obj/effect/decal/cleanable/greenglow/radioactive + name = "radioactive goo" + desc = "Holy crap, stop looking at this and move away immediately! It's radioactive!" + light_power = 5 + light_range = 3 + light_color = LIGHT_COLOR_NUCLEAR + +/obj/effect/decal/cleanable/greenglow/radioactive/Initialize(mapload, list/datum/disease/diseases) + . = ..() + AddComponent( + /datum/component/radioactive_emitter, \ + cooldown_time = 5 SECONDS, \ + range = 4, \ + threshold = RAD_MEDIUM_INSULATION, \ + ) + /obj/effect/decal/cleanable/cobweb name = "cobweb" desc = "Somebody should remove that." diff --git a/code/game/objects/effects/material_insert.dm b/code/game/objects/effects/material_insert.dm new file mode 100644 index 0000000000000..9ca86226b24b9 --- /dev/null +++ b/code/game/objects/effects/material_insert.dm @@ -0,0 +1,22 @@ +/** + * Creates a mutable appearance with the material color applied for its insertion animation into an autolathe or techfab + * Arguments + * + * * color - the material color that will be applied + */ +/proc/material_insertion_animation(color) + RETURN_TYPE(/mutable_appearance) + + var/static/list/mutable_appearance/apps = list() + + var/mutable_appearance/cached_app = apps[color] + if(isnull(cached_app)) + var/icon/modified_icon = icon('icons/obj/machines/research.dmi', "material_insertion") + + //assuming most of the icon is white we find what ratio to scale the intensity of each part roughly + var/list/rgb_list = rgb2num(color) + modified_icon.SetIntensity(rgb_list[1] / 255, rgb_list[2] / 255, rgb_list[3] / 255) + cached_app = mutable_appearance(modified_icon, "material_insertion") + + apps[color] = cached_app + return cached_app diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 433cf51062f3e..bf662fcc1b90a 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -120,6 +120,8 @@ slapcraft_recipes = list(/datum/crafting_recipe/improv_explosive)\ ) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) + /obj/item/radio/Destroy() remove_radio_all(src) //Just to be sure QDEL_NULL(wires) @@ -127,6 +129,12 @@ QDEL_NULL(keyslot) return ..() +/obj/item/radio/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(broadcasting) //no broadcasting but it can still be used to send radio messages. + set_broadcasting(FALSE) + return COMSIG_SABOTEUR_SUCCESS + /obj/item/radio/proc/set_frequency(new_frequency) SEND_SIGNAL(src, COMSIG_RADIO_NEW_FREQUENCY, args) remove_radio(src, frequency) diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/melee/baton.dm index b692b58bfcf2d..90baa8f986b38 100644 --- a/code/game/objects/items/melee/baton.dm +++ b/code/game/objects/items/melee/baton.dm @@ -454,6 +454,7 @@ else cell = new preload_cell_type(src) RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(convert)) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) update_appearance() /obj/item/melee/baton/security/get_cell() @@ -488,6 +489,14 @@ qdel(item) qdel(src) +/obj/item/melee/baton/security/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(!active) + return + toggle_light() + active = FALSE + update_appearance() + return COMSIG_SABOTEUR_SUCCESS /obj/item/melee/baton/security/Exited(atom/movable/mov_content) . = ..() if(mov_content == cell) diff --git a/code/game/objects/items/piggy_bank.dm b/code/game/objects/items/piggy_bank.dm index 6a9ee494a22ff..8d50e8a39e8fe 100644 --- a/code/game/objects/items/piggy_bank.dm +++ b/code/game/objects/items/piggy_bank.dm @@ -4,7 +4,7 @@ */ /obj/item/piggy_bank name = "piggy bank" - desc = "A pig-shaped money container made of porkelain, oink. Do not throw." //pun very intended. + desc = "A pig-shaped money container made of porkelain, oink. Do not throw." //pun very intended. icon = 'icons/obj/fluff/general.dmi' icon_state = "piggy_bank" max_integrity = 8 @@ -20,6 +20,8 @@ var/datum/callback/persistence_cb ///How much dosh can this piggy bank hold. var/maximum_value = PAYCHECK_COMMAND * 20 + ///A limit to much dosh can you put inside this piggy bank each round. If 0, there's no limit. Only applies to persistent piggies. + var/maximum_savings_per_shift = 0 ///How much dosh this piggy bank spawns with. var/initial_value = 0 @@ -46,9 +48,12 @@ persistence_cb = CALLBACK(src, PROC_REF(save_cash)) SSticker.OnRoundend(persistence_cb) - if(initial_value & initial_value + calculate_dosh_amount() <= maximum_value) + if(initial_value && initial_value + calculate_dosh_amount() <= maximum_value) new /obj/item/holochip(src, initial_value) + if(maximum_savings_per_shift) + maximum_value = calculate_dosh_amount() + maximum_savings_per_shift + /obj/item/piggy_bank/proc/save_cash() SSpersistence.save_piggy_bank(src) @@ -127,3 +132,17 @@ /obj/item/piggy_bank/museum/Initialize(mapload) . = ..() AddComponent(/datum/component/areabound) //do not steal. + +/obj/item/piggy_bank/vault + name = "vault piggy bank" + desc = "A pig-shaped money container made of porkelain, containing the station's emergency funds carried between shifts, oink. Do not throw." + persistence_id = "vault_piggy" + greyscale_colors = COLOR_LIGHT_ORANGE + maximum_value = PAYCHECK_COMMAND * 33 + initial_value = PAYCHECK_CREW //it takes about 66 shifts for it to hit its max value on its own. + maximum_savings_per_shift = PAYCHECK_COMMAND * 16 //and 2 if you actively use it. + +/obj/item/piggy_bank/vault/Initialize(mapload) + . = ..() + //one piggy bank should exist, preferibly inside the vault's safe. + REGISTER_REQUIRED_MAP_ITEM(1, 1) diff --git a/code/game/objects/items/stacks/bscrystal.dm b/code/game/objects/items/stacks/bscrystal.dm index 19b518157c8c2..75c35eabb1818 100644 --- a/code/game/objects/items/stacks/bscrystal.dm +++ b/code/game/objects/items/stacks/bscrystal.dm @@ -74,7 +74,7 @@ attack_verb_simple = list("bluespace polybash", "bluespace polybatter", "bluespace polybludgeon", "bluespace polythrash", "bluespace polysmash") novariants = TRUE grind_results = list(/datum/reagent/bluespace = 20) - point_value = 30 + point_value = 90 merge_type = /obj/item/stack/sheet/bluespace_crystal material_type = /datum/material/bluespace var/crystal_type = /obj/item/stack/ore/bluespace_crystal/refined diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 5a789d34350e2..d6bd65afe31e1 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -159,7 +159,7 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \ resistance_flags = ACID_PROOF merge_type = /obj/item/stack/sheet/rglass grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/iron = 10) - point_value = 4 + point_value = 12 matter_amount = 6 tableVariant = /obj/structure/table/reinforced/rglass @@ -197,7 +197,7 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \ material_flags = NONE merge_type = /obj/item/stack/sheet/plasmarglass grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/toxin/plasma = 10, /datum/reagent/iron = 10) - point_value = 23 + point_value = 69 matter_amount = 8 tableVariant = /obj/structure/table/reinforced/plasmarglass diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index 8eafe6d52e5ae..0620d82cd9bf4 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -99,7 +99,7 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \ sheettype = "diamond" mats_per_unit = list(/datum/material/diamond=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/carbon = 20) - point_value = 25 + point_value = 75 merge_type = /obj/item/stack/sheet/mineral/diamond material_type = /datum/material/diamond walltype = /turf/closed/wall/mineral/diamond @@ -124,7 +124,7 @@ GLOBAL_LIST_INIT(diamond_recipes, list ( \ sheettype = "uranium" mats_per_unit = list(/datum/material/uranium=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/uranium = 20) - point_value = 20 + point_value = 60 merge_type = /obj/item/stack/sheet/mineral/uranium material_type = /datum/material/uranium walltype = /turf/closed/wall/mineral/uranium @@ -157,7 +157,7 @@ GLOBAL_LIST_INIT(uranium_recipes, list ( \ max_integrity = 100 mats_per_unit = list(/datum/material/plasma=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/toxin/plasma = 20) - point_value = 20 + point_value = 60 merge_type = /obj/item/stack/sheet/mineral/plasma material_type = /datum/material/plasma walltype = /turf/closed/wall/mineral/plasma @@ -192,7 +192,7 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \ sheettype = "gold" mats_per_unit = list(/datum/material/gold=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/gold = 20) - point_value = 20 + point_value = 60 merge_type = /obj/item/stack/sheet/mineral/gold material_type = /datum/material/gold walltype = /turf/closed/wall/mineral/gold @@ -219,7 +219,7 @@ GLOBAL_LIST_INIT(gold_recipes, list ( \ sheettype = "silver" mats_per_unit = list(/datum/material/silver=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/silver = 20) - point_value = 20 + point_value = 60 merge_type = /obj/item/stack/sheet/mineral/silver material_type = /datum/material/silver tableVariant = /obj/structure/table/optable @@ -245,7 +245,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \ sheettype = "bananium" mats_per_unit = list(/datum/material/bananium=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/consumable/banana = 20) - point_value = 50 + point_value = 150 merge_type = /obj/item/stack/sheet/mineral/bananium material_type = /datum/material/bananium walltype = /turf/closed/wall/mineral/bananium @@ -276,7 +276,7 @@ GLOBAL_LIST_INIT(bananium_recipes, list ( \ throw_range = 3 sheettype = "titanium" mats_per_unit = list(/datum/material/titanium=SHEET_MATERIAL_AMOUNT) - point_value = 20 + point_value = 60 merge_type = /obj/item/stack/sheet/mineral/titanium material_type = /datum/material/titanium walltype = /turf/closed/wall/mineral/titanium @@ -308,7 +308,7 @@ GLOBAL_LIST_INIT(titanium_recipes, list ( \ throw_range = 3 sheettype = "plastitanium" mats_per_unit = list(/datum/material/alloy/plastitanium=SHEET_MATERIAL_AMOUNT) - point_value = 45 + point_value = 135 material_type = /datum/material/alloy/plastitanium merge_type = /obj/item/stack/sheet/mineral/plastitanium material_flags = NONE @@ -482,7 +482,7 @@ GLOBAL_LIST_INIT(metalhydrogen_recipes, list( singular_name = "metal hydrogen sheet" w_class = WEIGHT_CLASS_NORMAL resistance_flags = FIRE_PROOF | LAVA_PROOF | ACID_PROOF | INDESTRUCTIBLE - point_value = 100 + point_value = 300 mats_per_unit = list(/datum/material/metalhydrogen = SHEET_MATERIAL_AMOUNT) material_type = /datum/material/metalhydrogen merge_type = /obj/item/stack/sheet/mineral/metal_hydrogen @@ -497,7 +497,7 @@ GLOBAL_LIST_INIT(metalhydrogen_recipes, list( inhand_icon_state = "sheet-zaukerite" singular_name = "zaukerite crystal" w_class = WEIGHT_CLASS_NORMAL - point_value = 120 + point_value = 360 mats_per_unit = list(/datum/material/zaukerite = SHEET_MATERIAL_AMOUNT) merge_type = /obj/item/stack/sheet/mineral/zaukerite material_type = /datum/material/zaukerite diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 3b32e6b1d9356..bad21b87ac306 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -150,7 +150,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \ resistance_flags = FIRE_PROOF merge_type = /obj/item/stack/sheet/iron grind_results = list(/datum/reagent/iron = 20) - point_value = 2 + point_value = 6 tableVariant = /obj/structure/table material_type = /datum/material/iron matter_amount = 4 @@ -277,7 +277,7 @@ GLOBAL_LIST_INIT(plasteel_recipes, list ( \ resistance_flags = FIRE_PROOF merge_type = /obj/item/stack/sheet/plasteel grind_results = list(/datum/reagent/iron = 20, /datum/reagent/toxin/plasma = 20) - point_value = 23 + point_value = 69 tableVariant = /obj/structure/table/reinforced material_flags = NONE matter_amount = 12 diff --git a/code/game/objects/items/sticker.dm b/code/game/objects/items/sticker.dm deleted file mode 100644 index 459c8d211e4d9..0000000000000 --- a/code/game/objects/items/sticker.dm +++ /dev/null @@ -1,131 +0,0 @@ -/// parent type for all other stickers. do not spawn directly -/obj/item/sticker - name = "sticker" - desc = "A sticker with some strong adhesive on the back, sticks to stuff!" - item_flags = NOBLUDGEON | XENOMORPH_HOLDABLE //funny - resistance_flags = FLAMMABLE - icon = 'icons/obj/toys/stickers.dmi' - w_class = WEIGHT_CLASS_TINY - throw_range = 3 - vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_PLANE | VIS_INHERIT_LAYER - ///If not null, pick an icon_state from this list - var/icon_states - /// If the sticker should be disincluded from normal sticker boxes. - var/contraband = FALSE - -/obj/item/sticker/Initialize(mapload) - . = ..() - if(icon_states) - icon_state = pick(icon_states) - pixel_y = rand(-3,3) - pixel_x = rand(-3,3) - AddElement(/datum/element/sticker) - -/obj/item/sticker/smile - name = "smiley sticker" - icon_state = "smile" - -/obj/item/sticker/frown - name = "frowny sticker" - icon_state = "frown" - -/obj/item/sticker/left_arrow - name = "left arrow sticker" - icon_state = "larrow" - -/obj/item/sticker/right_arrow - name = "right arrow sticker" - icon_state = "rarrow" - -/obj/item/sticker/star - name = "star sticker" - icon_state = "star1" - icon_states = list("star1","star2") - -/obj/item/sticker/heart - name = "heart sticker" - icon_state = "heart" - -/obj/item/sticker/googly - name = "googly eye sticker" - icon_state = "googly1" - icon_states = list("googly1","googly2") - -/obj/item/sticker/rev - name = "blue R sticker" - desc = "A sticker of FUCK THE SYSTEM, the galaxy's premiere hardcore punk band." - icon_state = "revhead" - -/obj/item/sticker/pslime - name = "slime plushie sticker" - icon_state = "pslime" - -/obj/item/sticker/pliz - name = "lizard plushie sticker" - icon_state = "plizard" - -/obj/item/sticker/pbee - name = "bee plushie sticker" - icon_state = "pbee" - -/obj/item/sticker/psnake - name = "snake plushie sticker" - icon_state = "psnake" - -/obj/item/sticker/robot - name = "bot sticker" - icon_state = "tile" - icon_states = list("tile","medbot","clean") - -/obj/item/sticker/toolbox - name = "toolbox sticker" - icon_state = "toolbox" - -/obj/item/sticker/clown - name = "clown sticker" - icon_state = "honkman" - -/obj/item/sticker/mime - name = "mime sticker" - icon_state = "silentman" - -/obj/item/sticker/assistant - name = "assistant sticker" - icon_state = "tider" - -/obj/item/sticker/syndicate - name = "syndicate sticker" - icon_state = "synd" - contraband = TRUE - -/obj/item/sticker/syndicate/c4 - name = "C-4 sticker" - icon_state = "c4" - -/obj/item/sticker/syndicate/bomb - name = "syndicate bomb sticker" - icon_state = "sbomb" - -/obj/item/sticker/syndicate/apc - name = "broken APC sticker" - icon_state = "milf" - -/obj/item/sticker/syndicate/larva - name = "larva sticker" - icon_state = "larva" - -/obj/item/sticker/syndicate/cult - name = "bloody paper sticker" - icon_state = "cult" - -/obj/item/sticker/syndicate/flash - name = "flash sticker" - icon_state = "flash" - -/obj/item/sticker/syndicate/op - name = "operative sticker" - icon_state = "newcop" - -/obj/item/sticker/syndicate/trap - name = "bear trap sticker" - icon_state = "trap" diff --git a/code/game/objects/items/stickers.dm b/code/game/objects/items/stickers.dm new file mode 100644 index 0000000000000..a8b800866a497 --- /dev/null +++ b/code/game/objects/items/stickers.dm @@ -0,0 +1,203 @@ +#define MAX_STICKER_COUNT 15 + +/** + * What stickers can do? + * + * - They can be attached to any object. + * - They inherit cursor position when attached. + * - They are unclickable by mouse, I suppose? + * - They can be washed off. + * - They can be burnt off. + * - They can be attached to the object they collided with. + * - They play "attack" animation when attached. + * + */ + +/obj/item/sticker + name = "sticker" + desc = "A sticker with some strong adhesive on the back, sticks to stuff!" + + icon = 'icons/obj/toys/stickers.dmi' + + max_integrity = 50 + resistance_flags = FLAMMABLE + + throw_range = 3 + pressure_resistance = 0 + + item_flags = NOBLUDGEON | XENOMORPH_HOLDABLE //funny ~Jimmyl + w_class = WEIGHT_CLASS_TINY + + /// `list` or `null`, contains possible alternate `icon_states`. + var/list/icon_states + /// Whether sticker is legal and allowed to generate inside non-syndicate boxes. + var/contraband = FALSE + +/obj/item/sticker/Initialize(mapload) + . = ..() + + if(length(icon_states)) + icon_state = pick(icon_states) + +/obj/item/sticker/Bump(atom/bumped_atom) + if(prob(50) && attempt_attach(bumped_atom)) + bumped_atom.balloon_alert_to_viewers("sticker landed on sticky side!") + +/obj/item/sticker/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!isatom(interacting_with)) + return NONE + + var/cursor_x = text2num(LAZYACCESS(modifiers, ICON_X)) + var/cursor_y = text2num(LAZYACCESS(modifiers, ICON_Y)) + + if(isnull(cursor_x) || isnull(cursor_y)) + return NONE + + if(attempt_attach(interacting_with, user, cursor_x, cursor_y)) + return ITEM_INTERACT_SUCCESS + + return NONE + +/** + * Attempts to attach sticker to an object. Returns `FALSE` if atom has more than + * `MAX_STICKER_COUNT` stickers, `TRUE` otherwise. If no `px` or `py` were passed + * picks random coordinates based on a `target`'s icon. + */ +/obj/item/sticker/proc/attempt_attach(atom/target, mob/user, px, py) + if(COUNT_TRAIT_SOURCES(target, TRAIT_STICKERED) >= MAX_STICKER_COUNT) + balloon_alert_to_viewers("sticker won't stick!") + return FALSE + + if(isnull(px) || isnull(py)) + var/icon/target_mask = icon(target.icon, target.icon_state) + + if(isnull(px)) + px = rand(1, target_mask.Width()) + + if(isnull(py)) + py = rand(1, target_mask.Height()) + + if(!isnull(user)) + user.do_attack_animation(target, used_item = src) + target.balloon_alert(user, "sticker sticked") + + target.AddComponent(/datum/component/sticker, src, user, get_dir(target, src), px, py) + return TRUE + +#undef MAX_STICKER_COUNT + +/obj/item/sticker/smile + name = "smiley sticker" + icon_state = "smile" + +/obj/item/sticker/frown + name = "frowny sticker" + icon_state = "frown" + +/obj/item/sticker/left_arrow + name = "left arrow sticker" + icon_state = "arrow-left" + +/obj/item/sticker/right_arrow + name = "right arrow sticker" + icon_state = "arrow-right" + +/obj/item/sticker/star + name = "star sticker" + icon_state = "star" + +/obj/item/sticker/heart + name = "heart sticker" + icon_state = "heart" + +/obj/item/sticker/googly + name = "googly eye sticker" + icon_state = "googly" + icon_states = list("googly", "googly-alt") + +/obj/item/sticker/rev + name = "blue R sticker" + desc = "A sticker of FUCK THE SYSTEM, the galaxy's premiere hardcore punk band." + icon_state = "revhead" + +/obj/item/sticker/pslime + name = "slime plushie sticker" + icon_state = "pslime" + +/obj/item/sticker/pliz + name = "lizard plushie sticker" + icon_state = "plizard" + +/obj/item/sticker/pbee + name = "bee plushie sticker" + icon_state = "pbee" + +/obj/item/sticker/psnake + name = "snake plushie sticker" + icon_state = "psnake" + +/obj/item/sticker/robot + name = "bot sticker" + icon_state = "tile" + icon_states = list("tile", "medbot", "clean") + +/obj/item/sticker/toolbox + name = "toolbox sticker" + icon_state = "soul" + +/obj/item/sticker/clown + name = "clown sticker" + icon_state = "honkman" + +/obj/item/sticker/mime + name = "mime sticker" + icon_state = "silentman" + +/obj/item/sticker/assistant + name = "assistant sticker" + icon_state = "tider" + +/obj/item/sticker/skub + name = "skub sticker" + icon_state = "skub" + +/obj/item/sticker/anti_skub + name = "anti-skub sticker" + icon_state = "anti_skub" + +/obj/item/sticker/syndicate + name = "syndicate sticker" + icon_state = "synd" + contraband = TRUE + +/obj/item/sticker/syndicate/c4 + name = "C-4 sticker" + icon_state = "c4" + +/obj/item/sticker/syndicate/bomb + name = "syndicate bomb sticker" + icon_state = "sbomb" + +/obj/item/sticker/syndicate/apc + name = "broken APC sticker" + icon_state = "milf" + +/obj/item/sticker/syndicate/larva + name = "larva sticker" + icon_state = "larva" + +/obj/item/sticker/syndicate/cult + name = "bloody paper sticker" + icon_state = "cult" + +/obj/item/sticker/syndicate/flash + name = "flash sticker" + icon_state = "flash" + +/obj/item/sticker/syndicate/op + name = "operative sticker" + icon_state = "newcop" + +/obj/item/sticker/syndicate/trap + name = "bear trap sticker" + icon_state = "trap" diff --git a/code/game/objects/items/storage/boxes/service_boxes.dm b/code/game/objects/items/storage/boxes/service_boxes.dm index 14656f0f5f71d..8dcc1f4f6b62f 100644 --- a/code/game/objects/items/storage/boxes/service_boxes.dm +++ b/code/game/objects/items/storage/boxes/service_boxes.dm @@ -209,16 +209,21 @@ desc = "A box full of random stickers. Do give to the clown." /obj/item/storage/box/stickers/proc/generate_non_contraband_stickers_list() - . = list() + var/list/allowed_stickers = list() + for(var/obj/item/sticker/sticker_type as anything in subtypesof(/obj/item/sticker)) - if(!initial(sticker_type.contraband)) - . += sticker_type - return . + if(!sticker_type::contraband) + allowed_stickers += sticker_type + + return allowed_stickers + /obj/item/storage/box/stickers/PopulateContents() var/static/list/non_contraband - if(!non_contraband) + + if(isnull(non_contraband)) non_contraband = generate_non_contraband_stickers_list() - for(var/i in 1 to rand(4,8)) + + for(var/i in 1 to rand(4, 8)) var/type = pick(non_contraband) new type(src) diff --git a/code/game/objects/items/storage/fancy.dm b/code/game/objects/items/storage/fancy.dm index a7fbe6e7452f9..d83c80ed56514 100644 --- a/code/game/objects/items/storage/fancy.dm +++ b/code/game/objects/items/storage/fancy.dm @@ -223,7 +223,7 @@ balloon_alert(user, "ooh, free coupon") var/obj/item/coupon/attached_coupon = new user.put_in_hands(attached_coupon) - attached_coupon.generate(rigged_omen ? COUPON_OMEN : null) + attached_coupon.generate(rigged_omen ? COUPON_OMEN : null, null, user) attached_coupon = null spawn_coupon = FALSE name = "discarded cigarette packet" diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 217a07500064d..aaff0fb70ce31 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -674,6 +674,7 @@ /obj/item/storage/box/syndie_kit/stickers/PopulateContents() var/list/types = subtypesof(/obj/item/sticker/syndicate) + for(var/i in 1 to atom_storage.max_slots) var/type = pick(types) new type(src) diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm index 284f5df2a6d61..70ab15427b759 100644 --- a/code/game/objects/structures/lavaland/ore_vent.dm +++ b/code/game/objects/structures/lavaland/ore_vent.dm @@ -21,6 +21,8 @@ var/discovered = FALSE /// Is this type of vent exempt from the map's vent budget/limit? Think the free iron/glass vent or boss vents. This also causes it to not roll for random mineral breakdown. var/unique_vent = FALSE + /// Does this vent spawn a node drone when tapped? Currently unique to boss vents so try not to VV it. + var/spawn_drone_on_tap = TRUE /// What icon_state do we use when the ore vent has been tapped? var/icon_state_tapped = "ore_vent_active" /// A weighted list of what minerals are contained in this vent, with weight determining how likely each mineral is to be picked in produced boulders. @@ -40,9 +42,7 @@ /// What string do we use to warn the player about the excavation event? var/excavation_warning = "Are you ready to excavate this ore vent?" - ///Are we currently spawning mobs? - var/spawning_mobs = FALSE - /// A list of mobs that can be spawned by this vent during a wave defense event. + /// A list of mobs that can be spawned by this vent during a wave defense event. var/list/defending_mobs = list( /mob/living/basic/mining/goliath, /mob/living/basic/mining/legion/spawner_made, @@ -198,6 +198,34 @@ /obj/structure/ore_vent/proc/ore_quantity_function(ore_floor) return SHEET_MATERIAL_AMOUNT * round(boulder_size * (log(rand(1 + ore_floor, 4 + ore_floor)) ** -1)) +/** + * This confirms that the user wants to start the wave defense event, and that they can start it. + */ +/obj/structure/ore_vent/proc/pre_wave_defense(mob/user, spawn_drone = TRUE) + if(tgui_alert(user, excavation_warning, "Begin defending ore vent?", list("Yes", "No")) != "Yes") + return FALSE + if(!can_interact(user)) + return FALSE + if(!COOLDOWN_FINISHED(src, wave_cooldown) || node) + return FALSE + //This is where we start spitting out mobs. + Shake(duration = 3 SECONDS) + if(spawn_drone) + node = new /mob/living/basic/node_drone(loc) + node.arrive(src) + RegisterSignal(node, COMSIG_QDELETING, PROC_REF(handle_wave_conclusion)) + particles = new /particles/smoke/ash() + for(var/i in 1 to 5) // Clears the surroundings of the ore vent before starting wave defense. + for(var/turf/closed/mineral/rock in oview(i)) + if(istype(rock, /turf/open/misc/asteroid) && prob(35)) // so it's too common + new /obj/effect/decal/cleanable/rubble(rock) + if(prob(100 - (i * 15))) + rock.gets_drilled(user, FALSE) + if(prob(50)) + new /obj/effect/decal/cleanable/rubble(rock) + sleep(0.6 SECONDS) + return TRUE + /** * Starts the wave defense event, which will spawn a number of lavaland mobs based on the size of the ore vent. * Called after the vent has been tapped by a scanning device. @@ -221,7 +249,6 @@ wave_timer = 150 SECONDS COOLDOWN_START(src, wave_cooldown, wave_timer) addtimer(CALLBACK(src, PROC_REF(handle_wave_conclusion)), wave_timer) - spawning_mobs = TRUE icon_state = icon_state_tapped update_appearance(UPDATE_ICON_STATE) @@ -272,7 +299,7 @@ if(tapped) balloon_alert_to_viewers("vent tapped!") return - if(!COOLDOWN_FINISHED(src, wave_cooldown)) + if(!COOLDOWN_FINISHED(src, wave_cooldown) || node) //We're already defending the vent, so don't scan it again. if(!scan_only) balloon_alert_to_viewers("protect the node drone!") return @@ -302,27 +329,9 @@ return if(scan_only) return - if(tgui_alert(user, excavation_warning, "Begin defending ore vent?", list("Yes", "No")) != "Yes") - return - if(!COOLDOWN_FINISHED(src, wave_cooldown)) - return - //This is where we start spitting out mobs. - Shake(duration = 3 SECONDS) - node = new /mob/living/basic/node_drone(loc) - node.arrive(src) - RegisterSignal(node, COMSIG_QDELETING, PROC_REF(handle_wave_conclusion)) - particles = new /particles/smoke/ash() - - for(var/i in 1 to 5) // Clears the surroundings of the ore vent before starting wave defense. - for(var/turf/closed/mineral/rock in oview(i)) - if(istype(rock, /turf/open/misc/asteroid) && prob(35)) // so it's too common - new /obj/effect/decal/cleanable/rubble(rock) - if(prob(100 - (i * 15))) - rock.gets_drilled(user, FALSE) - if(prob(50)) - new /obj/effect/decal/cleanable/rubble(rock) - sleep(0.6 SECONDS) + if(!pre_wave_defense(user, spawn_drone_on_tap)) + return start_wave_defense() /** @@ -416,15 +425,15 @@ if(LARGE_VENT_TYPE) boulder_size = BOULDER_SIZE_LARGE if(mapload) - SSore_generation.ore_vent_sizes["large"] += 1 + GLOB.ore_vent_sizes["large"] += 1 if(MEDIUM_VENT_TYPE) boulder_size = BOULDER_SIZE_MEDIUM if(mapload) - SSore_generation.ore_vent_sizes["medium"] += 1 + GLOB.ore_vent_sizes["medium"] += 1 if(SMALL_VENT_TYPE) boulder_size = BOULDER_SIZE_SMALL if(mapload) - SSore_generation.ore_vent_sizes["small"] += 1 + GLOB.ore_vent_sizes["small"] += 1 else boulder_size = BOULDER_SIZE_SMALL //Might as well set a default value name = initial(name) @@ -463,6 +472,7 @@ name = "menacing ore vent" desc = "An ore vent, brimming with underground ore. This one has an evil aura about it. Better be careful." unique_vent = TRUE + spawn_drone_on_tap = FALSE boulder_size = BOULDER_SIZE_LARGE mineral_breakdown = list( // All the riches of the world, eeny meeny boulder room. /datum/material/iron = 1, @@ -481,7 +491,7 @@ /mob/living/simple_animal/hostile/megafauna/dragon, /mob/living/simple_animal/hostile/megafauna/colossus, ) - excavation_warning = "Something big is nearby. Are you ABSOLUTELY ready to excavate this ore vent?" + excavation_warning = "Something big is nearby. Are you ABSOLUTELY ready to excavate this ore vent? A NODE drone will be deployed after threat is neutralized." ///What boss do we want to spawn? var/summoned_boss = null @@ -506,6 +516,8 @@ . += span_notice("[boss_string] is etched onto the side of the vent.") /obj/structure/ore_vent/boss/start_wave_defense() + if(!COOLDOWN_FINISHED(src, wave_cooldown)) + return // Completely override the normal wave defense, and just spawn the boss. var/mob/living/simple_animal/hostile/megafauna/boss = new summoned_boss(loc) RegisterSignal(boss, COMSIG_LIVING_DEATH, PROC_REF(handle_wave_conclusion)) diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index 9c1a8f1c4f3c0..a000dee8cd625 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -247,5 +247,13 @@ FLOOR SAFES . = ..() AddElement(/datum/element/undertile) +///Special safe for the station's vault. Not explicitly required, but the piggy bank inside it is. +/obj/structure/safe/vault + +/obj/structure/safe/vault/Initialize(mapload) + . = ..() + var/obj/item/piggy_bank/vault/piggy = new(src) + space += piggy.w_class + #undef SOUND_CHANCE #undef BROKEN_THRESHOLD diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm index 4d2a1d7ba6d18..74f14c56cbb32 100644 --- a/code/game/turfs/closed/minerals.dm +++ b/code/game/turfs/closed/minerals.dm @@ -141,15 +141,15 @@ return rand(1,5) if(distance < VENT_PROX_VERY_HIGH) - return 5 + return ORE_WALL_VERY_HIGH if(distance < VENT_PROX_HIGH) - return 4 + return ORE_WALL_HIGH if(distance < VENT_PROX_MEDIUM) - return 3 + return ORE_WALL_MEDIUM if(distance < VENT_PROX_LOW) - return 2 + return ORE_WALL_LOW if(distance < VENT_PROX_FAR) - return 1 + return ORE_WALL_FAR return 0 /turf/closed/mineral/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) @@ -319,7 +319,7 @@ var/turf/closed/mineral/M = T M.turf_type = src.turf_type M.mineralAmt = scale_ore_to_vent() - SSore_generation.post_ore_random["[M.mineralAmt]"] += 1 + GLOB.post_ore_random["[M.mineralAmt]"] += 1 src = M M.levelupdate() else @@ -330,7 +330,7 @@ Change_Ore(path, FALSE) Spread_Vein(path) mineralAmt = scale_ore_to_vent() - SSore_generation.post_ore_manual["[mineralAmt]"] += 1 + GLOB.post_ore_manual["[mineralAmt]"] += 1 /turf/closed/mineral/random/high_chance icon_state = "rock_highchance" diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm index 01aac3e869161..17dcdc377895f 100644 --- a/code/modules/antagonists/cult/cult_comms.dm +++ b/code/modules/antagonists/cult/cult_comms.dm @@ -147,7 +147,7 @@ asked_cultists += team_member.current var/list/yes_voters = SSpolling.poll_candidates( - question = "[span_notice(nominee)] seeks to lead your cult, do you support [nominee.p_them()]?", + question = "[span_notice(nominee.name)] seeks to lead your cult, do you support [nominee.p_them()]?", poll_time = 30 SECONDS, group = asked_cultists, alert_pic = nominee, diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm index 84f3a151dd2cb..2b323d51162a4 100644 --- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm +++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm @@ -271,6 +271,10 @@ . = TRUE if("send") start_sending() + //We ensure that the holding facility is loaded in time in case we're selling mobs. + //This isn't the prettiest place to put it, but 'start_sending()' is also used by civilian bounty computers + //And we don't need them to also load the holding facility. + SSmapping.lazy_load_template(LAZY_TEMPLATE_KEY_NINJA_HOLDING_FACILITY) . = TRUE if("stop") stop_sending() @@ -283,12 +287,9 @@ status_report = "Predicted value: " var/value = 0 - var/datum/export_report/report = new + var/obj/machinery/piratepad/pad = pad_ref?.resolve() - for(var/atom/movable/AM in get_turf(pad)) - if(AM == pad) - continue - export_item_and_contents(AM, apply_elastic = FALSE, dry_run = TRUE, external_report = report, ignore_typecache = nosell_typecache) + var/datum/export_report/report = pirate_export_loop(pad) for(var/datum/export/exported_datum in report.total_amount) status_report += exported_datum.total_printout(report,notes = FALSE) @@ -303,13 +304,8 @@ if(!sending) return - var/datum/export_report/report = new var/obj/machinery/piratepad/pad = pad_ref?.resolve() - - for(var/atom/movable/item_on_pad in get_turf(pad)) - if(item_on_pad == pad) - continue - export_item_and_contents(item_on_pad, apply_elastic = FALSE, delete_unsold = FALSE, external_report = report, ignore_typecache = nosell_typecache) + var/datum/export_report/report = pirate_export_loop(pad, dry_run = FALSE) status_report = "Sold: " var/value = 0 @@ -341,6 +337,33 @@ pad.icon_state = pad.idle_state sending = FALSE +///The loop that calculates the value of stuff on a pirate pad, or plain sell them if dry_run is FALSE. +/obj/machinery/computer/piratepad_control/proc/pirate_export_loop(obj/machinery/piratepad/pad, dry_run = TRUE) + var/datum/export_report/report = new + for(var/atom/movable/item_on_pad as anything in get_turf(pad)) + if(item_on_pad == pad) + continue + var/list/hidden_mobs = list() + var/skip_movable = FALSE + var/list/item_contents = item_on_pad.get_all_contents() + for(var/atom/movable/thing in reverse_range(item_contents)) + ///Don't destroy/sell stuff like the captain's laser gun, or borgs. + if(thing.resistance_flags & INDESTRUCTIBLE || is_type_in_typecache(thing, nosell_typecache)) + skip_movable = TRUE + break + if(isliving(thing)) + hidden_mobs += thing + if(skip_movable) + continue + for(var/mob/living/hidden as anything in hidden_mobs) + ///Sell mobs, but leave their contents intact. + export_single_item(hidden, apply_elastic = FALSE, dry_run = dry_run, external_report = report) + ///there are still licing mobs inside that item. Stop, don't sell it ffs. + if(locate(/mob/living) in item_on_pad.get_all_contents()) + continue + export_item_and_contents(item_on_pad, apply_elastic = FALSE, dry_run = dry_run, delete_unsold = FALSE, external_report = report, ignore_typecache = nosell_typecache) + return report + /// Prepares to sell the items on the pad /obj/machinery/computer/piratepad_control/proc/start_sending() var/obj/machinery/piratepad/pad = pad_ref?.resolve() @@ -391,7 +414,7 @@ /datum/export/pirate/ransom/get_cost(atom/movable/exported_item) var/mob/living/carbon/human/ransomee = exported_item - if(ransomee.stat != CONSCIOUS || !ransomee.mind) //mint condition only + if(ransomee.stat != CONSCIOUS || !ransomee.mind || HAS_TRAIT(ransomee.mind, TRAIT_HAS_BEEN_KIDNAPPED)) //mint condition only return 0 else if(FACTION_PIRATE in ransomee.faction) //can't ransom your fellow pirates to CentCom! return 0 @@ -400,6 +423,33 @@ else return 1000 +/datum/export/pirate/ransom/sell_object(mob/living/carbon/human/sold_item, datum/export_report/report, dry_run = TRUE, apply_elastic = TRUE) + . = ..() + if(. == EXPORT_NOT_SOLD) + return + var/turf/picked_turf = pick(GLOB.holdingfacility) + sold_item.forceMove(picked_turf) + var/mob_cost = get_cost(sold_item) + sold_item.process_capture(mob_cost, mob_cost * 1.2) + do_sparks(8, FALSE, sold_item) + playsound(picked_turf, 'sound/weapons/emitter2.ogg', 25, TRUE) + sold_item.flash_act() + sold_item.adjust_confusion(10 SECONDS) + sold_item.adjust_dizzy(10 SECONDS) + addtimer(src, CALLBACK(src, PROC_REF(send_back_to_station), sold_item), COME_BACK_FROM_CAPTURE_TIME) + to_chat(sold_item, span_hypnophrase("A million voices echo in your head... \"Yaarrr, thanks for the booty, landlubber. \ + You will be ransomed back to your station, so it's only a matter of time before we ship you back...")) + + return EXPORT_SOLD_DONT_DELETE + +///Send them back to the station after a while. +/datum/export/pirate/ransom/proc/send_back_to_station(mob/living/prisoner) + ///Deleted or already bailed out of the place. + if(QDELETED(prisoner) || !istype(get_area(prisoner), /area/centcom/central_command_areas/holding)) + return + var/obj/structure/closet/supplypod/back_to_station/return_pod = new() + return_pod.return_from_capture(prisoner) + /datum/export/pirate/parrot cost = 2000 unit_name = "alive parrot" diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm index e3c96fc815f3b..0a9422e07b807 100644 --- a/code/modules/antagonists/spy/spy_bounty.dm +++ b/code/modules/antagonists/spy/spy_bounty.dm @@ -133,13 +133,15 @@ do_sparks(3, FALSE, stealing) // Don't mess with it while it's going away + var/had_attack_hand_interaction = stealing.interaction_flags_atom & INTERACT_ATOM_ATTACK_HAND stealing.interaction_flags_atom &= ~INTERACT_ATOM_ATTACK_HAND + var/was_anchored = stealing.anchored stealing.anchored = TRUE // Add some pizzazz - animate(stealing, time = 0.5 SECONDS, transform = matrix(stealing.transform).Scale(0.01), easing = CUBIC_EASING) + animate(stealing, time = 0.5 SECONDS, transform = stealing.transform.Scale(0.01), easing = CUBIC_EASING) if(isitem(stealing) && ((stealing.resistance_flags & INDESTRUCTIBLE) || prob(black_market_prob))) - addtimer(CALLBACK(src, PROC_REF(send_to_black_market), stealing), 0.5 SECONDS) + addtimer(CALLBACK(src, PROC_REF(send_to_black_market), stealing, had_attack_hand_interaction, was_anchored), 0.5 SECONDS) else addtimer(CALLBACK(src, PROC_REF(finish_cleanup), stealing), 0.5 SECONDS) @@ -158,33 +160,31 @@ * * * thing - The item to put up on the black market. */ -/datum/spy_bounty/proc/send_to_black_market(atom/movable/thing) +/datum/spy_bounty/proc/send_to_black_market(atom/movable/thing, had_attack_hand_interaction, was_anchored) if(QDELETED(thing)) // Just in case anything does anything weird return FALSE - thing.interaction_flags_atom = initial(thing.interaction_flags_atom) - thing.anchored = initial(thing.anchored) + ///reset the appearance and all. + if(had_attack_hand_interaction) + thing.interaction_flags_atom |= INTERACT_ATOM_ATTACK_HAND + thing.anchored = was_anchored + thing.transform = thing.transform.Scale(10) thing.moveToNullspace() - var/datum/market_item/new_item = new() - new_item.item = thing - new_item.name = "Stolen [thing.name]" - new_item.desc = "A [thing.name], stolen from somewhere on the station. Whoever owned it probably wouldn't be happy to see it here." - new_item.category = "Fenced Goods" - new_item.stock = 1 - new_item.availability_prob = 100 - + var/item_price switch(difficulty) if(SPY_DIFFICULTY_EASY) - new_item.price = PAYCHECK_COMMAND * 2.5 + item_price = PAYCHECK_COMMAND * 2.5 if(SPY_DIFFICULTY_MEDIUM) - new_item.price = PAYCHECK_COMMAND * 5 + item_price = PAYCHECK_COMMAND * 5 if(SPY_DIFFICULTY_HARD) - new_item.price = PAYCHECK_COMMAND * 10 + item_price = PAYCHECK_COMMAND * 10 - new_item.price += rand(0, PAYCHECK_COMMAND * 5) + item_price += rand(0, PAYCHECK_COMMAND * 5) if(thing.resistance_flags & INDESTRUCTIBLE) - new_item.price *= 2 + item_price *= 2 + + var/datum/market_item/stolen_good/new_item = new(thing, item_price) return SSblackmarket.markets[/datum/market/blackmarket].add_item(new_item) diff --git a/code/modules/antagonists/traitor/contractor/syndicate_contract.dm b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm index f2868bdce3025..2c9d45e382dd4 100644 --- a/code/modules/antagonists/traitor/contractor/syndicate_contract.dm +++ b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm @@ -15,6 +15,8 @@ var/wanted_message ///List of everything found on the victim at the time of contracting, used to return their stuff afterwards. var/list/victim_belongings = list() + ///timerid for stuff that handles victim chat messages, effects and returnal + var/victim_timerid /datum/syndicate_contract/New(contract_owner, blacklist, type=CONTRACT_PAYOUT_SMALL) contract = new(src) @@ -122,20 +124,8 @@ //we'll start the effects in a few seconds since it takes a moment for the pod to leave. addtimer(CALLBACK(src, PROC_REF(handle_victim_experience), person_sent), 3 SECONDS) - // This is slightly delayed because of the sleep calls above to handle the narrative. - // We don't want to tell the station instantly. - var/points_to_check - var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR) - if(cargo_account) - points_to_check = cargo_account.account_balance - if(points_to_check >= ransom) - cargo_account.adjust_money(-ransom) - else - cargo_account.adjust_money(-points_to_check) - priority_announce( - text = "One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. \ - As is policy we've taken a portion of the station's funds to offset the overall cost.", - sender_override = "Nanotrasen Asset Protection") + var/datum/market_item/hostage/market_item = person_sent.process_capture(ransom * (rand(11, 13)/10)) + RegisterSignal(market_item, COMSIG_MARKET_ITEM_SPAWNED, PROC_REF(on_victim_shipped)) addtimer(CALLBACK(src, PROC_REF(finish_enter)), 3 SECONDS) @@ -166,10 +156,10 @@ * level - The current stage of harassement they are facing. This increases by itself, looping until finished. */ /datum/syndicate_contract/proc/handle_victim_experience(mob/living/victim, level = VICTIM_EXPERIENCE_START) - // Ship 'em back - dead or alive, 4 minutes wait. + // Ship 'em back - dead or alive // Even if they weren't the target, we're still treating them the same. if(!level) - addtimer(CALLBACK(src, PROC_REF(return_victim), victim), (60 * 10) * 4) + victim_timerid = addtimer(CALLBACK(src, PROC_REF(return_victim), victim), COME_BACK_FROM_CAPTURE_TIME, TIMER_STOPPABLE) if(victim.stat == DEAD) return @@ -209,7 +199,7 @@ level++ //move onto the next level. if(time_until_next) - addtimer(CALLBACK(src, PROC_REF(handle_victim_experience), victim, level), time_until_next) + victim_timerid = addtimer(CALLBACK(src, PROC_REF(handle_victim_experience), victim, level), time_until_next, TIMER_STOPPABLE) #undef VICTIM_EXPERIENCE_START #undef VICTIM_EXPERIENCE_FIRST_HIT @@ -217,34 +207,30 @@ #undef VICTIM_EXPERIENCE_THIRD_HIT #undef VICTIM_EXPERIENCE_LAST_HIT -// We're returning the victim +/// We're returning the victim to the station /datum/syndicate_contract/proc/return_victim(mob/living/victim) var/list/possible_drop_loc = list() - - for(var/turf/possible_drop in contract.dropoff.contents) + for(var/turf/possible_drop in shuffle(contract.dropoff.contents)) if(!isspaceturf(possible_drop) && !isclosedturf(possible_drop)) if(!possible_drop.is_blocked_turf()) possible_drop_loc.Add(possible_drop) - if(!possible_drop_loc.len) - to_chat(victim, span_hypnophrase("A million voices echo in your head... \"Seems where you got sent here from won't \ - be able to handle our pod... if we wanted the occupant to survive. Brace yourself, corporate dog.\"")) - for(var/turf/possible_drop in contract.dropoff.contents) - possible_drop_loc.Add(possible_drop) - if(iscarbon(victim)) - var/mob/living/carbon/carbon_victim = victim - if(carbon_victim.can_heartattack()) - carbon_victim.set_heartattack(TRUE) - - var/pod_rand_loc = rand(1, possible_drop_loc.len) - var/obj/structure/closet/supplypod/return_pod = new() - return_pod.bluespace = TRUE - return_pod.explosionSize = list(0,0,0,0) - return_pod.style = STYLE_SYNDICATE - - do_sparks(8, FALSE, victim) - victim.visible_message(span_notice("[victim] vanishes...")) + var/turf/destination + if(length(possible_drop_loc)) + destination = pick(possible_drop_loc) + var/obj/structure/closet/supplypod/back_to_station/return_pod = new() + return_pod.return_from_capture(victim, destination) + returnal_side_effects(return_pod, victim) + +///Called if the victim is being returned to the station early, when from the black market. +/datum/syndicate_contract/proc/on_victim_shipped(datum/market_item/source, obj/item/market_uplink/uplink, shipping_method, turf/shipping_loc) + SIGNAL_HANDLER + deltimer(victim_timerid) + returnal_side_effects(shipping_loc, source.item) + +///The annoying negative effects applied to the victim when returned to the station. +/datum/syndicate_contract/proc/returnal_side_effects(atom/dropoff_location, mob/living/victim) for(var/datum/weakref/belonging_ref in victim_belongings) var/obj/item/belonging = belonging_ref.resolve() if(!belonging) @@ -256,16 +242,12 @@ continue if(belonging == human_victim.shoes) continue - belonging.forceMove(return_pod) - - for(var/obj/item/W in victim_belongings) - W.forceMove(return_pod) + belonging.forceMove(dropoff_location) - victim.forceMove(return_pod) + for(var/obj/item/item in victim_belongings) + item.forceMove(dropoff_location) victim.flash_act() victim.adjust_eye_blur(3 SECONDS) victim.adjust_dizzy(3.5 SECONDS) victim.adjust_confusion(2 SECONDS) - - new /obj/effect/pod_landingzone(possible_drop_loc[pod_rand_loc], return_pod) diff --git a/code/modules/antagonists/traitor/objectives/kidnapping.dm b/code/modules/antagonists/traitor/objectives/kidnapping.dm index 73a3fc3b3f041..d6aec912fdbac 100644 --- a/code/modules/antagonists/traitor/objectives/kidnapping.dm +++ b/code/modules/antagonists/traitor/objectives/kidnapping.dm @@ -15,6 +15,8 @@ var/alive_bonus = 0 /// All stripped targets belongings (weakrefs) var/list/target_belongings = list() + /// The ID of the stoppable timer for returning the captured crew + var/list/victim_timerid duplicate_type = /datum/traitor_objective/target_player @@ -216,6 +218,7 @@ new /obj/effect/pod_landingzone(get_turf(user), new_pod) /datum/traitor_objective/target_player/kidnapping/proc/enter_check(obj/structure/closet/supplypod/extractionpod/source, entered_atom) + SIGNAL_HANDLER if(!istype(source)) CRASH("Kidnapping objective's enter_check called with source being not an extraction pod: [source ? source.type : "N/A"]") @@ -224,9 +227,6 @@ var/mob/living/carbon/human/sent_mob = entered_atom - if(sent_mob.mind) - ADD_TRAIT(sent_mob.mind, TRAIT_HAS_BEEN_KIDNAPPED, TRAIT_GENERIC) - for(var/obj/item/belonging in sent_mob.gather_belongings()) if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET)) continue @@ -236,12 +236,8 @@ continue target_belongings.Add(WEAKREF(belonging)) - var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR) - - if(cargo_account) //Just in case - cargo_account.adjust_money(-min(rand(1000, 3000), cargo_account.account_balance)) //Not so much, especially for competent cargo. Plus this can't be mass-triggered like it has been done with contractors - - priority_announce("One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. As is policy we've taken a portion of the station's funds to offset the overall cost.", "Nanotrasen Asset Protection", has_important_message = TRUE) + var/datum/market_item/hostage/market_item = sent_mob.process_capture(rand(1000, 3000)) + RegisterSignal(market_item, COMSIG_MARKET_ITEM_SPAWNED, PROC_REF(on_victim_shipped)) addtimer(CALLBACK(src, PROC_REF(handle_target), sent_mob), 1.5 SECONDS) @@ -257,7 +253,7 @@ source.startExitSequence(source) /datum/traitor_objective/target_player/kidnapping/proc/handle_target(mob/living/carbon/human/sent_mob) - addtimer(CALLBACK(src, PROC_REF(return_target), sent_mob), 3 MINUTES) + victim_timerid = addtimer(CALLBACK(src, PROC_REF(return_target), sent_mob), COME_BACK_FROM_CAPTURE_TIME, TIMER_STOPPABLE) if(sent_mob.stat == DEAD) return @@ -266,29 +262,24 @@ sent_mob.adjust_dizzy(10 SECONDS) sent_mob.set_eye_blur_if_lower(100 SECONDS) sent_mob.dna.species.give_important_for_life(sent_mob) // so plasmamen do not get left for dead - to_chat(sent_mob, span_hypnophrase(span_reallybig("A million voices echo in your head... \"Your mind held many valuable secrets - \ + to_chat(sent_mob, span_hypnophrase("A million voices echo in your head... \"Your mind held many valuable secrets - \ we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \ - so it's only a matter of time before we ship you back...\""))) + so it's only a matter of time before we ship you back...\"")) /datum/traitor_objective/target_player/kidnapping/proc/return_target(mob/living/carbon/human/sent_mob) if(!sent_mob || QDELETED(sent_mob)) //suicided and qdeleted themselves return - var/turf/return_turf = get_safe_random_station_turf() - if(!return_turf) //SOMEHOW - to_chat(sent_mob, span_hypnophrase(span_reallybig("A million voices echo in your head... \"Seems where you got sent here from won't \ - be able to handle our pod... You will die here instead.\""))) - if (sent_mob.can_heartattack()) - sent_mob.set_heartattack(TRUE) - return + var/obj/structure/closet/supplypod/back_to_station/return_pod = new() + return_pod.return_from_capture(sent_mob) + returnal_side_effects(return_pod, sent_mob) - var/obj/structure/closet/supplypod/return_pod = new() - return_pod.bluespace = TRUE - return_pod.explosionSize = list(0,0,0,0) - return_pod.style = STYLE_SYNDICATE +/datum/traitor_objective/target_player/kidnapping/proc/on_victim_shipped(datum/market_item/source, obj/item/market_uplink/uplink, shipping_method, turf/shipping_loc) + SIGNAL_HANDLER + deltimer(victim_timerid) + returnal_side_effects(shipping_loc, source.item) - do_sparks(8, FALSE, sent_mob) - sent_mob.visible_message(span_notice("[sent_mob] vanishes!")) +/datum/traitor_objective/target_player/kidnapping/proc/returnal_side_effects(atom/dropoff_location, mob/living/carbon/human/sent_mob) for(var/obj/item/belonging in sent_mob.gather_belongings()) if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET)) continue @@ -298,13 +289,10 @@ var/obj/item/belonging = belonging_ref.resolve() if(!belonging) continue - belonging.forceMove(return_pod) + belonging.forceMove(dropoff_location) - sent_mob.forceMove(return_pod) sent_mob.flash_act() sent_mob.adjust_confusion(10 SECONDS) sent_mob.adjust_dizzy(10 SECONDS) sent_mob.set_eye_blur_if_lower(100 SECONDS) sent_mob.dna.species.give_important_for_life(sent_mob) // so plasmamen do not get left for dead - - new /obj/effect/pod_landingzone(return_turf, return_pod) diff --git a/code/modules/cargo/coupon.dm b/code/modules/cargo/coupon.dm index 4c5e56a7d4119..8eefcc8676613 100644 --- a/code/modules/cargo/coupon.dm +++ b/code/modules/cargo/coupon.dm @@ -63,7 +63,7 @@ update_name() /// Choose what our prize is :D -/obj/item/coupon/proc/generate(discount, datum/supply_pack/discounted_pack) +/obj/item/coupon/proc/generate(discount, datum/supply_pack/discounted_pack, mob/user) src.discounted_pack = discounted_pack || pick(GLOB.discountable_packs[pick_weight(GLOB.pack_discount_odds)]) var/static/list/chances = list("0.10" = 4, "0.15" = 8, "0.20" = 10, "0.25" = 8, "0.50" = 4, COUPON_OMEN = 1) discount_pct_off = discount || pick_weight(chances) @@ -77,14 +77,14 @@ name = "coupon - fuck you" desc = "The small text reads, 'You will be slaughtered'... That doesn't sound right, does it?" - if(!ismob(loc)) + var/mob/cursed = user || loc + if(!ismob(cursed)) return FALSE - var/mob/cursed = loc to_chat(cursed, span_warning("The coupon reads 'fuck you' in large, bold text... is- is that a prize, or?")) if(!cursed.GetComponent(/datum/component/omen)) - cursed.AddComponent(/datum/component/omen, 1) + cursed.AddComponent(/datum/component/omen, src, 1) return TRUE if(HAS_TRAIT(cursed, TRAIT_CURSED)) to_chat(cursed, span_warning("What a horrible night... To have a curse!")) @@ -98,6 +98,7 @@ /obj/item/coupon/proc/curse_heart(mob/living/cursed) if(!iscarbon(cursed)) cursed.gib(DROP_ALL_REMAINS) + burn_evilly() return TRUE var/mob/living/carbon/player = cursed @@ -105,6 +106,11 @@ to_chat(player, span_mind_control("What could that coupon mean?")) to_chat(player, span_userdanger("...The suspense is killing you!")) player.set_heartattack(status = TRUE) + burn_evilly() + +/obj/item/coupon/proc/burn_evilly() + visible_message(span_warning("[src] burns up in a sinister flash, taking an evil energy with it...")) + burn() /obj/item/coupon/attack_atom(obj/O, mob/living/user, params) if(!istype(O, /obj/machinery/computer/cargo)) diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm index fd6e91a858038..224a27062f5dd 100644 --- a/code/modules/cargo/exports.dm +++ b/code/modules/cargo/exports.dm @@ -30,6 +30,14 @@ Then the player gets the profit from selling his own wasted time. ///set to false if any objects in a dry run were unscannable var/all_contents_scannable = TRUE +/// Makes sure the exports list is populated and that the report isn't null. +/proc/init_export(datum/export_report/external_report) + if(!length(GLOB.exports_list)) + setupExports() + if(isnull(external_report)) + external_report = new + return external_report + /* * Handles exporting a movable atom and its contents * Arguments: @@ -40,32 +48,15 @@ Then the player gets the profit from selling his own wasted time. ** ignore_typecache: typecache containing types that should be completely ignored */ /proc/export_item_and_contents(atom/movable/exported_atom, apply_elastic = TRUE, delete_unsold = TRUE, dry_run = FALSE, datum/export_report/external_report, list/ignore_typecache) - if(!GLOB.exports_list.len) - setupExports() + external_report = init_export() var/list/contents = exported_atom.get_all_contents_ignoring(ignore_typecache) - var/datum/export_report/report = external_report - - if(!report) //If we don't have any longer transaction going on - report = new - // We go backwards, so it'll be innermost objects sold first. We also make sure nothing is accidentally delete before everything is sold. var/list/to_delete = list() for(var/atom/movable/thing as anything in reverse_range(contents)) - var/sold = FALSE - for(var/datum/export/export as anything in GLOB.exports_list) - if(export.applies_to(thing, apply_elastic)) - if(!dry_run && (SEND_SIGNAL(thing, COMSIG_ITEM_PRE_EXPORT) & COMPONENT_STOP_EXPORT)) - break - //Don't add value of unscannable items for a dry run report - if(dry_run && !export.scannable) - report.all_contents_scannable = FALSE - break - sold = export.sell_object(thing, report, dry_run, apply_elastic) - report.exported_atoms += " [thing.name]" - break - if(!dry_run && (sold || delete_unsold)) + var/sold = _export_loop(thing, apply_elastic, dry_run, external_report) + if(!dry_run && (sold || delete_unsold) && sold != EXPORT_SOLD_DONT_DELETE) if(ismob(thing)) thing.investigate_log("deleted through cargo export", INVESTIGATE_CARGO) to_delete += thing @@ -74,7 +65,35 @@ Then the player gets the profit from selling his own wasted time. if(!QDELETED(thing)) qdel(thing) - return report + return external_report + +/// It works like export_item_and_contents(), however it ignores the contents. Meaning only `exported_atom` will be valued. +/proc/export_single_item(atom/movable/exported_atom, apply_elastic = TRUE, delete_unsold = TRUE, dry_run = FALSE, datum/export_report/external_report) + external_report = init_export() + + var/sold = _export_loop(exported_atom, apply_elastic, dry_run, external_report) + if(!dry_run && (sold || delete_unsold) && sold != EXPORT_SOLD_DONT_DELETE) + if(ismob(exported_atom)) + exported_atom.investigate_log("deleted through cargo export", INVESTIGATE_CARGO) + qdel(exported_atom) + + return external_report + +/// The main bit responsible for selling the item. Shared by export_single_item() and export_item_and_contents() +/proc/_export_loop(atom/movable/exported_atom, apply_elastic = TRUE, dry_run = FALSE, datum/export_report/external_report) + var/sold = EXPORT_NOT_SOLD + for(var/datum/export/export as anything in GLOB.exports_list) + if(export.applies_to(exported_atom, apply_elastic)) + if(!dry_run && (SEND_SIGNAL(exported_atom, COMSIG_ITEM_PRE_EXPORT) & COMPONENT_STOP_EXPORT)) + break + //Don't add value of unscannable items for a dry run report + if(dry_run && !export.scannable) + external_report.all_contents_scannable = FALSE + break + sold = export.sell_object(exported_atom, exported_atom, dry_run, apply_elastic) + external_report.exported_atoms += " [exported_atom.name]" + break + return sold /datum/export /// Unit name. Only used in "Received [total_amount] [name]s [message]." @@ -164,7 +183,7 @@ Then the player gets the profit from selling his own wasted time. var/export_amount = get_amount(sold_item) if(export_amount <= 0 || (export_value <= 0 && !allow_negative_cost)) - return FALSE + return EXPORT_NOT_SOLD // If we're not doing a dry run, send COMSIG_ITEM_EXPORTED to the sold item var/export_result @@ -180,7 +199,7 @@ Then the player gets the profit from selling his own wasted time. if(apply_elastic) cost *= NUM_E**(-1 * k_elasticity * export_amount) //marginal cost modifier SSblackbox.record_feedback("nested tally", "export_sold_cost", 1, list("[sold_item.type]", "[export_value]")) - return TRUE + return EXPORT_SOLD /* * Total printout for the cargo console. diff --git a/code/modules/cargo/markets/_market.dm b/code/modules/cargo/markets/_market.dm index a4af2bc981d94..78585ed842f2e 100644 --- a/code/modules/cargo/markets/_market.dm +++ b/code/modules/cargo/markets/_market.dm @@ -20,39 +20,53 @@ categories += item.category available_items[item.category] = list() - available_items[item.category] += item + available_items[item.category][item.identifier] = item + RegisterSignal(item, COMSIG_QDELETING, PROC_REF(on_item_del)) return TRUE +/datum/market/proc/on_item_del(datum/market_item/item) + SIGNAL_HANDLER + available_items[item.category] -= item.identifier + if(!length(available_items[item.category])) + available_items -= item.category + /// Handles buying the item, this is mainly for future use and moving the code away from the uplink. -/datum/market/proc/purchase(item, category, method, obj/item/market_uplink/uplink, user) - if(!istype(uplink) || !(method in shipping)) +/datum/market/proc/purchase(identifier, category, method, obj/item/market_uplink/uplink, user) + var/datum/market_item/item = available_items[category][identifier] + if(isnull(item)) + return FALSE + + if(!istype(uplink) || !((method in shipping) || (method in item.shipping_override))) + return FALSE + + var/shipment_fee = item.shipping_override?[method] + if(isnull(shipment_fee)) + shipment_fee = shipping[method] + var/price = item.price + shipment_fee + + if(!uplink.current_user)///There is no ID card on the user, or the ID card has no account + to_chat(user, span_warning("The uplink sparks, as it can't identify an ID card with a bank account on you.")) return FALSE + var/balance = uplink?.current_user.account_balance - for(var/datum/market_item/I in available_items[category]) - if(I.type != item) - continue - var/price = I.price + shipping[method] - - if(!uplink.current_user)///There is no ID card on the user, or the ID card has no account - to_chat(user, span_warning("The uplink sparks, as it can't identify an ID card with a bank account on you.")) - return FALSE - var/balance = uplink?.current_user.account_balance - - // I can't get the price of the item and shipping in a clean way to the UI, so I have to do this. - if(balance < price) - to_chat(user, span_warning("You don't have enough credits in [uplink] for [I] with [method] shipping.")) - return FALSE - - if(I.buy(uplink, user, method)) - uplink.current_user.adjust_money(-price, "Other: Third Party Transaction") - if(ismob(user)) - var/mob/m_user = user - m_user.playsound_local(get_turf(m_user), 'sound/machines/twobeep_high.ogg', 50, TRUE) - return TRUE + // I can't get the price of the item and shipping in a clean way to the UI, so I have to do this. + if(balance < price) + to_chat(user, span_warning("You don't have enough credits in [uplink] for [item] with [method] shipping.")) return FALSE + if(item.buy(uplink, user, method)) + uplink.current_user.adjust_money(-price, "Other: Third Party Transaction") + if(ismob(user)) + var/mob/m_user = user + m_user.playsound_local(get_turf(m_user), 'sound/machines/twobeep_high.ogg', 50, TRUE) + return TRUE + + return FALSE + /datum/market/blackmarket name = "Black Market" - shipping = list(SHIPPING_METHOD_LTSRBT =50, - SHIPPING_METHOD_LAUNCH =10, - SHIPPING_METHOD_TELEPORT=75) + shipping = list( + SHIPPING_METHOD_LTSRBT = 40, + SHIPPING_METHOD_LAUNCH = 10, + SHIPPING_METHOD_TELEPORT= 75, + ) diff --git a/code/modules/cargo/markets/market_item.dm b/code/modules/cargo/markets/market_item.dm index d5689c17a45e6..01eb37c077ba2 100644 --- a/code/modules/cargo/markets/market_item.dm +++ b/code/modules/cargo/markets/market_item.dm @@ -14,7 +14,7 @@ var/stock /// Path to or the item itself what this entry is for, this should be set even if you override spawn_item to spawn your item. - var/obj/item/item + var/atom/movable/item /// Minimum price for the item if generated randomly. var/price_min = 0 @@ -27,27 +27,60 @@ /// Probability for this item to be available. Used by SSblackmarket on init. var/availability_prob = 0 + ///The identifier for the market item, generated on runtime and used to access them in the market categories. + var/identifier + + ///If set, these will override the shipment methods set by the market + var/list/shipping_override + /datum/market_item/New() if(isnull(price)) price = rand(price_min, price_max) if(isnull(stock)) stock = rand(stock_min, stock_max) + identifier = "[type]" + +///For 'dynamic' market items generated on runtime, this proc is to be used to properly sets the item, especially if it's a hardref. +/datum/market_item/proc/set_item(path_or_ref) + //we're replacing the item to sell, and the old item is an instance! + if(ismovable(item)) + UnregisterSignal(item, COMSIG_QDELETING) + item = path_or_ref + identifier = "[path_or_ref]" + if(ismovable(path_or_ref)) + RegisterSignal(item, COMSIG_QDELETING, PROC_REF(on_item_del)) + identifier = "[REF(src)]" /datum/market_item/Destroy() item = null return ..() +/datum/market_item/proc/on_item_del(datum/source) + SIGNAL_HANDLER + qdel(src) + /// Used for spawning the wanted item, override if you need to do something special with the item. -/datum/market_item/proc/spawn_item(loc) +/datum/market_item/proc/spawn_item(loc, datum/market_purchase/purchase) + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_MARKET_ITEM_SPAWNED, purchase.uplink, purchase.method, loc) if(ismovable(item)) - item.forceMove(loc) - return item + var/atom/movable/return_item = item + UnregisterSignal(item, COMSIG_QDELETING) + if(isnull(loc)) + item.moveToNullspace() + else + do_sparks(8, FALSE, item) + item.visible_message(span_notice("[item] vanishes...")) + item.forceMove(loc) + item = null + return return_item if(ispath(item)) return new item(loc) CRASH("Invalid item type for market item [item || "null"]") /// Buys the item and makes SSblackmarket handle it. /datum/market_item/proc/buy(obj/item/market_uplink/uplink, mob/buyer, shipping_method) + SHOULD_CALL_PARENT(TRUE) // Sanity if(!istype(uplink) || !istype(buyer)) return FALSE @@ -70,16 +103,34 @@ /datum/market_purchase /// The entry being purchased. var/datum/market_item/entry - /// Instance of the item being sent. - var/item + /// Instance of the item being sent, used by the market telepad + var/atom/movable/item /// The uplink where this purchase was done from. var/obj/item/market_uplink/uplink /// Shipping method used to buy this item. var/method -/datum/market_purchase/New(_entry, _uplink, _method) - entry = _entry - if(!ispath(entry.item)) +/datum/market_purchase/New(datum/market_item/entry, obj/item/market_uplink/uplink, method) + if(!uplink || !entry || !method) + CRASH("[type] created with a false value arg: (entry: [entry] - uplink: [uplink] - method: [method])") + src.entry = entry + src.uplink = uplink + src.method = method + RegisterSignal(entry, COMSIG_QDELETING, PROC_REF(on_instance_del)) + RegisterSignal(uplink, COMSIG_QDELETING, PROC_REF(on_instance_del)) + if(ismovable(entry.item)) item = entry.item - uplink = _uplink - method = _method + RegisterSignal(entry.item, COMSIG_QDELETING, PROC_REF(on_instance_del)) + +/datum/market_purchase/Destroy() + entry = null + uplink = null + SSblackmarket.queued_purchases -= src + return ..() + +/datum/market_purchase/proc/on_instance_del(datum/source) + SIGNAL_HANDLER + if(QDELETED(src)) + return + // Uh oh, uplink or item is gone. We will just keep the money and you will not get your order. + qdel(src) diff --git a/code/modules/cargo/markets/market_items/consumables.dm b/code/modules/cargo/markets/market_items/consumables.dm index 153241e5d3db6..5550d31c5f865 100644 --- a/code/modules/cargo/markets/market_items/consumables.dm +++ b/code/modules/cargo/markets/market_items/consumables.dm @@ -39,7 +39,8 @@ /obj/item/storage/pill_bottle/lsd, /obj/item/storage/pill_bottle/aranesp, /obj/item/storage/pill_bottle/stimulant)) - return new pillbottle(loc) + item = pillbottle + return ..() /datum/market_item/consumable/floor_pill name = "Strange Pill" diff --git a/code/modules/cargo/markets/market_items/hostages.dm b/code/modules/cargo/markets/market_items/hostages.dm new file mode 100644 index 0000000000000..6551ee6156b46 --- /dev/null +++ b/code/modules/cargo/markets/market_items/hostages.dm @@ -0,0 +1,84 @@ +///A special category for mobs captured by pirates, tots and contractors, should someone ever want to get them back in advance. +/datum/market_item/hostage + category = "Hostages" + stock = 1 + availability_prob = 100 + shipping_override = list(SHIPPING_METHOD_LTSRBT = 0, SHIPPING_METHOD_SUPPLYPOD = 350) + /// temporary reference to the 4 in 7 chances of signaler and electropack. + var/obj/item/assembly/signaler/signaler + +/datum/market_item/hostage/New(mob/living/mob, new_price) + ..() + set_item(mob) + name = "[mob.real_name]" + var/specimen = initial(mob.name) + var/humie_mob = ishuman(mob) + if(humie_mob) + var/mob/living/carbon/human/humie = mob + specimen = humie.dna.species.name + desc = pick(list( + "If you're looking for a recently stolen [specimen], you've come to the right place.", + "we've recently aquired a fine [specimen] from a station around here, eheh...", + "For a limited time, we're offering this [specimen] for you to buy (back).", + )) + desc += " DISCLAIMER: The offer will expire once the creature is returned to the station." + if(humie_mob) + desc += "[mob.p_they(TRUE)] may be delivered handcuffed, for safety of course." + + price = new_price + RegisterSignal(mob, COMSIG_LIVING_RETURN_FROM_CAPTURE, PROC_REF(on_return_from_capture)) + +/datum/market_item/hostage/proc/on_return_from_capture(mob/living/source, turf/destination) + SIGNAL_HANDLER + qdel(src) //as if we never existed, our mentions we'll be removed from the market. + +/datum/market_item/hostage/Destroy() + signaler = null + return ..() + +/datum/market_item/hostage/buy(obj/item/market_uplink/uplink, mob/buyer, shipping_method) + . = ..() + var/mob/living/humie = item + if(!. || !istype(humie) || !prob(57)) // 3 in 7 chance of the electropack set not spawning... + return + signaler = new(uplink.drop_location()) + RegisterSignal(signaler, COMSIG_QDELETING, PROC_REF(clear_signaler_ref)) + signaler.set_frequency(sanitize_frequency(rand(MIN_FREE_FREQ, MAX_FREE_FREQ))) + signaler.code = rand(1, 100) + buyer.put_in_hands(signaler) + to_chat(buyer, span_notice("A [signaler] appears [buyer.is_holding(signaler) ? "in your hands" : "at your feet"]!")) + +/datum/market_item/hostage/proc/clear_signaler_ref(datum/source) + SIGNAL_HANDLER + signaler = null + +/datum/market_item/hostage/spawn_item(loc, datum/market_purchase/purchase) + var/mob/living/mob = item + UnregisterSignal(mob, COMSIG_LIVING_RETURN_FROM_CAPTURE) + if(!mob.IsUnconscious()) + to_chat(mob, span_boldnicegreen("You have been bought back to the station. Be grateful to whoever got you out of the holding facility early.")) + if(!ishuman(item)) + return ..() + var/mob/living/carbon/human/humie = item + if(signaler) //57% chance that the mob is equipped with electropack and cuffs + humie.equip_to_slot_or_del(new /obj/item/restraints/handcuffs, ITEM_SLOT_HANDCUFFED, indirect_action = TRUE) + if(humie.back) //try to remove the neck item if possible before we attempt to install the collar bomb + humie.transferItemToLoc(humie.back, loc) + var/obj/item/electropack/pack = new(loc) + pack.set_frequency(signaler.frequency) + pack.code = signaler.code + humie.equip_to_slot_if_possible(pack, ITEM_SLOT_BACK, disable_warning = TRUE) + UnregisterSignal(signaler, COMSIG_QDELETING) + signaler = null + else if(prob(66)) // 29% chance of just cuffs + humie.equip_to_slot_or_del(new /obj/item/restraints/handcuffs, ITEM_SLOT_HANDCUFFED, indirect_action = TRUE) + else // 14% chance of just a tee souvenir and pin, no cuffs and shit. + var/obj/item/clothing/under/misc/syndicate_souvenir/souvenir = new(loc) + humie.equip_to_slot_if_possible(souvenir, ITEM_SLOT_ICLOTHING, indirect_action = TRUE) + var/obj/item/clothing/accessory/anti_sec_pin/pin = new(loc) + pin.attach(souvenir) + + if(isnull(humie.w_uniform)) + //FUCKING SLAVES, GET YOUR CLOTHES BACK ON! + humie.equip_to_slot_or_del(new /obj/item/clothing/under/costume/jabroni(humie), ITEM_SLOT_ICLOTHING, indirect_action = TRUE) + return ..() diff --git a/code/modules/cargo/markets/market_items/misc.dm b/code/modules/cargo/markets/market_items/misc.dm index 0953a73e0acc5..de0fcaa9256a4 100644 --- a/code/modules/cargo/markets/market_items/misc.dm +++ b/code/modules/cargo/markets/market_items/misc.dm @@ -68,9 +68,11 @@ stock_max = 3 availability_prob = 40 -/datum/market_item/misc/holywater/spawn_item(loc) +/datum/market_item/misc/holywater/spawn_item(loc, datum/market_purchase/purchase) if (prob(6.66)) - return new /obj/item/reagent_containers/cup/beaker/unholywater(loc) + item = /obj/item/reagent_containers/cup/beaker/unholywater + else + item = initial(item) return ..() /datum/market_item/misc/strange_seed diff --git a/code/modules/cargo/markets/market_items/stolen_goods.dm b/code/modules/cargo/markets/market_items/stolen_goods.dm new file mode 100644 index 0000000000000..c9c17f1d2b6c8 --- /dev/null +++ b/code/modules/cargo/markets/market_items/stolen_goods.dm @@ -0,0 +1,12 @@ +///A special category for goods stolen by spies for their bounties. +/datum/market_item/stolen_good + category = "Fenced Goods" + stock = 1 + availability_prob = 100 + +/datum/market_item/stolen_good/New(atom/movable/thing, thing_price) + ..() + set_item(thing) + name = "Stolen [thing.name]" + desc = "A [thing.name], stolen from somewhere on the station. Whoever owned it probably wouldn't be happy to see it here." + price = thing_price diff --git a/code/modules/cargo/markets/market_items/tools.dm b/code/modules/cargo/markets/market_items/tools.dm index c20834e640b16..5d036fae0ef5b 100644 --- a/code/modules/cargo/markets/market_items/tools.dm +++ b/code/modules/cargo/markets/market_items/tools.dm @@ -1,6 +1,18 @@ /datum/market_item/tool category = "Tools" +/datum/market_item/tool/blackmarket_telepad + name = "Black Market LTSRBT" + desc = "Need a faster and better way of transporting your illegal goods from and to the \ + station? Fear not, the Long-To-Short-Range-Bluespace-Transceiver is here to help. \ + Contains a LTSRBT circuit. Bluespace crystals and ansible not included." + item = /obj/item/circuitboard/machine/ltsrbt + stock_min = 2 + stock_max = 4 + price_min = CARGO_CRATE_VALUE * 2.5 + price_max = CARGO_CRATE_VALUE * 3.75 + availability_prob = 100 + /datum/market_item/tool/caravan_wrench name = "Experimental Wrench" desc = "The extra fast and handy wrench you always wanted!" diff --git a/code/modules/cargo/markets/market_items/weapons.dm b/code/modules/cargo/markets/market_items/weapons.dm index 052074a30b38d..11f242d57c874 100644 --- a/code/modules/cargo/markets/market_items/weapons.dm +++ b/code/modules/cargo/markets/market_items/weapons.dm @@ -66,7 +66,7 @@ /datum/market_item/weapon/fisher name = "SC/FISHER Saboteur Handgun" - desc = "A self-recharging, compact pistol that disrupts flashlights and security cameras, if only temporarily. Also usable in melee." + desc = "A self-recharging, compact pistol that disrupts lights, cameras, APCs, turrets and more, if only temporarily. Also usable in melee." item = /obj/item/gun/energy/recharge/fisher price_min = CARGO_CRATE_VALUE * 2 diff --git a/code/modules/cargo/markets/market_telepad.dm b/code/modules/cargo/markets/market_telepad.dm index e99e4b88d223e..7a3365d7a0faf 100644 --- a/code/modules/cargo/markets/market_telepad.dm +++ b/code/modules/cargo/markets/market_telepad.dm @@ -26,13 +26,13 @@ /// The time it takes for the machine to recharge before being able to send or receive items. var/recharge_time = 0 /// Current recharge progress. - var/recharge_cooldown = 0 + COOLDOWN_DECLARE(recharge_cooldown) /// Base recharge time in seconds which is used to get recharge_time. var/base_recharge_time = 100 /// Current /datum/market_purchase being received. - var/receiving + var/datum/market_purchase/receiving /// Current /datum/market_purchase being sent to the target uplink. - var/transmitting + var/datum/market_purchase/transmitting /// Queue for purchases that the machine should receive and send. var/list/datum/market_purchase/queue = list() @@ -67,46 +67,48 @@ /obj/machinery/ltsrbt/proc/add_to_queue(datum/market_purchase/purchase) if(!recharge_cooldown && !receiving && !transmitting) receiving = purchase - return - queue += purchase + else + queue += purchase + RegisterSignal(receiving, COMSIG_QDELETING, PROC_REF(on_receiving_del)) + +/obj/machinery/ltsrbt/proc/on_receiving_del(datum/market_purchase/purchase) + SIGNAL_HANDLER + queue -= purchase + if(receiving == purchase) + receiving = null + if(transmitting == purchase) + transmitting = null /obj/machinery/ltsrbt/process(seconds_per_tick) if(machine_stat & NOPOWER) return - if(recharge_cooldown > 0) - recharge_cooldown -= seconds_per_tick + if(!COOLDOWN_FINISHED(src, recharge_cooldown) && isnull(receiving) && isnull(transmitting)) return - var/turf/T = get_turf(src) + COOLDOWN_START(src, recharge_cooldown, recharge_time) + + var/turf/turf = get_turf(src) if(receiving) - var/datum/market_purchase/P = receiving - P.item = P.entry.spawn_item(T) + receiving.item = receiving.entry.spawn_item(turf, receiving) use_power(power_usage_per_teleport / power_efficiency) var/datum/effect_system/spark_spread/sparks = new sparks.set_up(5, 1, get_turf(src)) - sparks.attach(P.item) + sparks.attach(receiving.item) sparks.start() + transmitting = receiving receiving = null - transmitting = P recharge_cooldown = recharge_time return - else if(transmitting) - var/datum/market_purchase/P = transmitting - if(!P.item) - QDEL_NULL(transmitting) - if(!(P.item in T.contents)) - QDEL_NULL(transmitting) - return - do_teleport(P.item, get_turf(P.uplink)) - use_power(power_usage_per_teleport / power_efficiency) + if(transmitting) + if(transmitting.item.loc == turf) + do_teleport(transmitting.item, get_turf(transmitting.uplink)) + use_power(power_usage_per_teleport / power_efficiency) QDEL_NULL(transmitting) - - recharge_cooldown = recharge_time return if(queue.len) diff --git a/code/modules/cargo/markets/market_uplink.dm b/code/modules/cargo/markets/market_uplink.dm index a82218082e90d..da86161e46ab2 100644 --- a/code/modules/cargo/markets/market_uplink.dm +++ b/code/modules/cargo/markets/market_uplink.dm @@ -9,7 +9,7 @@ var/viewing_category /// What market is currently being bought from by the uplink? var/viewing_market - /// What item is the current uplink attempting to buy? + /// the identifier of the item that the current uplink is attempting to buy var/selected_item /// Is the uplink in the process of buying the selected item? var/buying @@ -56,25 +56,31 @@ current_user = null data["categories"] = market ? market.categories : null data["delivery_methods"] = list() - if(market) - for(var/delivery in market.shipping) - data["delivery_methods"] += list(list("name" = delivery, "price" = market.shipping[delivery])) data["money"] = "N/A cr" if(current_user) data["money"] = current_user.account_balance data["buying"] = buying + if(buying && market) + var/datum/market_item/target_item = market.available_items[viewing_category][selected_item] + var/list/shipping_list = market.shipping + if(length(target_item?.shipping_override)) + shipping_list = target_item.shipping_override + for(var/delivery in shipping_list) + UNTYPED_LIST_ADD(data["delivery_methods"], list("name" = delivery, "price" = shipping_list[delivery])) data["items"] = list() - data["viewing_category"] = viewing_category + data["viewing_category"] = market.categories[viewing_category] ? viewing_category : null data["viewing_market"] = viewing_market if(viewing_category && market) if(market.available_items[viewing_category]) - for(var/datum/market_item/I in market.available_items[viewing_category]) + var/list/market_category = market.available_items[viewing_category] + for(var/id in market_category) + var/datum/market_item/item = market_category[id] data["items"] += list(list( - "id" = I.type, - "name" = I.name, - "cost" = I.price, - "amount" = I.stock, - "desc" = I.desc || I.name + "id" = id, + "name" = item.name, + "cost" = item.price, + "amount" = item.stock, + "desc" = item.desc || item.name )) return data @@ -123,8 +129,7 @@ if("select") if(isnull(params["item"])) return - var/item = text2path(params["item"]) - selected_item = item + selected_item = params["item"] buying = TRUE . = TRUE if("cancel") diff --git a/code/modules/cargo/packs/costumes_toys.dm b/code/modules/cargo/packs/costumes_toys.dm index 51fb4686038f5..e23e6112a4bfc 100644 --- a/code/modules/cargo/packs/costumes_toys.dm +++ b/code/modules/cargo/packs/costumes_toys.dm @@ -250,8 +250,9 @@ discountable = SUPPLY_PACK_STD_DISCOUNTABLE /datum/supply_pack/costumes_toys/stickers/fill(obj/structure/closet/crate/crate) - for(var/i in 1 to rand(1,2)) + for(var/i in 1 to rand(1, 2)) new /obj/item/storage/box/stickers(crate) + if(prob(30)) // a pair of googly eyes because funny new /obj/item/storage/box/stickers/googly(crate) diff --git a/code/modules/cargo/packs/imports.dm b/code/modules/cargo/packs/imports.dm index 781205eb03bf8..94849d61ef527 100644 --- a/code/modules/cargo/packs/imports.dm +++ b/code/modules/cargo/packs/imports.dm @@ -118,19 +118,6 @@ if(prob(10)) //A little extra sugar every now and then to shake things up. new /obj/item/switchblade(our_crate) -/datum/supply_pack/imports/blackmarket_telepad - name = "Black Market LTSRBT" - desc = "Need a faster and better way of transporting your illegal goods from and to the \ - station? Fear not, the Long-To-Short-Range-Bluespace-Transceiver (LTSRBT for short) \ - is here to help. Contains a LTSRBT circuit, two bluespace crystals, and one ansible." - cost = CARGO_CRATE_VALUE * 20 - contraband = TRUE - contains = list( - /obj/item/circuitboard/machine/ltsrbt, - /obj/item/stack/ore/bluespace_crystal/artificial = 2, - /obj/item/stock_parts/subspace/ansible, - ) - /datum/supply_pack/imports/contraband name = "'Contraband' Crate" desc = "Psst.. bud... want some contraband? I can get you a poster, some nice cigs, dank, even some \ diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm index 0b0073258298b..b8e93f2815c0d 100644 --- a/code/modules/cargo/packs/security.dm +++ b/code/modules/cargo/packs/security.dm @@ -330,8 +330,8 @@ /datum/supply_pack/security/armory/thermal name = "Thermal Pistol Crate" desc = "Contains a pair of holsters each with two experimental thermal pistols, \ - using nanites as the basis for their ammo." - cost = CARGO_CRATE_VALUE * 7 + using nanites as the basis for their ammo. Can be shaken to reload." + cost = CARGO_CRATE_VALUE * 10 contains = list(/obj/item/storage/belt/holster/energy/thermal = 2) crate_name = "thermal pistol crate" diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index 6b0517d07b272..8f814412fb305 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -90,6 +90,14 @@ delays = list(POD_TRANSIT = 20, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30) resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF +/obj/structure/closet/supplypod/back_to_station + name = "blood-red supply pod" + desc = "An intimidating supply pod, covered in the blood-red markings" + bluespace = TRUE + explosionSize = list(0,0,0,0) + style = STYLE_SYNDICATE + specialised = TRUE + /datum/armor/closet_supplypod melee = 30 bullet = 50 @@ -213,6 +221,24 @@ /obj/structure/closet/supplypod/open(mob/living/user, force = FALSE, special_effects = TRUE) return +///Called by the drop pods that return captured crewmembers from the ninja den. +/obj/structure/closet/supplypod/proc/return_from_capture(mob/living/victim, turf/destination = get_safe_random_station_turf()) + if(isnull(destination)) //Uuuuh, something went wrong. This is gonna hurt. + to_chat(victim, span_hypnophrase("A million voices echo in your head... \"Seems where you got sent won't \ + be able to handle our pod... as if we wanted the occupant to survive. Brace yourself, corporate dog.\"")) + flags_1 &= ~PREVENT_CONTENTS_EXPLOSION_1 + explosionSize = list(0,1,1,1) + destination = get_random_station_turf() + + do_sparks(8, FALSE, victim) + victim.visible_message(span_notice("[victim] vanishes...")) + + victim.forceMove(src) + + new /obj/effect/pod_landingzone(destination, src) + + SEND_SIGNAL(victim, COMSIG_LIVING_RETURN_FROM_CAPTURE, destination) + /obj/structure/closet/supplypod/proc/handleReturnAfterDeparting(atom/movable/holder = src) reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open_pod() ) bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index be8323a1186e2..c2e4b9c9ef7f6 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -40,9 +40,7 @@ /obj/item/clothing/head/utility/hardhat/Initialize(mapload) . = ..() AddElement(/datum/element/update_icon_updates_onmob) - -/obj/item/clothing/head/utility/hardhat/attack_self(mob/living/user) - toggle_helmet_light(user) + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) /obj/item/clothing/head/utility/hardhat/proc/toggle_helmet_light(mob/living/user) on = !on @@ -62,6 +60,15 @@ /obj/item/clothing/head/utility/hardhat/proc/turn_off(mob/user) set_light_on(FALSE) +/obj/item/clothing/head/utility/hardhat/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(on) + toggle_helmet_light() + return COMSIG_SABOTEUR_SUCCESS + +/obj/item/clothing/head/utility/hardhat/attack_self(mob/living/user) + toggle_helmet_light(user) + /obj/item/clothing/head/utility/hardhat/orange icon_state = "hardhat0_orange" inhand_icon_state = null diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index 64b83c076505e..860ac12d20209 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -78,6 +78,7 @@ . = ..() visor_toggling() update_appearance() + RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur)) /obj/item/clothing/head/helmet/space/plasmaman/examine() . = ..() @@ -108,6 +109,11 @@ playsound(src, 'sound/mecha/mechmove03.ogg', 50, TRUE) //Visors don't just come from nothing update_appearance() +/obj/item/clothing/head/helmet/space/plasmaman/update_icon_state() + . = ..() + icon_state = "[initial(icon_state)][helmet_on ? "-light":""]" + inhand_icon_state = icon_state + /obj/item/clothing/head/helmet/space/plasmaman/update_overlays() . = ..() . += visor_icon @@ -139,6 +145,7 @@ hitting_clothing.forceMove(src) update_appearance() +///By the by, helmets have the update_icon_updates_onmob element, so we don't have to call mob.update_worn_head() /obj/item/clothing/head/helmet/space/plasmaman/worn_overlays(mutable_appearance/standing, isinhands) . = ..() if(!isinhands && smile) @@ -161,9 +168,7 @@ /obj/item/clothing/head/helmet/space/plasmaman/attack_self(mob/user) helmet_on = !helmet_on - icon_state = "[initial(icon_state)][helmet_on ? "-light":""]" - inhand_icon_state = icon_state - user.update_worn_head() //So the mob overlay updates + update_appearance() if(helmet_on) if(!up) @@ -176,6 +181,14 @@ update_item_action_buttons() +/obj/item/clothing/head/helmet/space/plasmaman/proc/on_saboteur(datum/source, disrupt_duration) + SIGNAL_HANDLER + if(!helmet_on) + return + helmet_on = FALSE + update_appearance() + return COMSIG_SABOTEUR_SUCCESS + /obj/item/clothing/head/helmet/space/plasmaman/attack_hand_secondary(mob/user) ..() . = SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN diff --git a/code/modules/clothing/suits/shirt.dm b/code/modules/clothing/suits/shirt.dm index e291250a580a6..3d5c8fb4462db 100644 --- a/code/modules/clothing/suits/shirt.dm +++ b/code/modules/clothing/suits/shirt.dm @@ -21,6 +21,19 @@ washer.visible_message("[src] implodes due to repeated washing.") qdel(src) +/obj/item/clothing/suit/costume/wellworn_shirt/skub + name = "pro-skub shirt" + desc = "A worn out, curiously comfortable t-shirt proclaiming your pro-skub stance. Fuck those anti-skubbies." + icon_state = "wellworn_shirt_pro_skub" + greyscale_colors = "#FFFF4D" + greyscale_config = /datum/greyscale_config/wellworn_shirt_skub + greyscale_config_worn = /datum/greyscale_config/wellworn_shirt_skub/worn + +/obj/item/clothing/suit/costume/wellworn_shirt/skub/anti + name = "anti-skub shirt" + desc = "A worn out, curiously comfortable t-shirt proclaiming your anti-skub stance. Fuck those pro-skubbies." + icon_state = "wellworn_shirt_anti_skub" + /obj/item/clothing/suit/costume/wellworn_shirt/graphic name = "well-worn graphic shirt" desc = "A worn out, curiously comfortable t-shirt with a character from Phanic the Weasel on the front. It adds some charm points to itself and the wearer, and reminds you of when the series was still good; way back in 2500." diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm index 24d973e773d64..0a0348e7408a9 100644 --- a/code/modules/clothing/under/accessories/badges.dm +++ b/code/modules/clothing/under/accessories/badges.dm @@ -216,8 +216,8 @@ GLOBAL_LIST_INIT(pride_pin_reskins, list( /obj/item/clothing/accessory/anti_sec_pin/attach(obj/item/clothing/under/attach_to, mob/living/attacher) . = ..() - if (!.) - return FALSE + if (!. || isnull(attacher)) + return var/target = ishuman(attach_to.loc) ? attach_to.loc : attach_to log_combat(attacher, target, "pinned an 'arrest me immediately' pin onto", src) diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm index f247d11ef8484..8dee817f3e57f 100644 --- a/code/modules/fishing/fish/fish_traits.dm +++ b/code/modules/fishing/fish/fish_traits.dm @@ -213,9 +213,10 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list( /datum/fish_trait/revival/proc/check_status(obj/item/fish/source) SIGNAL_HANDLER if(source.status == FISH_DEAD) - addtimer(CALLBACK(src, PROC_REF(revive), source), rand(1 MINUTES, 2 MINUTES)) + addtimer(CALLBACK(src, PROC_REF(revive), WEAKREF(source)), rand(1 MINUTES, 2 MINUTES)) -/datum/fish_trait/revival/proc/revive(obj/item/fish/source) +/datum/fish_trait/revival/proc/revive(datum/weakref/fish_ref) + var/obj/item/fish/source = fish_ref.resolve() if(QDELETED(source) || source.status != FISH_DEAD) return source.set_status(FISH_ALIVE) diff --git a/code/modules/instruments/items.dm b/code/modules/instruments/items.dm index d9a7e2f3e8b18..dcc4ef8daba3d 100644 --- a/code/modules/instruments/items.dm +++ b/code/modules/instruments/items.dm @@ -34,20 +34,7 @@ user.visible_message(span_suicide("[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!")) return BRUTELOSS -/obj/item/instrument/attack_self(mob/user) - if(!ISADVANCEDTOOLUSER(user)) - to_chat(user, span_warning("You don't have the dexterity to do this!")) - return TRUE - interact(user) - -/obj/item/instrument/interact(mob/user) - ui_interact(user) - -/obj/item/instrument/ui_interact(mob/living/user) - if(!isliving(user) || user.stat != CONSCIOUS || (HAS_TRAIT(user, TRAIT_HANDS_BLOCKED) && !ispAI(user))) - return - - user.set_machine(src) +/obj/item/instrument/ui_interact(mob/user, datum/tgui/ui) song.ui_interact(user) /obj/item/instrument/violin @@ -130,9 +117,9 @@ . = ..() AddElement(/datum/element/spooky) -/obj/item/instrument/trumpet/spectral/attack(mob/living/carbon/C, mob/user) - playsound (src, 'sound/runtime/instruments/trombone/En4.mid', 100,1,-1) - ..() +/obj/item/instrument/trumpet/spectral/attack(mob/living/target_mob, mob/living/user, params) + playsound(src, 'sound/runtime/instruments/trombone/En4.mid', 1000, 1, -1) + return ..() /obj/item/instrument/saxophone name = "saxophone" @@ -154,9 +141,9 @@ . = ..() AddElement(/datum/element/spooky) -/obj/item/instrument/saxophone/spectral/attack(mob/living/carbon/C, mob/user) - playsound (src, 'sound/runtime/instruments/saxophone/En4.mid', 100,1,-1) - ..() +/obj/item/instrument/saxophone/spectral/attack(mob/living/target_mob, mob/living/user, params) + playsound(src, 'sound/runtime/instruments/trombone/En4.mid', 1000, 1, -1) + return ..() /obj/item/instrument/trombone name = "trombone" @@ -178,9 +165,9 @@ . = ..() AddElement(/datum/element/spooky) -/obj/item/instrument/trombone/spectral/attack(mob/living/carbon/C, mob/user) - playsound (src, 'sound/runtime/instruments/trombone/Cn4.mid', 100,1,-1) - ..() +/obj/item/instrument/trombone/spectral/attack(mob/living/target_mob, mob/living/user, params) + playsound(src, 'sound/runtime/instruments/trombone/Cn4.mid', 1000, 1, -1) + return ..() /obj/item/instrument/recorder name = "recorder" @@ -201,19 +188,24 @@ w_class = WEIGHT_CLASS_SMALL actions_types = list(/datum/action/item_action/instrument) -/obj/item/instrument/harmonica/proc/handle_speech(datum/source, list/speech_args) - SIGNAL_HANDLER - if(song.playing && ismob(loc)) - to_chat(loc, span_warning("You stop playing the harmonica to talk...")) - song.playing = FALSE - -/obj/item/instrument/harmonica/equipped(mob/M, slot) +/obj/item/instrument/harmonica/equipped(mob/user, slot, initial = FALSE) . = ..() - RegisterSignal(M, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + if(!(slot & slot_flags)) + return + RegisterSignal(user, COMSIG_MOB_SAY, PROC_REF(handle_speech)) -/obj/item/instrument/harmonica/dropped(mob/M) +/obj/item/instrument/harmonica/dropped(mob/user, silent = FALSE) . = ..() - UnregisterSignal(M, COMSIG_MOB_SAY) + UnregisterSignal(user, COMSIG_MOB_SAY) + +/obj/item/instrument/harmonica/proc/handle_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + if(!song.playing) + return + if(!ismob(loc)) + CRASH("[src] was still registered to listen in on [source] but was not found to be on their mob.") + to_chat(loc, span_warning("You stop playing the harmonica to talk...")) + song.playing = FALSE /datum/action/item_action/instrument name = "Use Instrument" diff --git a/code/modules/instruments/piano_synth.dm b/code/modules/instruments/piano_synth.dm index 71d0d96ef560d..8e107d494c779 100644 --- a/code/modules/instruments/piano_synth.dm +++ b/code/modules/instruments/piano_synth.dm @@ -148,7 +148,7 @@ stopped_playing.set_output(COMPONENT_SIGNAL) /obj/item/circuit_component/synth/proc/import_song() - synth.song.ParseSong(song.value) + synth.song.ParseSong(new_song = song.value) /obj/item/circuit_component/synth/proc/set_repetitions() synth.song.set_repeats(repetitions.value) @@ -169,7 +169,9 @@ synth.song.note_shift = clamp(note_shift.value, synth.song.note_shift_min, synth.song.note_shift_max) /obj/item/circuit_component/synth/proc/set_sustain_mode() - synth.song.sustain_mode = SSinstruments.note_sustain_modes[sustain_mode.value] + if(!(sustain_mode.value in SSinstruments.note_sustain_modes)) + return + synth.song.sustain_mode = sustain_mode.value /obj/item/circuit_component/synth/proc/set_sustain_value() switch(synth.song.sustain_mode) diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm index 68039df21468e..fb0e4f087449a 100644 --- a/code/modules/instruments/songs/_song.dm +++ b/code/modules/instruments/songs/_song.dm @@ -23,11 +23,6 @@ /// Are we currently playing? var/playing = FALSE - /// Are we currently editing? - var/editing = TRUE - /// Is the help screen open? - var/help = FALSE - /// Repeats left var/repeat = 0 /// Maximum times we can repeat @@ -107,7 +102,6 @@ var/note_shift = 0 var/note_shift_min = -100 var/note_shift_max = 100 - var/can_noteshift = TRUE /// The kind of sustain we're using var/sustain_mode = SUSTAIN_LINEAR /// When a note is considered dead if it is below this in volume @@ -129,7 +123,7 @@ tempo = sanitize_tempo(tempo, TRUE) src.parent = parent if(instrument_ids) - allowed_instrument_ids = islist(instrument_ids)? instrument_ids : list(instrument_ids) + allowed_instrument_ids = islist(instrument_ids) ? instrument_ids : list(instrument_ids) if(length(allowed_instrument_ids)) set_instrument(allowed_instrument_ids[1]) hearing_mobs = list() @@ -217,8 +211,6 @@ delay_by = 0 current_chord = 1 music_player = user - if(ismob(music_player)) - updateDialog(music_player) START_PROCESSING(SSinstruments, src) /** @@ -328,12 +320,6 @@ /datum/song/proc/set_bpm(bpm) tempo = sanitize_tempo(600 / bpm) -/** - * Updates the window for our users. Override down the line. - */ -/datum/song/proc/updateDialog(mob/user) - ui_interact(user) - /datum/song/process(wait) if(!playing) return PROCESS_KILL @@ -359,7 +345,6 @@ /datum/song/proc/set_volume(volume) src.volume = clamp(round(volume, 1), max(0, min_volume), min(100, max_volume)) update_sustain() - updateDialog() /** * Setter for setting how low the volume has to get before a note is considered "dead" and dropped @@ -367,7 +352,6 @@ /datum/song/proc/set_dropoff_volume(volume) sustain_dropoff_volume = clamp(round(volume, 0.01), INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100) update_sustain() - updateDialog() /** * Setter for setting exponential falloff factor. @@ -375,7 +359,6 @@ /datum/song/proc/set_exponential_drop_rate(drop) sustain_exponential_dropoff = clamp(round(drop, 0.00001), INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX) update_sustain() - updateDialog() /** * Setter for setting linear falloff duration. @@ -383,7 +366,6 @@ /datum/song/proc/set_linear_falloff_duration(duration) sustain_linear_duration = clamp(round(duration * 10, world.tick_lag), world.tick_lag, INSTRUMENT_MAX_TOTAL_SUSTAIN) update_sustain() - updateDialog() /datum/song/vv_edit_var(var_name, var_value) . = ..() @@ -401,9 +383,6 @@ // subtype for handheld instruments, like violin /datum/song/handheld -/datum/song/handheld/updateDialog(mob/user) - parent.ui_interact(user || usr) - /datum/song/handheld/should_stop_playing(atom/player) . = ..() if(. == STOP_PLAYING || . == IGNORE_INSTRUMENT_CHECKS) @@ -414,9 +393,6 @@ // subtype for stationary structures, like pianos /datum/song/stationary -/datum/song/stationary/updateDialog(mob/user) - parent.ui_interact(user || usr) - /datum/song/stationary/should_stop_playing(atom/player) . = ..() if(. == STOP_PLAYING || . == IGNORE_INSTRUMENT_CHECKS) diff --git a/code/modules/instruments/songs/editor.dm b/code/modules/instruments/songs/editor.dm index 58c0562c9b04d..927e03d055dcf 100644 --- a/code/modules/instruments/songs/editor.dm +++ b/code/modules/instruments/songs/editor.dm @@ -1,96 +1,189 @@ -/** - * Returns the HTML for the status UI for this song datum. - */ -/datum/song/proc/instrument_status_ui() - . = list() - . += "