From ad98a804cb3e5c2b4398198e33479a2b73b7ef82 Mon Sep 17 00:00:00 2001 From: Migratingcocofruit <69551563+Migratingcocofruit@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:54:10 +0300 Subject: [PATCH] Adds the alien cache ruin and a PTL terminal (#30367) * Make the cache room * adds alien cache machine * adds the cache properly * adds multitile component * adds terminal building and reward selection procs * adds laser receptacle file * Refactors ptl targetting code * more work on the ptl terminal * change file name to laser_terminal * changes area to powered makes laser terminal work properly * makes a new pickweight function because the old one is bad * Update alien_cache.dm * Update canister.dm * Update alien_cache.dm * adds a template of the ruin * adds some spawners to the reward pool * adjusts agent b amount * adjustments to loot table and changes to spawner comments * new placeholder sprite for the cache * randomizes reward spawn location and makes a PTL terminal sprite * updates alien cache sprite and examine proc * Adds overlays to the cache to indicate the current progress * fixes mob capsule spawner and laser terminal * Adds animations to PTL terminal * new box part of the sprite for the PTL terminal. Adds glow * fixes untargetting the PTL terminal and adds a little light overlay to the receiving dish * changes PTL terminal id generation slightly * adds simon says. incomplete * Update paradise.dme * simon says works now * adds more rhythms * Adds success and failure sounds * Makes the ptl terminal board printable * Update secure_alien_airlock.dm * undo changes to preferences.dm * undefs things * removes identical to parent vars * Update alien_cache_random_spawners.dm * replaces animation end loop flag so open dream CI doesn't give errors * changes centre platform colour and adds a fan to the entrance * more pads more sounds * better sprites * remove old sprite * adds the cache to example config * makes it not always spawn * Update simon_says_32x32.dmi * adds some flavor to the ruin * Update alien_cache_site.dmm * adds a signal to the ruin --- .../SpaceRuins/alien_cache_site.dmm | 1195 +++++++++++++++++ code/__DEFINES/_math.dm | 5 +- code/__HELPERS/lists.dm | 22 + .../mapping/templates/space_map_templates.dm | 8 + .../areas/ruins/alien_cache_site_areas.dm | 7 + code/game/atoms.dm | 35 + .../random/alien_cache_random_spawners.dm | 150 +++ .../transmission_laser.dm | 38 +- .../machinery/portable/canister.dm | 2 +- .../hostile/megafauna/megafauna.dm | 30 + code/modules/power/alien_cache.dm | 259 ++++ code/modules/power/laser_terminal.dm | 108 ++ .../research/designs/machine_designs.dm | 10 + .../objects_and_mobs/secure_alien_airlock.dm | 15 + .../ruins/objects_and_mobs/simon_says.dm | 184 +++ config/example/config.toml | 1 + icons/effects/simon_says_32x32.dmi | Bin 0 -> 423 bytes icons/obj/machines/alien_cache.dmi | Bin 0 -> 1584 bytes icons/obj/machines/ptl_terminal.dmi | Bin 0 -> 1676 bytes paradise.dme | 6 + sound/effects/simon_says_1.ogg | Bin 0 -> 3862 bytes sound/effects/simon_says_2.ogg | Bin 0 -> 3795 bytes sound/effects/simon_says_3.ogg | Bin 0 -> 3769 bytes sound/effects/simon_says_4.ogg | Bin 0 -> 3757 bytes sound/effects/simon_says_5.ogg | Bin 0 -> 3779 bytes sound/effects/simon_says_6.ogg | Bin 0 -> 3844 bytes sound/effects/simon_says_success.ogg | Bin 0 -> 7265 bytes sound/effects/simon_says_wrong.ogg | Bin 0 -> 4609 bytes 28 files changed, 2050 insertions(+), 25 deletions(-) create mode 100644 _maps/map_files/RandomRuins/SpaceRuins/alien_cache_site.dmm create mode 100644 code/game/area/areas/ruins/alien_cache_site_areas.dm create mode 100644 code/game/objects/effects/spawners/random/alien_cache_random_spawners.dm create mode 100644 code/modules/power/alien_cache.dm create mode 100644 code/modules/power/laser_terminal.dm create mode 100644 code/modules/ruins/objects_and_mobs/secure_alien_airlock.dm create mode 100644 code/modules/ruins/objects_and_mobs/simon_says.dm create mode 100644 icons/effects/simon_says_32x32.dmi create mode 100644 icons/obj/machines/alien_cache.dmi create mode 100644 icons/obj/machines/ptl_terminal.dmi create mode 100644 sound/effects/simon_says_1.ogg create mode 100644 sound/effects/simon_says_2.ogg create mode 100644 sound/effects/simon_says_3.ogg create mode 100644 sound/effects/simon_says_4.ogg create mode 100644 sound/effects/simon_says_5.ogg create mode 100644 sound/effects/simon_says_6.ogg create mode 100644 sound/effects/simon_says_success.ogg create mode 100644 sound/effects/simon_says_wrong.ogg diff --git a/_maps/map_files/RandomRuins/SpaceRuins/alien_cache_site.dmm b/_maps/map_files/RandomRuins/SpaceRuins/alien_cache_site.dmm new file mode 100644 index 00000000000..0154741ff81 --- /dev/null +++ b/_maps/map_files/RandomRuins/SpaceRuins/alien_cache_site.dmm @@ -0,0 +1,1195 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"cK" = ( +/obj/structure/closet, +/obj/item/soap, +/obj/item/soap, +/obj/item/clothing/under/rank/cargo/expedition/skirt, +/obj/item/clothing/under/rank/cargo/expedition/skirt, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"dV" = ( +/obj/structure/lightable/bonfire, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"ec" = ( +/obj/machinery/hydroponics/soil, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"en" = ( +/obj/structure/table/abductor, +/obj/item/kitchen/knife, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"et" = ( +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site/cache_room) +"eF" = ( +/obj/effect/decal/cleanable/dirt, +/obj/item/pickaxe, +/obj/item/weldingtool/largetank, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"fA" = ( +/obj/structure/fans/tiny/invisible, +/obj/machinery/door/airlock/abductor/secure, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"gf" = ( +/obj/effect/spawner/random/glowstick, +/obj/structure/table/abductor, +/obj/effect/spawner/random/engineering/tools, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"hj" = ( +/obj/effect/spawner/random/snacks, +/obj/effect/spawner/random/snacks, +/obj/effect/spawner/random/snacks, +/obj/structure/table/wood, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"jZ" = ( +/obj/item/reagent_containers/glass/bottle/nutrient/empty, +/obj/item/reagent_containers/glass/bucket, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"kY" = ( +/obj/effect/simon_says, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"ml" = ( +/obj/structure/table/abductor, +/obj/item/reagent_containers/glass/bucket, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"mQ" = ( +/turf/space, +/area/space) +"ok" = ( +/obj/effect/spawner/random/food_or_drink, +/obj/effect/spawner/random/food_or_drink, +/obj/structure/table/wood, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"pX" = ( +/obj/item/assembly/igniter, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"uD" = ( +/obj/structure/bed/abductor, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"wy" = ( +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"zB" = ( +/obj/structure/closet, +/obj/item/clothing/under/rank/cargo/expedition, +/obj/item/clothing/under/rank/cargo/expedition, +/obj/item/soap, +/obj/item/reagent_containers/glass/bucket, +/obj/item/reagent_containers/glass/bucket, +/obj/item/reagent_containers/glass/bucket, +/obj/item/gps{ + gpstag = "ALN0"; + tracking = 1 + }, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"Bl" = ( +/obj/structure/reagent_dispensers/watertank, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"CK" = ( +/turf/simulated/wall/indestructible/abductor, +/area/ruin/powered/alien_cache_site) +"Do" = ( +/obj/machinery/shower/directional/east, +/obj/structure/curtain/open/shower, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"Ey" = ( +/obj/machinery/alien_cache, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site/cache_room) +"Ha" = ( +/turf/simulated/wall/indestructible/abductor, +/area/ruin/powered/alien_cache_site/cache_room) +"JC" = ( +/obj/structure/curtain/open, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"LK" = ( +/obj/effect/mapping_helpers/turfs/damage, +/turf/simulated/wall/indestructible/abductor{ + desc = "Effectively impervious to conventional methods of destruction. It looks like someone tried to break it, but failed" + }, +/area/ruin/powered/alien_cache_site) +"TW" = ( +/turf/simulated/wall/mineral/wood, +/area/ruin/powered/alien_cache_site) +"Yg" = ( +/obj/machinery/door/airlock/abductor, +/obj/structure/fans/tiny/invisible, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) +"YA" = ( +/obj/effect/spawner/random/engineering/toolbox, +/obj/structure/table/abductor, +/turf/simulated/floor/plating/abductor, +/area/ruin/powered/alien_cache_site) + +(1,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(2,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(3,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(4,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(5,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(6,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(7,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(8,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(9,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(10,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +CK +CK +CK +CK +CK +CK +CK +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(11,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +CK +CK +wy +wy +hj +ok +pX +dV +CK +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(12,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +CK +gf +wy +wy +wy +wy +wy +wy +ml +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(13,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +Ha +Ha +Ha +Ha +Ha +CK +YA +wy +wy +wy +wy +wy +wy +en +CK +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(14,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +Ha +Ha +Ha +et +et +et +Ha +CK +wy +wy +wy +wy +wy +wy +wy +wy +wy +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(15,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +Ha +et +et +et +et +et +et +LK +eF +wy +wy +wy +wy +wy +wy +wy +wy +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(16,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +Ha +Ey +et +et +et +et +et +fA +wy +wy +wy +wy +wy +wy +wy +kY +wy +Yg +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(17,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +Ha +et +et +et +et +et +et +CK +wy +wy +wy +wy +wy +wy +wy +wy +jZ +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(18,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +Ha +Ha +Ha +et +et +et +Ha +CK +wy +wy +wy +wy +wy +wy +wy +ec +Bl +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(19,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +Ha +Ha +Ha +Ha +Ha +CK +TW +TW +JC +TW +JC +TW +TW +TW +CK +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(20,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +CK +zB +wy +wy +TW +wy +wy +wy +Do +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(21,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +CK +CK +uD +uD +TW +uD +uD +cK +CK +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(22,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +CK +CK +CK +CK +CK +CK +CK +CK +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(23,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(24,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(25,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(26,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(27,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(28,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(29,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(30,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} +(31,1,1) = {" +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +mQ +"} diff --git a/code/__DEFINES/_math.dm b/code/__DEFINES/_math.dm index 9e6a3e8be9a..4a6a54d626a 100644 --- a/code/__DEFINES/_math.dm +++ b/code/__DEFINES/_math.dm @@ -209,8 +209,11 @@ #define LORENTZ_DISTRIBUTION(x, s) ( s*tan(TODEGREES(PI*(rand()-0.5))) + x ) #define LORENTZ_CUMULATIVE_DISTRIBUTION(x, y, s) ( (1/PI)*TORADIANS(arctan((x-y)/s)) + 1/2 ) - #define RULE_OF_THREE(a, b, x) ((a*x)/b) + +/// Returns probability based on deviation from the mean(m) and sigma(s) +#define normal_distribution(x, m, s) ((1 / ((2 * PI * s**2)**0.5)) * NUM_E ** -(((x - m) ** 2) / (2 * s ** 2))) + // ) /proc/RaiseToPower(num, power) diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index 414926c99ca..d00977bfb74 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -293,6 +293,28 @@ return null +/** + * Picks an element based on its weight. Weight can be any real number + * L - The input list + * + * example: list("a" = 0.33, "b" = 0.67) will have a 67% chance to pick "b" + */ +/proc/pickweight_fraction(list/L) + var/total = 0 + var/item + for(item in L) + if(L[item] < 0) + continue + total += L[item] + + total = rand() * total + for(item in L) + total -=L [item] + if(total <= 0) + return item + + return null + //Pick a random element from the list and remove it from the list. /proc/pick_n_take(list/listfrom) if(length(listfrom) > 0) diff --git a/code/datums/mapping/templates/space_map_templates.dm b/code/datums/mapping/templates/space_map_templates.dm index e81ac14e3d6..4c32628448d 100644 --- a/code/datums/mapping/templates/space_map_templates.dm +++ b/code/datums/mapping/templates/space_map_templates.dm @@ -400,3 +400,11 @@ description = "Nanotrasen uses relays like these to further extend telecommunications around an area of space, as well as long range beacons for easier deployment in the future. \ Unfortunately, the anomalous activity around Epsilon Eridani, along with orbital debris and space-faring hostiles, has rendered many of these relay stations inoperable, leaving their communications and teleportation systems offline." never_spawn_on_the_same_level = list("bluespace_relay_beacon") + +/datum/map_template/ruin/space/alien_cache_site + id = "alien_cache_site" + suffix = "alien_cache_site.dmm" + name = "Alien Cache Site" + description = "A site containing a cache of alien design, who knows what valuable technology is hiding inside?" + never_spawn_on_the_same_level = list("alien_cache_site") + allow_duplicates = FALSE diff --git a/code/game/area/areas/ruins/alien_cache_site_areas.dm b/code/game/area/areas/ruins/alien_cache_site_areas.dm new file mode 100644 index 00000000000..d1a492d3bbc --- /dev/null +++ b/code/game/area/areas/ruins/alien_cache_site_areas.dm @@ -0,0 +1,7 @@ +/area/ruin/powered/alien_cache_site + name = "Alien Cache Site" + report_alerts = FALSE + dynamic_lighting = FALSE + +/area/ruin/powered/alien_cache_site/cache_room + name = "Alien Cache Chamber" diff --git a/code/game/atoms.dm b/code/game/atoms.dm index d435c5bfc1e..893d04bb7af 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1510,3 +1510,38 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) attack_info.last_attacker_ckey = attacker.ckey if(istype(weapon)) attack_info.last_attacker_weapon = "[weapon] ([weapon.type])" + +// MARK: PTL PROCS + +// Called when the target is selected +/atom/proc/on_ptl_target(obj/machinery/power/transmission_laser/ptl) + if(ptl.firing) + on_ptl_fire() + return + +// Called for each process of the PTL +/atom/proc/on_ptl_tick(obj/machinery/power/transmission_laser/ptl, output_level) + return + +// Called when no longer targeted by the ptl +/atom/proc/on_ptl_untarget(obj/machinery/power/transmission_laser/ptl) + return + +// Called when the PTL starts firing on the target +/atom/proc/on_ptl_fire(obj/machinery/power/transmission_laser/ptl) + return + +// Called when the PTL stops firing on the target +/atom/proc/on_ptl_stop(obj/machinery/power/transmission_laser/ptl) + return + +// Used in the PTL ui +/atom/proc/ptl_data() + return name + +// Called if an atom untargets itself +/atom/proc/untarget_self(obj/machinery/power/transmission_laser/ptl) + on_ptl_untarget(ptl) + if(ptl) + ptl.target = null + return diff --git a/code/game/objects/effects/spawners/random/alien_cache_random_spawners.dm b/code/game/objects/effects/spawners/random/alien_cache_random_spawners.dm new file mode 100644 index 00000000000..b5e216b2aa5 --- /dev/null +++ b/code/game/objects/effects/spawners/random/alien_cache_random_spawners.dm @@ -0,0 +1,150 @@ +// Canister +#define COMPRESSED_GAS_AMOUNT 300000 +#define AGENT_B_AMOUNT 100000 +#define NAME_O2 "O2" +#define NAME_CO2 "CO2" +#define NAME_N2 "N2" +#define NAME_N2O "N2O" +#define NAME_TOX "Plasma" +#define NAME_AGENT_B "Agent B" +// Lazarus Capsule +#define MOB_TYPE (1 << 0) +#define MOB_PROTOTYPE (1 << 1) + +// MARK: Random Spanwers + +/obj/effect/spawner/random/alien_cache + name = "alien cache loot spawner" + +// Random alien tools +/obj/effect/spawner/random/alien_cache/alien_tool + name = "alien tool spawner" + loot = list( + /obj/item/screwdriver/abductor, + /obj/item/wrench/abductor, + /obj/item/multitool/abductor, + /obj/item/wirecutters/abductor, + /obj/item/crowbar/abductor, + + ) + +// Random alien surgical tools +/obj/effect/spawner/random/alien_cache/alien_surgical_tool + name = "alien surgical tool spawner" + loot = list( + /obj/item/bonegel/alien, + /obj/item/scalpel/laser/alien, + /obj/item/bonesetter/alien, + /obj/item/circular_saw/alien, + /obj/item/hemostat/alien, + /obj/item/fix_o_vein/alien, + /obj/item/retractor/alien, + ) + +// Random alien tool implant +/obj/effect/spawner/random/alien_cache/alien_tool_implant + name = "alien tool implant spawner" + loot = list( + /obj/item/organ/internal/cyberimp/arm/toolset_abductor, + /obj/item/organ/internal/cyberimp/arm/toolset_abductor/l, + /obj/item/organ/internal/cyberimp/arm/janitorial_abductor, + /obj/item/organ/internal/cyberimp/arm/janitorial_abductor/l, + /obj/item/organ/internal/cyberimp/arm/surgical_abductor, + /obj/item/organ/internal/cyberimp/arm/surgical_abductor/l, + ) + +// Random alien tool borg upgrade +/obj/effect/spawner/random/alien_cache/alien_tool_borg + name = "alien borg tool upgrade" + loot = list( + /obj/item/borg/upgrade/abductor_engi, + /obj/item/borg/upgrade/abductor_medi, + /obj/item/borg/upgrade/abductor_jani, + ) + +//MARK: Canister Spawners + +// Gas canister with random gas +/obj/effect/spawner/alien_cache/gas_canister + name = "random gas canister spawner" + var/list/gasses = list( + NAME_O2 = COMPRESSED_GAS_AMOUNT, + NAME_N2 = COMPRESSED_GAS_AMOUNT, + NAME_CO2 = COMPRESSED_GAS_AMOUNT, + NAME_TOX = COMPRESSED_GAS_AMOUNT, + NAME_N2O = COMPRESSED_GAS_AMOUNT) + +// Agent B canister +/obj/effect/spawner/alien_cache/gas_canister/agent_b + name = "agent b canister spawner" + gasses = list(NAME_AGENT_B = AGENT_B_AMOUNT) + +/obj/effect/spawner/alien_cache/gas_canister/proc/add_gas(obj/machinery/atmospherics/portable/canister/spawned) + var/picked = pick(gasses) + switch(picked) + if(NAME_O2) + spawned.air_contents.set_oxygen(gasses["[picked]"]) + if(NAME_N2) + spawned.air_contents.set_nitrogen(gasses["[picked]"]) + if(NAME_N2O) + spawned.air_contents.set_sleeping_agent(gasses["[picked]"]) + if(NAME_CO2) + spawned.air_contents.set_carbon_dioxide(gasses["[picked]"]) + if(NAME_TOX) + spawned.air_contents.set_toxins(gasses["[picked]"]) + if(NAME_AGENT_B) + spawned.air_contents.set_agent_b(gasses["[picked]"]) + +/obj/effect/spawner/alien_cache/gas_canister/Initialize(mapload) + . = ..() + var/obj/machinery/atmospherics/portable/canister/spawned = new(get_turf(src)) + add_gas(spawned) + update_icon() + qdel(src) + +// MARK: Lazarus Spawners +// Lazarus Capsule with a mob inside +/obj/effect/spawner/alien_cache/mob_capsule + var/list/mob_types = list() + +// Lazarus Capsule with a megafauna inside +/obj/effect/spawner/alien_cache/mob_capsule/megafauna + mob_types = list(/mob/living/simple_animal/hostile/megafauna = MOB_PROTOTYPE) + +/obj/effect/spawner/alien_cache/mob_capsule/Initialize(mapload) + . = ..() + var/obj/item/mobcapsule/capsule = new(get_turf(src)) + var/type + if(length(mob_types)) + var/list/pool = list() + for(var/mob_type in mob_types) + if(mob_types[mob_type] & MOB_TYPE) + pool += mob_type + if(mob_types[mob_type] & MOB_PROTOTYPE) + pool += subtypesof(mob_type) + type = pick(pool) + else + type = pick(subtypesof(/mob/living/simple_animal)) + var/mob/living/simple_animal/my_mob = new type(loc) + my_mob.faction = list("neutral") + capsule.captured = my_mob + my_mob.forceMove(capsule) + +// TEG crate for the cache +/obj/structure/closet/crate/teg + +/obj/structure/closet/crate/teg/populate_contents() + new /obj/machinery/power/teg(src) + new /obj/item/pipe/circulator(src) + new /obj/item/pipe/circulator(src) + +#undef COMPRESSED_GAS_AMOUNT +#undef AGENT_B_AMOUNT +#undef NAME_O2 +#undef NAME_CO2 +#undef NAME_N2 +#undef NAME_N2O +#undef NAME_TOX +#undef NAME_AGENT_B +#undef MOB_TYPE +#undef MOB_PROTOTYPE diff --git a/code/goonstation/modules/power/power_transmission_laser/transmission_laser.dm b/code/goonstation/modules/power/power_transmission_laser/transmission_laser.dm index 58a6a32f051..ec2eeda3ab3 100644 --- a/code/goonstation/modules/power/power_transmission_laser/transmission_laser.dm +++ b/code/goonstation/modules/power/power_transmission_laser/transmission_laser.dm @@ -71,10 +71,8 @@ /area/mine/outpost, /area/shuttle/mining, ) - /// Megafauna being targeted - var/mob/living/simple_animal/hostile/megafauna/target - /// Overlay that goes over the mob that gets beamed - var/image/orbital_strike + /// PTL target + var/atom/target /obj/machinery/power/transmission_laser/north pixel_x = -64 @@ -276,7 +274,7 @@ data["accepting_power"] = turned_on data["sucking_power"] = inputting data["firing"] = firing - data["target"] = target ? target.internal_gps.gpstag : "" + data["target"] = target ? target.ptl_data() : "" data["power_format"] = power_format_multi data["input_number"] = input_number @@ -338,23 +336,24 @@ for(var/area_type in targetable_areas) if(istype(boss_loc, area_type)) target_list[monster.internal_gps.gpstag] = monster + for(var/obj/machinery/power/laser_terminal/receptacle in GLOB.laser_terminals) + target_list["[receptacle.name]: [receptacle.id]"] = receptacle // Target CC to sell power target_list["Collection Terminal"] = null var/choose = tgui_input_list(user, "Select target", "Target", target_list) if(!choose) return + untarget() target = target_list[choose] - RegisterSignal(target, COMSIG_MOB_DEATH, PROC_REF(untarget)) - if(firing && target) - orbital_strike = image(target.icon, target, "orbital_strike", FLY_LAYER, SOUTH) - target.add_overlay(orbital_strike) + if(target) + target.on_ptl_target(src) /// Stop targeting a mob once it dies /obj/machinery/power/transmission_laser/proc/untarget() - SIGNAL_HANDLER - target.cut_overlay(orbital_strike) - UnregisterSignal(target, COMSIG_MOB_DEATH) + SIGNAL_HANDLER // This can be called via various signals depending on what we register on the a target + if(target) + target.on_ptl_untarget(src) target = null /obj/machinery/power/transmission_laser/process() @@ -394,14 +393,8 @@ if(!target) sell_power(output_level * WATT_TICK_TO_JOULE) else - if(!QDELETED(target)) // Just for safety. - target.loot = list() // disable loot drops form the target to prevent cheese - if(10 * output_level * target.damage_coeff[BURN] / (1 MW) > target.health) // If we would kill the target dust it. - target.health = 0 // We need this so can_die() won't prevent dusting - visible_message("\The [src] is reduced to dust by the beam!") - target.dust() - else - target.adjustFireLoss(10 * output_level / (1 MW)) + if(!QDELETED(target)) // Make sure our target still exists + INVOKE_ASYNC(target, TYPE_PROC_REF(/atom, on_ptl_tick), src, output_level) else target = null if(output_level > EYE_DAMAGE_THRESHOLD) @@ -481,8 +474,7 @@ /obj/machinery/power/transmission_laser/proc/setup_lasers() if(target) - orbital_strike = image(target.icon, target, "orbital_strike", FLY_LAYER, SOUTH) - target.add_overlay(orbital_strike) + target.on_ptl_fire() var/turf/last_step = get_step(get_front_turf(), dir) for(var/num in 1 to range) if(!(locate(/obj/effect/transmission_beam) in last_step)) @@ -495,7 +487,7 @@ /obj/machinery/power/transmission_laser/proc/destroy_lasers() if(target) - target.cut_overlay(orbital_strike) + target.on_ptl_stop() for(var/obj/effect/transmission_beam/listed_beam as anything in laser_effects) laser_effects -= listed_beam qdel(listed_beam) diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index 19c95eec056..475134099ef 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -439,12 +439,12 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) name = "Canister \[Air\]" icon_state = "grey" //See Initialize() can_label = FALSE + /obj/machinery/atmospherics/portable/canister/custom_mix name = "Canister \[Custom\]" icon_state = "whiters" //See Initialize() can_label = FALSE - /obj/machinery/atmospherics/portable/canister/toxins/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index eae50b6984e..dc9aa895a7a 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -193,6 +193,36 @@ difficulty_ore_modifier -= 4 enraged = FALSE +// MARK: PTL Interaction +/mob/living/simple_animal/hostile/megafauna/on_ptl_target(obj/machinery/power/transmission_laser/ptl) + ptl.RegisterSignal(src, COMSIG_MOB_DEATH, TYPE_PROC_REF(/obj/machinery/power/transmission_laser, untarget), ptl) + if(ptl?.firing) + on_ptl_fire(ptl) + return + +/mob/living/simple_animal/hostile/megafauna/on_ptl_tick(obj/machinery/power/transmission_laser/ptl, output_level) + loot = list() // disable loot drops form the target to prevent cheese + if(10 * output_level * damage_coeff[BURN] / (1 MW) > health) // If we would kill the target dust it. + health = 0 // We need this so can_die() won't prevent dusting + visible_message("\The [src] is reduced to dust by the beam!") + dust() + else + adjustFireLoss(10 * output_level / (1 MW)) + +/mob/living/simple_animal/hostile/megafauna/on_ptl_untarget(obj/machinery/power/transmission_laser/ptl) + on_ptl_stop(ptl) + if(ptl) + ptl.UnregisterSignal(src, COMSIG_MOB_DEATH) + +/mob/living/simple_animal/hostile/megafauna/on_ptl_fire(obj/machinery/power/transmission_laser/ptl) + var/orbital_strike = image(icon, src, "orbital_strike", FLY_LAYER, SOUTH) + add_overlay(orbital_strike) + +/mob/living/simple_animal/hostile/megafauna/on_ptl_stop(obj/machinery/power/transmission_laser/ptl) + for(var/image/overlay in overlays) + if(overlay.name == "orbital_strike") + cut_overlay(overlay) + /mob/living/simple_animal/hostile/megafauna/DestroySurroundings() . = ..() for(var/turf/simulated/floor/chasm/C in circlerangeturfs(src, 1)) diff --git a/code/modules/power/alien_cache.dm b/code/modules/power/alien_cache.dm new file mode 100644 index 00000000000..6e302053b06 --- /dev/null +++ b/code/modules/power/alien_cache.dm @@ -0,0 +1,259 @@ +#define CACHE_MAX_LEVEL 10 +#define LEVEL_DISTRIBUTION_SIGMA 0.5 +#define LEVEL_REQUIREMENT(level) ((300 MJ) * ((level) ** 3)) + +/obj/machinery/alien_cache + name = "Alien Cache" + desc = "A strange storage device of alien design" + icon = 'icons/obj/machines/alien_cache.dmi' + icon_state = "cache_0" + base_icon_state = "cache_0" + pixel_x = -32 + pixel_y = -32 + density = TRUE + anchored = TRUE + // Alien stuff is pretty tough + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + /// The maximum reachable level + var/max_level = CACHE_MAX_LEVEL + /// The deepest level we opened + var/level_reached = 0 + /// The total amount of energy consumed + var/total_energy = 0 + /// Assoc list of level to rewards found at that level with a weight for each + var/list/random_rewards = list( + // level 1 + list( + /obj/item/stack/sheet/mineral/abductor/fifty = 1, + /obj/machinery/the_singularitygen/tesla = 0.3, + /obj/machinery/the_singularitygen = 0.3, + /obj/item/stack/sheet/mineral/bananium/thirty = 1, + /obj/item/stack/sheet/mineral/tranquillite/thirty = 1, + ), + // level 2 + list( + /obj/effect/spawner/random/alien_cache/alien_surgical_tool = 1, + /obj/item/mod/module/dispenser = 1, + /obj/effect/spawner/alien_cache/mob_capsule = 1, + /obj/item/storage/pill_bottle/random_meds/labelled = 1, + ), + // level 3 + list( + /obj/item/mod/module/jetpack/advanced = 1, + /obj/effect/spawner/alien_cache/gas_canister = 1, + /obj/effect/spawner/random/alien_cache/alien_tool_borg = 1, + ), + // level 4 + list( + /obj/effect/spawner/random/alien_cache/alien_tool = 1, + /obj/item/assembly/signaler/anomaly/random = 1, + /obj/effect/spawner/alien_cache/gas_canister/agent_b = 1, + ), + // level 5 + list( + /obj/item/storage/belt/military/abductor/full = 1, + /obj/item/guardiancreator/biological = 1, + ), + ) + /// List of guaranteed rewards you get from the last stage + var/list/open_rewards = list( + /obj/item/circuitboard/machine/bluespace_tap = 1, + ) + /// Terminal for receiving power through + var/obj/machinery/power/terminal/terminal + +/obj/machinery/alien_cache/Initialize(mapload) + . = ..() + AddComponent(/datum/component/multitile, list( + list(1, MACH_CENTER, 1), + list(1, 0, 1), + )) + +/obj/machinery/alien_cache/update_overlays() + . = ..() + var/my_alpha = 0 + for(var/cache_level in 1 to CACHE_MAX_LEVEL) + if(total_energy <= 0 || cache_level > level_reached + 1) + break + if(cache_level < level_reached + 1) + my_alpha = 255 + if(cache_level == level_reached + 1) + my_alpha = clamp(1 - ((LEVEL_REQUIREMENT(cache_level) - total_energy) / (LEVEL_REQUIREMENT(cache_level + 1) - LEVEL_REQUIREMENT(cache_level))), 0, 1) + my_alpha *= 255 + . += mutable_appearance(icon, "cache_level_[cache_level]", alpha = my_alpha) + +/obj/machinery/alien_cache/examine(mob/user) + . = ..() + if(panel_open) + . += "The panel is open, revealing unterminated internal wiring." + else + . += "There's a loose panel on the front that could be pried open with a screwdriver" + +/obj/machinery/alien_cache/display_parts(user) + return list("ERROR: UNIDENTIFIED MACHINE DESIGN") + +/// generate a list with weights for tiers of loot depending on the reached level +/obj/machinery/alien_cache/proc/tier_weights() + var/list/tiers = list() + // Generate a weighted list of all possible tiers using normal distribution + var/avg_tier = (length(random_rewards) * level_reached / max_level) + for(var/i in 1 to length(random_rewards)) + var/tier_prob = normal_distribution(i, avg_tier, LEVEL_DISTRIBUTION_SIGMA) + if(tier_prob > 0) + tiers["[i]"] = tier_prob + + return tiers + +/// Returns a list of rewards with length equal to amount +/obj/machinery/alien_cache/proc/pick_rewards(amount = 1) + . = list() + if(!amount || amount < 1) + return + + var/list/tiers = tier_weights() + + for(var/i in 1 to amount) + var/selected_level = text2num(pickweight_fraction(tiers)) + if(length(random_rewards[selected_level])) + . += pickweight_fraction(random_rewards[selected_level]) + else + . += "no rewards in level [selected_level]" + +/obj/machinery/alien_cache/process() + if(terminal && level_reached < max_level) + var/available = terminal.get_surplus() + terminal.consume_direct_power(available) + total_energy += available * WATT_TICK_TO_JOULE + if(total_energy >= LEVEL_REQUIREMENT(level_reached + 1)) + level_reached++ + var/list/rewards = pick_rewards(3) + to_chat(world, "Level: [level_reached]\nRewards: [english_list(rewards)]") + spawn_loot(rewards) + if(level_reached >= max_level) + spawn_loot(open_rewards) + update_icon(UPDATE_OVERLAYS) + + +/obj/machinery/alien_cache/proc/spawn_loot(list/loot) + var/spawn_side = prob(50) ? WEST : EAST + if(dir & (EAST | WEST)) + // We can turn EAST/WEST into NORTH/SOUTH simply by right shifting + spawn_side = spawn_side >> 2 + var/turf/spawnloc = get_step(get_step(get_step(src, dir), dir), spawn_side) + new /obj/effect/portal(spawnloc, null, src, 10) + for(var/lootpath in loot) + new lootpath(spawnloc) + +/// Items interaction mostly stolen from SMES +/obj/machinery/alien_cache/item_interaction(mob/living/user, obj/item/used, list/modifiers) + // Building and linking a terminal + if(istype(used, /obj/item/stack/cable_coil)) + var/dir = get_dir(user, src) + if(dir & (dir - 1)) // Checks for diagonal interaction + return ITEM_INTERACT_COMPLETE + + if(terminal) // Checks for an existing terminal + to_chat(user, "[src] already has a terminal!") + return ITEM_INTERACT_COMPLETE + + if(!panel_open) // Checks to see if the panel is closed + to_chat(user, "You must open the panel first!") + return ITEM_INTERACT_COMPLETE + + var/turf/T = get_turf(user) + if(T.intact) // Checks to see if floor plating is present + to_chat(user, "You must first remove the floor plating!") + return ITEM_INTERACT_COMPLETE + + var/obj/item/stack/cable_coil/C = used + if(C.get_amount() < 10) + to_chat(user, "You need more wires.") + return ITEM_INTERACT_COMPLETE + + if(user.loc == loc) + to_chat(user, "You must not be on the same tile as [src].") + return ITEM_INTERACT_COMPLETE + + // Direction the terminal will face to + var/turf/temporary_location = get_step(src, REVERSE_DIR(dir)) + + if(isspaceturf(temporary_location)) + to_chat(user, "You can't build a terminal on space.") + return ITEM_INTERACT_COMPLETE + + else if(istype(temporary_location)) + if(temporary_location.intact) + to_chat(user, "You must remove the floor plating first.") + return ITEM_INTERACT_COMPLETE + + to_chat(user, "You try to connect the cable to [src]") + playsound(loc, C.usesound, 50, TRUE) + + if(do_after(user, 5 SECONDS, target = get_turf(src))) + if(!terminal && panel_open) + T = get_turf(user) + var/obj/structure/cable/N = T.get_cable_node() //get the connecting node cable, if there's one + if(prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) //animate the electrocution if uncautious and unlucky + do_sparks(5, TRUE, src) + return + + C.use(10) // make sure the cable gets used up + user.visible_message(\ + "[user.name] adds the cables and connects the power terminal.",\ + "You add the cables and connect the power terminal.") + + make_terminal(user, temporary_location) + terminal.connect_to_network() + stat &= ~BROKEN + return ITEM_INTERACT_COMPLETE + + return ..() + +/obj/machinery/alien_cache/multitool_act(mob/living/user, obj/item/I) + . = TRUE + to_chat(user, chat_box_examine("Total energy: [DisplayJoules(total_energy)]
Levels opened: [level_reached]
")) + +/obj/machinery/alien_cache/screwdriver_act(mob/living/user, obj/item/I) + // Opening using screwdriver + return default_deconstruction_screwdriver(user, "[icon_state]-o", initial(icon_state), I) + +/obj/machinery/alien_cache/wirecutter_act(mob/living/user, obj/item/I) + // No terminal to cut, just bonk + if(!terminal) + return FALSE + // If we have a terminal assume we are trying to disassemble it + . = TRUE + var/turf/T = get_turf(terminal) + if(T.intact) //is the floor plating removed ? + to_chat(user, "You must first expose the power terminal!") + return + if(!panel_open) + to_chat(user, "You must first open the panel") + to_chat(user, "You begin to dismantle the power terminal...") + playsound(src.loc, I.usesound, 50, TRUE) + + if(do_after(user, 5 SECONDS * I.toolspeed, target = src)) + if(terminal && panel_open) + if(prob(50) && electrocute_mob(usr, terminal.powernet, terminal, 1, TRUE)) // Animate the electrocution if uncautious and unlucky + do_sparks(5, TRUE, src) + return + + // Returns wires on deletion of the terminal + new /obj/item/stack/cable_coil(T, 10) + user.visible_message(\ + "[user.name] cuts the cables and dismantles the power terminal.",\ + "You cut the cables and dismantle the power terminal.") + qdel(terminal) + return + +/// Terminal creation proc stolen from SMES +/obj/machinery/alien_cache/proc/make_terminal(user, temporary_location) + // Create a terminal object at the same position as original turf loc + // Wires will attach to this + terminal = new /obj/machinery/power/terminal(temporary_location) + terminal.master = src + terminal.dir = REVERSE_DIR(dir) + +#undef CACHE_MAX_LEVEL +#undef LEVEL_DISTRIBUTION_SIGMA +#undef LEVEL_REQUIREMENT diff --git a/code/modules/power/laser_terminal.dm b/code/modules/power/laser_terminal.dm new file mode 100644 index 00000000000..c1ad00c2b13 --- /dev/null +++ b/code/modules/power/laser_terminal.dm @@ -0,0 +1,108 @@ +GLOBAL_LIST_EMPTY(laser_terminals) + +/obj/machinery/power/laser_terminal + name = "Laser Terminal" + desc = "A terminal designed to capture power sent by the power transmission laser" + icon = 'icons/obj/machines/ptl_terminal.dmi' + icon_state = "ptl_terminal_0" + base_icon_state = "ptl_terminal_0" + pixel_y = 10 + density = TRUE + /// How much of the received energy we end up outputting to the grid + var/conversion_efficiency = 0.5 + /// List of lasers targetting us + var/list/lasers = list() + /// The amount of power coming into the PTL + var/total_input + /// id of the terminal so you know which one you're targetting + var/id = 0 + +/obj/item/circuitboard/machine/laser_terminal + board_name = "Laser Terminal" + icon_state = "engineering" + build_path = /obj/machinery/power/laser_terminal + origin_tech = "plasmatech=3; powerstorage=4" + req_components = list( + /obj/item/stack/cable_coil = 5, + /obj/item/stock_parts/capacitor = 2, + ) + +/obj/machinery/power/laser_terminal/Initialize(mapload) + . = ..() + id = copytext(UID(), findlasttext(UID(), "_") + 1) + GLOB.laser_terminals += src + component_parts = list() + component_parts += new /obj/item/circuitboard/machine/laser_terminal(null) + component_parts += new /obj/item/stock_parts/capacitor(null) + component_parts += new /obj/item/stock_parts/capacitor(null) + component_parts += new /obj/item/stack/cable_coil(null, 5) + RefreshParts() + connect_to_network() + +/obj/machinery/power/laser_terminal/Destroy() + GLOB.laser_terminals -= src + . = ..() + +/obj/machinery/power/laser_terminal/RefreshParts() + var/cap_rating = 0 + for(var/obj/item/stock_parts/capacitor/cap in component_parts) + cap_rating += cap.rating + // Goes from half to full conversion depending on capacitor level + conversion_efficiency = cap_rating / 8 + +/obj/machinery/power/laser_terminal/on_ptl_target(obj/machinery/power/transmission_laser/ptl) + lasers |= ptl + +/obj/machinery/power/laser_terminal/on_ptl_untarget(obj/machinery/power/transmission_laser/ptl) + lasers -= ptl + +/obj/machinery/power/laser_terminal/update_overlays() + . = ..() + if(total_input > 0) + . += "ptl_terminal_graph" + . += "ptl_terminal_blinkers" + . += "ptl_terminal_light" + switch(total_input) + if(1 MW to 10 MW) + . += "ptl_terminal_bar_1" + if(10 MW to 20 MW) + . += "ptl_terminal_bar_2" + if(20 MW to 50 MW) + . += "ptl_terminal_bar_3" + if(50 MW to 100 MW) + . += "ptl_terminal_bar_4" + if(100 MW to 1 GW) + . += "ptl_terminal_bar_5" + if(1 GW to INFINITY) + . += "ptl_terminal_bar_6" + +/obj/machinery/power/laser_terminal/process() + var/tick_input = 0 + for(var/obj/machinery/power/transmission_laser/ptl in lasers) + if(ptl.firing) + tick_input += ptl.output_level + // We use another var so we don't zero total input since it's also used for other things that could potentially happen mid-process + total_input = tick_input + produce_direct_power(total_input * conversion_efficiency) + update_icon(UPDATE_OVERLAYS) + +/obj/machinery/power/laser_terminal/screwdriver_act(mob/living/user, obj/item/I) + return default_deconstruction_screwdriver(user, initial(icon_state), initial(icon_state), I) + +/obj/machinery/power/laser_terminal/wrench_act(mob/living/user, obj/item/I) + . = TRUE + if(panel_open) + to_chat(user, "Close the maintenance panel first!") + return + . = default_unfasten_wrench(user, I, 2) + if(anchored) + connect_to_network() + else + disconnect_from_network() + +/obj/machinery/power/laser_terminal/crowbar_act(mob/living/user, obj/item/I) + return default_deconstruction_crowbar(user, I, FALSE) + +/obj/machinery/power/laser_terminal/multitool_act(mob/living/user, obj/item/I) + . = TRUE + to_chat(user, chat_box_examine("Unit ID: [id]
Input Power: [DisplayPower(total_input)]
Output Power: [DisplayPower(total_input * conversion_efficiency)]
")) diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index 9957eba1f62..e548e7d3038 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -12,6 +12,16 @@ build_path = /obj/item/circuitboard/thermomachine category = list ("Engineering Machinery") +/datum/design/laser_terminal + name = "Machine Board (Laser Terminal)" + desc = "The circuit board for a PTL terminal." + id = "thermomachine" + req_tech = list("powerstorage" = 4, "plasmatech" = 3) + build_type = IMPRINTER + materials = list(MAT_GLASS = 1000) + build_path = /obj/item/circuitboard/machine/laser_terminal + category = list ("Engineering Machinery") + /datum/design/space_heater name = "Machine Board (Space Heater)" desc = "The circuit board for a space heater" diff --git a/code/modules/ruins/objects_and_mobs/secure_alien_airlock.dm b/code/modules/ruins/objects_and_mobs/secure_alien_airlock.dm new file mode 100644 index 00000000000..4b820846491 --- /dev/null +++ b/code/modules/ruins/objects_and_mobs/secure_alien_airlock.dm @@ -0,0 +1,15 @@ +/obj/machinery/door/airlock/abductor/secure + security_level = 6 + autoclose = FALSE + flags = INDESTRUCTIBLE + locked = TRUE + req_access = list() + +/obj/machinery/door/airlock/abductor/secure/Initialize(mapload) + . = ..() + // Randomize the wires so they aren't the same as the station + wires.randomize() + +/// Always returns false since you aren't supposed to open it normally +/obj/machinery/door/airlock/abductor/secure/check_access(obj/item/I) + return FALSE diff --git a/code/modules/ruins/objects_and_mobs/simon_says.dm b/code/modules/ruins/objects_and_mobs/simon_says.dm new file mode 100644 index 00000000000..18e535adef2 --- /dev/null +++ b/code/modules/ruins/objects_and_mobs/simon_says.dm @@ -0,0 +1,184 @@ +/obj/effect/simon_says + name = "strange platform" + desc = "A strange platform of alien design." + icon = 'icons/effects/simon_says_32x32.dmi' + icon_state = "pad" + var/fail_sound = 'sound/effects/simon_says_wrong.ogg' + var/success_sound = 'sound/effects/simon_says_success.ogg' + /// The chosen rhythm to play + var/list/rhythm = list() + /// List of all rhythms to choose from, made from lists of time from the start in deciseconds + var/static/list/rhythms = list( + // 7 Elevens are like the greatest thing + list(0, 7, 11, 14, 21, 22, 28, 33, 35, 42, 44, 49, 55, 56, 63, 66, 70), + list(0, 2.5, 7.5, 10, 15, 17.5, 22.5, 27.5, 30, 35, 40), + list(0, 8, 10, 16, 20, 24, 30, 32, 40), + list(0, 6, 10, 12, 18, 20, 24, 30), + list(0, 3, 6, 12, 18, 30, 33, 36, 39, 45, 51), + ) + var/static/list/pad_colors = list( + "#b45e5e", + "#3c6474", + "#8a7d44", + "#b45e5e", + "#3c6474", + "#8a7d44", + ) + var/static/list/pad_sounds = list( + 'sound/effects/simon_says_1.ogg', + 'sound/effects/simon_says_2.ogg', + 'sound/effects/simon_says_3.ogg', + 'sound/effects/simon_says_4.ogg', + 'sound/effects/simon_says_5.ogg', + 'sound/effects/simon_says_6.ogg',) + /// The time at which we should finish playing the sequence + var/playing_until = 0 + /// Buttons pressed by the player so far + var/list/pressed_buttons = list() + /// The sequence we generated + var/list/sequence = list() + /// List of connected pads(normally generated on init) + var/list/pads = list() + /// current index in the sequence we check our pressed button against + var/current_index = 0 + /// ID for autolinking an airlock on init + var/autolink_id + /// UID of the linked airlock + var/airlock_uid + +/obj/effect/simon_says/Initialize(mapload) + . = ..() + var/turf/place = get_turf(src) + place = locate(place.x, place.y + 4, place.z) + if(place.x >= world.maxx - 1 || place.x <= 2 || place.y >= world.maxy - 1 || place.y <= 2) + qdel(src) + return + var/id = 1 + for(var/cardinal in list(NORTHWEST, NORTHEAST, EAST, SOUTHEAST, SOUTHWEST, WEST)) + var/turf/curr_place = place + if(cardinal & (NORTH | SOUTH)) + curr_place = get_step(place, cardinal & (NORTH | SOUTH)) + else + curr_place = get_step(place, cardinal & (EAST | WEST)) + pads += new /obj/effect/simon_says_pad(get_step(curr_place, cardinal), id++, src) + +/obj/effect/simon_says/Destroy() + for(var/obj/effect/simon_says_pad/pad in pads) + qdel(pad) + return ..() + +/obj/effect/simon_says/Cross(atom/movable/crossed_atom) + . = ..() + if(!ismob(crossed_atom) || (playing_until >= world.time)) + return + play_music() + +/obj/effect/simon_says/proc/reset_buttons() + pressed_buttons = list() + current_index = 0 + +/obj/effect/simon_says/proc/play_music() + if(!length(sequence)) + generate_sequence() + playing_until = world.time + rhythm[length(rhythm)] + reset_buttons() + for(var/i in 1 to length(sequence)) + var/obj/effect/simon_says_pad/curr_pad = pads[sequence[i]] + addtimer(CALLBACK(curr_pad, TYPE_PROC_REF(/obj/effect/simon_says_pad, play_note)), rhythm[i]) + for(var/obj/machinery/door/airlock/to_link in range(10, get_turf(src))) + if(to_link.id_tag == autolink_id) + airlock_uid = to_link.UID() + +/obj/effect/simon_says/proc/generate_sequence() + sequence = list() + if(!length(rhythm)) + rhythm = pick(rhythms) + var/list/pad_options = list() + + // The reason we do all this instead of just rand(1, length(pads)) is so we can exclude pads that were played too recently + for(var/i in 1 to length(pads)) + pad_options += list(list(i, -5)) + + for(var/beat in rhythm) + // Exclude pads that have played too recently + var/list/excluded = list() + for(var/option in pad_options) + if(beat - option[2] < 3) + excluded += list(option) + pad_options -= excluded + var/picked = pick(pad_options) + sequence += picked[1] + picked[2] = beat + pad_options += excluded + +/obj/effect/simon_says/proc/check_completion() + if(!sequence || !length(sequence)) + return + if(sequence[current_index] != pressed_buttons[current_index]) + do_failure() + return + if(current_index == length(sequence)) + do_success() + + +/obj/effect/simon_says/proc/play_sound(sound, blink_color) + animate_flash(src, blink_color, 2) + playsound(src, sound, 100, FALSE, falloff_exponent = SOUND_FALLOFF_EXPONENT / 4) + +/obj/effect/simon_says/proc/do_failure() + reset_buttons() + play_sound(fail_sound, "#ff1100") + +/obj/effect/simon_says/proc/do_success() + reset_buttons() + play_sound(success_sound, "#00e600") + var/obj/machinery/door/airlock/activated = locateUID(airlock_uid) + activated.airlock_cycle_callback("secure_open") + + +/obj/effect/simon_says_pad + name = "strange platform" + desc = "A strange platform of alien design, it looks like something will happen if you stand on it" + icon = 'icons/effects/simon_says_32x32.dmi' + icon_state = "pad" + //pixel_x = -8 + //pixel_y = -8 + /// The simon says minigame we are a part of + var/obj/effect/simon_says/owner + /// Sound we make when playing + var/sound = 'sound/effects/simon_says_1.ogg' + /// ID used for comparing what we pressed against the sequence + var/id = 1 + /// Time the pad was last played + var/last_played = 0 + +/obj/effect/simon_says_pad/Initialize(mapload, _id, _owner) + . = ..() + owner = _owner + id = _id + if(owner) + color = owner.pad_colors[id] + sound = owner.pad_sounds[id] + +/obj/effect/simon_says_pad/proc/play_note() + last_played = world.time + var/list/hsl = rgb2num(color, COLORSPACE_HSL) + hsl[3] = hsl[3] + (100 - hsl[3]) * 0.7 + var/target = rgb(hsl[1], hsl[2], hsl[3], space= COLORSPACE_HSL) + playsound(src, sound, 100, FALSE, falloff_exponent = SOUND_FALLOFF_EXPONENT / 4) + animate_flash(src, target, 1.5) + +/obj/effect/simon_says_pad/Cross(atom/movable/crossed_atom) + . = ..() + if(!ismob(crossed_atom) || (owner.playing_until >= world.time) || world.time < last_played + 3) + return + play_note() + owner.pressed_buttons += id + owner.current_index++ + owner.check_completion() + +/proc/animate_flash(atom/to_flash , color, length) + var/my_color = to_flash.color + animate(to_flash, color = color, time = length, easing = SINE_EASING) + spawn(length) + animate(to_flash, color = my_color, time = length, easing = SINE_EASING) diff --git a/config/example/config.toml b/config/example/config.toml index 86230f38660..b2fe19e30eb 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -674,6 +674,7 @@ active_space_ruins = [ "_maps/map_files/RandomRuins/SpaceRuins/way_home.dmm", "_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm", "_maps/map_files/RandomRuins/SpaceRuins/wreckedcargoship.dmm", + "_maps/map_files/RandomRuins/SpaceRuins/alien_cache_site.dmm", ### The following ruins are based from past pre-spawned Zlevel content ### "_maps/map_files/RandomRuins/SpaceRuins/abandonedtele.dmm", diff --git a/icons/effects/simon_says_32x32.dmi b/icons/effects/simon_says_32x32.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a76856b5baf221bbe9ef0567bfd53228171c6fda GIT binary patch literal 423 zcmV;Y0a*TtP)7Cm%^jQiA;HapC{~00DGTPE!Ct=GbNc003utR9JLG zWpiV4X>fFDZ*Bkpc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TB zGg33tGfE(w;*!LYR3KBSATdRWi!&v&s2IpI$-(AC+X&p1Ir5p2oZB#W%*^~JZvZ?_HS+o{ZC;(?jSwXm(j0KRo zdqt3UH~_dDU>ITsz-6+n_q`*G0w_vn+R^)WZ){TqP>J9SB2t`z)=$ea`+f(bpO|N~ z`~i}ozus|tdh)sC`-wnNLTI{4_!YTfE;o^nHbkS8#{6j1YNauom5JHM_yALGZGwav RJq!Q<002ovPDHLkV1jjTt~vk! literal 0 HcmV?d00001 diff --git a/icons/obj/machines/alien_cache.dmi b/icons/obj/machines/alien_cache.dmi new file mode 100644 index 0000000000000000000000000000000000000000..d3649e496e3cbc323d926495d3db58159eaf7db6 GIT binary patch literal 1584 zcmZ8hdo4-DjHhaqP3|l zH%l{C+0dm@VP02OO(>b&CS}F6cGtgs&-tG3`};lT`#qoU*(g*v$k^H#000mI9vTe* z1`c`#mM+nI#tfQ@^#T%wMDJ*8YtyT7z)3%3xnD#%@S=JD`=PC`x~RZoe_(eWu$K~D zeHJK60O0AsZs7!isLwXQN26kZ;K`R6dV@(u$KylOkEY=;d3a3j5da`DV`B4ggl}^( zd9&(w?hz38o)^6_clT`UgT5X2>_xhD0XWZY(H3YlbO5@u$QEi|KThkQp&eq}0yYIW zZI%b0ErB`5cNHazk^q*(xI ztJX{Ky+ZOC$AD{lMYID)rlMvyTJU~ULtDXedFnXh zVp4fCys(y$&ek-DKA2Pb7fKVT8^W`&6wCZBJTdpykTRUxB5{kHi16VIl!B!5jl*|< zYq3F=M2l|Yk<;?1T8spnD3+HEFa3n7i?g|%TJ&sXLV1X}F1wM;U`gx0*+kZ4G;5!= zuAm;c`?kF-B-aNmy!Per2ZfWt3F8hR==+pnLROEU;0i}|a=Lm0jK%iKkN@rXF(R>0 z>%QMg;Zt&hh198Qa#b@rw`que2|U>A6r)d9&pmD4RD`dha8|+YOLU7r=*_Kb)MOZh z=;xf~JEu}<_W4+p(;xps*tJ_X$(M{;%Z8L7!t0SiT2jLUNgZDqcV-1kzV(G&t=2brp$fIoKap4gV&Gb1ya!xh+^^N;y3LpiMK-S1ZD^2oHKL<6Cg0( z6>puXXfm6XA?^S4s(mawD=V%htdZkyZSC9mMUpz7|I?#G;#BDf{Tcj%7x$qkU)4Sy zC1f${-_GA7u87xe^}uY+4?ryB>xtNhT;ef{+aU&=7q;}%@$&3$=hX;u@l zpjdtoe%Z?+xqSIc4)9}4pJ0!1jeK;Q;e(IQg@y`F(;97qX%(4!d^u7jNVqA8cd7vx zb9ytmmD8^T6+c+r^L{DEnpxNz0dmhz*<-0J3*-CY+XMP)Wr-$j2ZT)X=0vfq42&t* z66}whUeHXKTzJ>PWm%C0j)`T|D*fE3)3}}gt-D(1Z~Yk^)ADfYbI7o~|8L&Gsw*bl z!}ckEP4865I|#MOS=I8KDQP!uRGK7~M>$1$t-da@lNqk)533utkxfqFi>}mOh~@i% zO{3)NjoT`FB4+!hAe6)tyhk2wmz+IEJ+)!e0#xoMaOA4v0#7f|xb=PVI>%ypvTI~&d{&n^+w8b~ z!!mYMwpHXG(%ZbTqXB*Wbl%Za0VQ92-=o~=-JOp&tK%tpcm?tzzJd{A5yn*i6n~*2 zmki9}vg*|E&hr`e#NIAuhCLy!$0c2l?i?&a+=9_rrP)sp4PPV eufLSOb^Wm<$M7G?1hG2 zw=xw!_~4U=s~E?a7lxm13BQgxVp%+sWgD+2k@m_*aeOD}%ZQo2sEZVyAy8=kY`m+g z>voXeDM8)aU+2^!ueygPUpLFQN18eu@sJ8Jo4M~Rv)~lzj?y_5#1%m!M|~m>n4kR? z(q(ET9{E@|W~6LyOrXja>>Rbq=-t1u|G{t?rsmv_Ys-V}SVuO$pj9m*x1?g=bzQ>V z1LXh!Fck*9=oVKX;7Wouf6?k(84nd#-uN^hk)>F`3Ct|5@VOJXqF&q;zaUIhI4F@K zY21a#A!>ES43ce9I_#oGE5^w3xY%1@f(>yG_RyuY#2{uF2I%=01(4^tXybbL7&q5B zk1yTAYn)jVd#?8I7qea*x&O_#0;Qy1V?|Lf$IXpdgq!O&N|wGDEifUxkQm+>>Y4vW zH|20%Uzd7^W+RebJstDgc@Mili)k-a(iV0U`%aWDO*3a@D8o}VhXs)Mp%l+3)TKuX zdS=|mwN`@^FaedH(-HcCDobU=l;QH5aPa9?gm6ml90$v8bg%fsy<$CD<4QxCLJ_6{ zd96O{r+tn!`DaPWP^;rV6Mi2O$!^q}c*_ud52A`KjNMfKw1*uElUMvp%*ruikhRaB zPZ;+%2|SZM>T>EC(b>OlDL_+?@aHcW$T4`HhIKg^wW(Q`ywrpZL6JPyF6j2HLD|~N zHN6HrO8P8&Az8=|8&wP4LqqCzUb|q-9&}?TW8U9s3CM+iwFqR;!2+eN9Cgyp`Um|h zknSIdwj{I!{p>V`e-5{USCR-MU$7vsh9|0i0<@dTi72t?tbv}Owd(cKjUod znDjV@mxV1Ey^Fv5N&|fMYQxIwwGpv&4}Xn8*tqxDYGs(hL885eA9Wk5$pa^tOzQTN zrtiW?vV}PPB262Z<#!iKdau)JL*5xbDC>ov>+Qel1j}08Zjev5YqPdjO*>RH+15(~ zvj#Aj!TD8$tK7u$c3tvznzFZXZgwaqeS3O9t76iF6N3I5uhkbo0F*4YLT5!8xC4Fs zwH_WthN_HG|420hgPAo>dgdL#N|!Tda%-%@w;nc-aSvO&XrDk9X_^Png_n)`H)F_C zfZyFD)*W*R8|`tBHXGri65bQ@MDP~fX72C7AAe@4X*3TzL0%a9W+!?;Lh# zy6U7UPAzZM50-wl>rCBjcMw%_Y4mhOZ%A5#AUf)Ou~5H5rc&o}vu+Uj{C{sc?V_a@IZpUhNKZd0VMKO__B9Pku zJzv&dpF}77m7}2T-YtnMqT2eYuxK>QkR23XHxkcnRKN8{} z=jALS_&Vq@Cw%~HWbq`6{iW!Cn}mlJn4Zk%rP&*$B%81>ZOMq>3U~M;iS*|A<|M{s zZclbtZM|P-MCFV?Bo0DuWZ&Am9St;q%tbX9#oj<}yt9;rGi~?@|7*}ZRV_9CfV~UY zuI0y@&Qx6sygnJ6Bw`lFd+Qr?U(l{aAWV%KR6xaz_$D+Yt9*$kG^h|!GavpV{h!|9 zltJg3m#O^@8G@z4<2CNDIo19hM&c$=-a#4Ao#pxydIxveG7*f~wE3af+XI_}VwS*A z4~oL?9(s}5pL*{U;bW%+jE?r2tva`0=cqpwJ#;+Vjq_OWJ;7_y;ngG4GF2buY?N39 zL|JYh`Z^}%_e-GrC#CMX0fEc@&8vlxoM~F%&~t;sjRTUW7O}xe;r@LLm5ccI_Q=b& zlRsf;`o%k2)=A2OlPivLI&XAcxRb!6HeZC1FuSL0Zsb@^^ix;=Ov3D)pj12WJO2W_ C93}q% literal 0 HcmV?d00001 diff --git a/paradise.dme b/paradise.dme index d8d715bca1c..5d605c95be8 100644 --- a/paradise.dme +++ b/paradise.dme @@ -850,6 +850,7 @@ #include "code\game\area\shuttle_areas.dm" #include "code\game\area\areas\depot-areas.dm" #include "code\game\area\areas\mining_areas.dm" +#include "code\game\area\areas\ruins\alien_cache_site_areas.dm" #include "code\game\area\areas\ruins\blackmarketpackers.dm" #include "code\game\area\areas\ruins\lavaland_areas.dm" #include "code\game\area\areas\ruins\space_areas.dm" @@ -1194,6 +1195,7 @@ #include "code\game\objects\effects\spawners\random_barrier.dm" #include "code\game\objects\effects\spawners\vaultspawner.dm" #include "code\game\objects\effects\spawners\windowspawner.dm" +#include "code\game\objects\effects\spawners\random\alien_cache_random_spawners.dm" #include "code\game\objects\effects\spawners\random\bluespace_tap_spawners.dm" #include "code\game\objects\effects\spawners\random\decal_spawners.dm" #include "code\game\objects\effects\spawners\random\engineering_spawners.dm" @@ -2874,8 +2876,10 @@ #include "code\modules\pda\games\minesweeper.dm" #include "code\modules\persistence\persistence.dm" #include "code\modules\point\point.dm" +#include "code\modules\power\alien_cache.dm" #include "code\modules\power\cell.dm" #include "code\modules\power\gravitygenerator.dm" +#include "code\modules\power\laser_terminal.dm" #include "code\modules\power\lights.dm" #include "code\modules\power\power_machine.dm" #include "code\modules\power\smes.dm" @@ -3087,6 +3091,8 @@ #include "code\modules\ruins\objects_and_mobs\gym.dm" #include "code\modules\ruins\objects_and_mobs\id_upgrader.dm" #include "code\modules\ruins\objects_and_mobs\necropolis_gate.dm" +#include "code\modules\ruins\objects_and_mobs\secure_alien_airlock.dm" +#include "code\modules\ruins\objects_and_mobs\simon_says.dm" #include "code\modules\ruins\spaceruin_code\voyager.dm" #include "code\modules\security_levels\keycard_authentication.dm" #include "code\modules\security_levels\security_level_datums.dm" diff --git a/sound/effects/simon_says_1.ogg b/sound/effects/simon_says_1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a5fce7c329a8c814c2686f16ca15d5a977ec3256 GIT binary patch literal 3862 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 ZALM!+nsfRmloMV4hffcCaK#Sy{{wp)$G!jn literal 0 HcmV?d00001 diff --git a/sound/effects/simon_says_3.ogg b/sound/effects/simon_says_3.ogg new file mode 100644 index 0000000000000000000000000000000000000000..aa54566cb4de0fb24f4a5e285138b0a8f7e142c4 GIT binary patch literal 3769 zcmahM3s6&6_VN!%X@G!%Mhk5s38F(VQQ~h&AOe91yzuyh%}_q6+89(q{7eTIO+Xrv zr7;AJp#@456tJ;^!j4v1Kop963=xs~Cs12I?Nr!W-MudXYiD+*cka9Iyz{;H+;i?d zFJaRr0k8*iO}O}c4hFXV%N6IZgTx$2WHKQ!G+^tN zY@3{&lkNQhCo;tjL;ygR5ra1vWWGwgKPS=e0f{pp`O%LP@42587N5~akyQlN*KkTR zsQU&hP=E;>VDBa%?6nrQ=xEX_Y##G*l-5Utn&nAAG^U{8O*}HuMA-1`hW@mYU-$}I;tDq z|BM^x1pptBM7XkpboF!6RU>Hu?wxcexDNma`C?S{G35_q)Z}zE#o8x7I~me<#@fTI z;xPauCb@HW5L~Qsxd7m5!c~Sem0`2WQd*8vmNeOeA^;qr8AhaFZL}updNOKmla9aD zGqhqqt2p!_et65rjn1W^4t~zoeSp&_@>GR(70o-jh{*G(b8Tkb(Lbn3_t5c-tVQ~m z);;QDs&s7{~mu7i9QeOFDon+*yw?ithtW4Ijy(;w+Qdmyx=c~6Dz zP%DI-nzxke;*1cAY2Sk!i^Fg-OqF%`p?jL7_r^XOF0^Zp4R%8)p=bJgJ^l@gmCx#A zxZVhS$)T*FYj#9Y;;RV?oZVw`GfyD$Q zW`4*n4J;w%U!$F<+0P4ZY^W;>>kIj&-#V=RvFRI+HSY8-eB{#RE<_+@j;2YTuq-;o z*%YCqxfS8j3>w7E>c0T!dycbTZbcHNIlGj0LZ9yz++cHO0Jb#%B>YSo@?7*se@?pY zNlEg8ec4DDb40*JrGS$S5PR-FE)Zd*D`@&Fj@wuR7oh_Zgx z6j_6xUnbm8u$>TXjknk{i9jtf`LgRyFQcHS8{*DphYjs%>c0ExmU9$BvfAf7KUQ z1Dp&%0XaIK9F35<2n4L&Epo@aOZqpR%*_w!8)n$nIOM$!IyAMwc4*9P008b+zb5nz zk@#K|6S52yEHfcolK}mz#vftuy`f`Wv4kBI%KDqD%Gpy*RlU^u8uLchWWBkcx?gX> z({qkoLFke0JzBO>;{=`N2$Eo4(R=A#r2K){AIRJRR`4Q24=cLys~br?<8k2{o}n#S z@<0ziuTc%cxKoP^$&!;sC~5~fYyY*QpkzJFd1n#;_VHAQ;*usDxbFyvN_}KJyOf}S zM*&S)fpig-{VbZF4B^lkixK1?QBlEKLXmCcQELr}ytrEBD2HY&Muc0NO*-L0rgB(F zHMA!34mKM6GbGFnW$a%4VKpnrps^DAS;5r`c!a`(P8dS#PGbd86y-dZK!chWPcaZ9 zgN>D%Y)H5%+}hrKL`F4qt9e@~hO~@$RvS@Pju{WK8aK2eh@?`6KviN7UNBU}}v2ZZtm937guxZ%U-?zv^UzjjcjKd^6~|+?5hdl!7dqi7NRWJDoYcvl(kE{UB+PR} z3A7=EswPLVTFJo!9uG@2`(WPNV|niw$fG;gvPMv14447Gq ztQeSm(moqTE}yXSpmLFDCMt;u0~iMQIw%D(=`RXLlC7Aw;!Fmb3Nc#ooiq`~(rU`L;{{4#tl~b0SL7iLc28oELF4tNHv6MW`xE2@W)s4} z?B8zI(2U^`wSi0RilEXPHrcw|q&Vz%aL4K%j)>p0z7Z+LMHv#}cSBl`XYIZ+;Q)3{G-q6V5(Msr%*8+~y1a5)F zfihK~eQkG2TsOSf@s0ooT@V_P80rArJ@D&Y*g+B7&%)}KlUowQ2yr~%QVBfiK7hQ0 z`p`b2xSM;c+o{0s?*8RZR^vJN{rIl?z%Ay|W!F_}pO*yPzUx?Bhqz$>69Df1y8v8q z!g~&pHk9-@M<2N25(BGY*m}Vw7jP-iJ}QcF;PYDJ<)M4e?Yv--GS?g(9jy?IL*VFW z{kw5A``L)t-wlReE_~XI3tYK`LZO6aM~%fL7B74~)eLZU;3xm+yd53W$K&ADkM-n- zp?1IB+I#uc^tZDSJECJsG(}Dz!ln&?=f}QqC?aXs-2C>pjUQ&6|J$tV5ZIpXaesyR zcjv|9dpiYRI(KiQZd4{8cbFG}?_DkSpyKdj%^;kLl`B=B)?d^;dv!8Qz zbe;%X1UGPQT1Eefb}eE`^QPBK+=lLZ_Uh$1|7VBm9M{KQTzVyI(ew0=92QJvzK+{< z{6=pXR${^vtEFok!#!bZCUxYXuM9W%XsC}IO? zARk&|>Y4?{3=*uFpm;z2ynqTyB8?vzpvOx$y8+c-ozHpC`@GNdeV%j9 zbIxG^wJ5MaxnI*cemPtNtcTlpb8lj7!h$VQXt5 z8|p+Axt!N;))0tP*e~2mHyDM~NCSIqj!7T27N`mgc-u%GO*GSpv8^B#QWqJZ2E2&~!#vR$tG*cnFiVhly%&4+Q~g{~ zg;k#_mtQcc5B-UO}-VebHaSx>j1+j2{y)dl*~A}gdqt!>feHm+Q#bSK~{+a zTVzXV-Di5+*p9hu+TLs9HeN4KM{V*zE8twVjyidOhl+CSyJ?LkdAoY=KW_`}0%}Ri zz8dR+R-k-ZwpClx96+VC@5A3tBY9fVl)L%5^FGrypZsh**KT?HW+zY^yY7Esqs>Cx zIJc8wz6rGQ1NonGskdBPBi08OgvZ?%PEN%7hwE*kulpvpHkemY*|ocHNtDOo-Y9_@ zSw_cGW}1SE$a2Q+ZtnYarIP5T#-mknJux3&cQ>oPY5q89bs(>UmbkQ~1D9z813Bu4 zn9Hg^wMn6FX~iX6yBQB~`!51~FY*oBT$4#!E~wzXZ`&OZ-RN;=1i3T_ZG6P+3tsel z{ek&OFgr5@>azYgLBC8)=v0Fio&0oh?ogR>vUmjQlrBHVqLTlqdZrFR9s!%(GZAhV zLwg;YV3}z%(}V>@JL$>p{g!%?m3E7D<5tYZANd>Sug#>3`}suQ0%hh(P38(!zd$Kl zp(HkGvMrk2Hr?%ms@s#rqkpJMKlcRP!TJ6H!B%uiE$v@-Yp3vz03SJk0st6({qdOnPcDlwcya~BF~{Qy#NRW zUi*mN)5nyCu$}laLbl9_i=8s!uZpyvFP#sL^(O=Nrb2tY7o(66grM9*75U%ghD6TvrX=jg2!4 zjM)3O!d{GX#N3TV9eFE+Ylly+%VzFP9k;!8E0qKL< zEuAmkNLLQd7dvbDH~50gEJ8|`I?Oe`Q|7AeuH`h>3;<!a z31^!s_i=HyMK|)SbeQ}EsSZAsZJLpTv!;l_EaLVGkP0%3l$nG|4wm;Q<%2}_KrvW$ z%A~^!2*N@PJ5_{5msdoLY*G#n67qrK+XH2J1I3pcKRCOi`^1UkC!2pcx%`*2-###> z_NS8rawcUG((G1^OsC16FCJV#I4xqC4y+XC=~TmmNqssoQL7@mtW_yrR%M?C6%Dbc zc(h2VJg8IVypTIt*00kkYgMD!D*2>Jd1XL1I+@3A8HD3uRHnSGl9wHKZ;S_UUBxk^pI|3 z;>pCjE6e%)r!-)1q)3@}P&cym!P$%~LYb`^o+Pp#7sDX#J}w?*5QCH8@^Yu;Nd9E8 z{o&kNqOA-@;5wvH=Da#K@$31xB3&-%IDJyTLqAw|k zVX!5p3oGb)I10Fi8oYyHxQ=ndw77`dREFb~41Eo@gst5m;ndqRBx&`A0TI_xhAXzW zIIW6Gf#H^dV{grnR5sbebJc?NRl=*bw@g@+-Ru@z$D)tu;RuC;P7%ZH%)z4A`f7<^ zq}?P*XWJQx(T-Yk0SJZ^+uJ)2X*u>zlVm&Fo|Bu7wK25SWam-t&h4!@uCCSMU`iDv zi3U>=2@ggErh-HgFeQ_~s0x;HojH;h9bhVrH;{o>f*?)815-O3UkwPiE^7!WSb+zEJRdE~y>K7g3R_%AZ z@gbR7aGIfpJ``YTe4r4LpE*HrJS;K44sq{I#yurc!t<>cR`6U6d@66;W;XI%5e+zR zoN2)0ToGmyPEvS)y}^X5>&-a+VhhB*HyQU&^4Xz&5VmYo6M&xI$?SQwCX-JL?}@qg zV^SXt7d_sNXQm+y)dXZs2v^#K>KUYwN`PQ4w;Hms79@XCPsUodbOOvEF zwGI_D+w>W@q@^>w5cbr7qS-NET@Cv-RNagXF(uoUb>(P#&rR0gYTkfC``x*N0|m_j z*XMfLE?C2#U`{r8YyK39dgo-%xY$uA$F zm-TZ{Qt5$bn^{K|RQoO@XG`@*5wrqWU>n3COdHTlxSTm`+|3ITQQ4bkcy@D@2j_1$ zo2GG)N5r}6Ko@wp#H~vusr$1$E}7Z7rCjzbWjD|y91;h_#O{b= zTv0RHsDXkMOb{Yjy!3D7i8L@LvU=VGcpD-$6=PAf;MVrf0_%W3Iezho= zw*ax0aPE00ly!+~rKyHmRS;VPPlR!Zym5(T7OyyU-f%@5^KpWmSzIg0y{{}TvS ze;z_td}zUa%=P76zRCNq_@zKL4CN}i=!Y&wdM73E_rF>1xYT#|2d@xFQl<-qLUB{D z4-tjp{=1z3UL9gK@OF zAc8$3C*K#o@QpZeymD;k$PR9ccOydoOFjRD(x-b~r)%Mc#*!?A4p&U$UwJK%D!-Al zVhTIw`ec97g{NO#ovgbhPnBJ}ye>CB6k93i**@ml8S<<3r+o9>UB#b1dX#rQgt}qv SmSxt}AKUycI2Bt|Ug-Z){i-AY literal 0 HcmV?d00001 diff --git a/sound/effects/simon_says_5.ogg b/sound/effects/simon_says_5.ogg new file mode 100644 index 0000000000000000000000000000000000000000..1ce4b870ce360ee1610d7f2e7bfa6a5ae5d6229c GIT binary patch literal 3779 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 T7V^^*a`g1q#}&(eAA$Z2Q?A4I literal 0 HcmV?d00001 diff --git a/sound/effects/simon_says_6.ogg b/sound/effects/simon_says_6.ogg new file mode 100644 index 0000000000000000000000000000000000000000..9c482b8dd0be22206f4c4f4fd1dfaf93f003c453 GIT binary patch literal 3844 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 literal 0 HcmV?d00001 diff --git a/sound/effects/simon_says_success.ogg b/sound/effects/simon_says_success.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a50a32c0c3ee4252bd9d4e519aebb9cf03dc3863 GIT binary patch literal 7265 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