From 18cd2ffee2f0986acccbeaa06b8c2cc3f24ed334 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:56:27 -0500 Subject: [PATCH 01/11] Build(deps): Bump JamesIves/github-pages-deploy-action from 4.7.1 to 4.7.2 (#88359) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.7.1 to 4.7.2.
Release notes

Sourced from JamesIves/github-pages-deploy-action's releases.

v4.7.2

What's Changed

Bug Fixes 🐝

Build 🔧

Full Changelog: https://github.com/JamesIves/github-pages-deploy-action/compare/v4.7.1...v4.7.2

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=JamesIves/github-pages-deploy-action&package-manager=github_actions&previous-version=4.7.1&new-version=4.7.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/generate_documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml index 2062fb545689d..423c7f10ad61e 100644 --- a/.github/workflows/generate_documentation.yml +++ b/.github/workflows/generate_documentation.yml @@ -27,7 +27,7 @@ jobs: touch dmdoc/.nojekyll echo codedocs.tgstation13.org > dmdoc/CNAME - name: Deploy - uses: JamesIves/github-pages-deploy-action@v4.7.1 + uses: JamesIves/github-pages-deploy-action@v4.7.2 with: branch: gh-pages clean: true From d8a01aed9c3ea63427d35cfb797ee7992d6e4757 Mon Sep 17 00:00:00 2001 From: "tgstation-ci[bot]" <179393467+tgstation-ci[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 00:28:07 +0000 Subject: [PATCH 02/11] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-87799.yml | 4 ---- html/changelogs/AutoChangeLog-pr-88316.yml | 4 ---- html/changelogs/AutoChangeLog-pr-88335.yml | 4 ---- html/changelogs/AutoChangeLog-pr-88349.yml | 4 ---- html/changelogs/AutoChangeLog-pr-88350.yml | 4 ---- html/changelogs/archive/2024-12.yml | 14 ++++++++++++++ 6 files changed, 14 insertions(+), 20 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-87799.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-88316.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-88335.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-88349.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-88350.yml diff --git a/html/changelogs/AutoChangeLog-pr-87799.yml b/html/changelogs/AutoChangeLog-pr-87799.yml deleted file mode 100644 index 0d1fe14853c45..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-87799.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Autisem" -delete-after: True -changes: - - refactor: "Nanotrasen has introducted new upgrades into the aging station shield statalites, they require a but longer to toggle on however" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-88316.yml b/html/changelogs/AutoChangeLog-pr-88316.yml deleted file mode 100644 index 7600f7da3cf98..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-88316.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "imedial" -delete-after: True -changes: - - bugfix: "Map vote now cares about current player count" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-88335.yml b/html/changelogs/AutoChangeLog-pr-88335.yml deleted file mode 100644 index 0e5c84fcecb79..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-88335.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "OrionTheFox" -delete-after: True -changes: - - bugfix: "fixed the Icebox Phonebooth air alarm being on the outside, thus triggering because the planet is, indeed, cold. It is now inside and all-access so that callers can turn it off when they decide the phone's more important than their health and safety." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-88349.yml b/html/changelogs/AutoChangeLog-pr-88349.yml deleted file mode 100644 index eb71d087d0c33..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-88349.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SmArtKar" -delete-after: True -changes: - - bugfix: "Fixed a qdel loop in hypnosis brain trauma" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-88350.yml b/html/changelogs/AutoChangeLog-pr-88350.yml deleted file mode 100644 index 1a389e6077c2c..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-88350.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SmArtKar" -delete-after: True -changes: - - bugfix: "Fixed atrocinator not yeeting you up" \ No newline at end of file diff --git a/html/changelogs/archive/2024-12.yml b/html/changelogs/archive/2024-12.yml index 00c5e93f019c0..66f8779a4a24a 100644 --- a/html/changelogs/archive/2024-12.yml +++ b/html/changelogs/archive/2024-12.yml @@ -140,3 +140,17 @@ - code_imp: The stacked_lights test now screams with area names too. SmArtKar: - qol: Shifted the escape menu stat panel down a bit +2024-12-06: + Autisem: + - refactor: Nanotrasen has introducted new upgrades into the aging station shield + statalites, they require a but longer to toggle on however + OrionTheFox: + - bugfix: fixed the Icebox Phonebooth air alarm being on the outside, thus triggering + because the planet is, indeed, cold. It is now inside and all-access so that + callers can turn it off when they decide the phone's more important than their + health and safety. + SmArtKar: + - bugfix: Fixed atrocinator not yeeting you up + - bugfix: Fixed a qdel loop in hypnosis brain trauma + imedial: + - bugfix: Map vote now cares about current player count From d4fb571edc5704d6ad81f4226bd1c16647eda1f2 Mon Sep 17 00:00:00 2001 From: carlarctg <53100513+carlarctg@users.noreply.github.com> Date: Fri, 6 Dec 2024 03:31:20 -0300 Subject: [PATCH 03/11] Recovered crew no longer show up on roundend report, (#88345) ## About The Pull Request Recovered crew no longer show up on roundend report, they are technically antags, but they are not antags ![image](https://github.com/user-attachments/assets/255e26bb-acb0-419e-91bd-d203897dc26a) ## Why It's Good For The Game Too easy to get greentexts. we don't want players to feel accomplished too often ## Changelog :cl: fix: Recovered crew no longer show up on roundend report /:cl: --- code/modules/lost_crew/recovered_crew.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/lost_crew/recovered_crew.dm b/code/modules/lost_crew/recovered_crew.dm index 65c6a3715fb67..2d5181a0d4e41 100644 --- a/code/modules/lost_crew/recovered_crew.dm +++ b/code/modules/lost_crew/recovered_crew.dm @@ -7,3 +7,4 @@ show_to_ghosts = FALSE silent = TRUE block_midrounds = FALSE + show_in_roundend = FALSE From 3534594d701295dae58dddf783f6e1d4a275c236 Mon Sep 17 00:00:00 2001 From: "tgstation-ci[bot]" <179393467+tgstation-ci[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 06:31:42 +0000 Subject: [PATCH 04/11] Automatic changelog for PR #88345 [ci skip] --- html/changelogs/AutoChangeLog-pr-88345.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-88345.yml diff --git a/html/changelogs/AutoChangeLog-pr-88345.yml b/html/changelogs/AutoChangeLog-pr-88345.yml new file mode 100644 index 0000000000000..fc3bfc788498e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-88345.yml @@ -0,0 +1,4 @@ +author: "carlarctg" +delete-after: True +changes: + - bugfix: "Recovered crew no longer show up on roundend report" \ No newline at end of file From 58a0794fa6e1a4f32804a56af115725b5800d801 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 6 Dec 2024 00:31:55 -0600 Subject: [PATCH 05/11] Fix drink labels for alcohol bottles (#88355) ## About The Pull Request - Fixes #88351 An examine proc used bitflags to determine the contents of a bottle despite whatever reagents are inside. I went and changed the examine message to use `The label says it contains` instead of `It is` which is more appropriate. Also the empty bottle parent type was listed as `ALCOHOL` despite spawning with no reagents. A lot of alcohol subtypes relied on this to give them the correct bitflag. ## Why It's Good For The Game Drink consistency. ## Changelog :cl: fix: Fix drink labels for alcohol bottles /:cl: --- .../mapfluff/ruins/spaceruin_code/meateor.dm | 1 - .../reagents/reagent_containers/cups/_cup.dm | 2 +- .../reagent_containers/cups/glassbottle.dm | 37 +++++++++++++++++-- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm index d40d9178f3a85..88b9e9f9503f4 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm @@ -36,7 +36,6 @@ /datum/reagent/medicine/c2/penthrite = 5, /datum/reagent/consumable/vinegar = 5, ) - drink_type = NONE age_restricted = FALSE /// Abstract holder object for shared behaviour diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm index 0c6d638fca18d..ccd9c4169a5a3 100644 --- a/code/modules/reagents/reagent_containers/cups/_cup.dm +++ b/code/modules/reagents/reagent_containers/cups/_cup.dm @@ -28,7 +28,7 @@ . = ..() if(drink_type) var/list/types = bitfield_to_list(drink_type, FOOD_FLAGS) - . += span_notice("It is [LOWER_TEXT(english_list(types))].") + . += span_notice("The label says it contains [LOWER_TEXT(english_list(types))] ingredients.") /** * Checks if the mob actually liked drinking this cup. diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm index 5712d383f0b57..90cc93e54111f 100644 --- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm +++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm @@ -21,7 +21,6 @@ var/broken_inhand_icon_state = "broken_beer" lefthand_file = 'icons/mob/inhands/items/drinks_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/drinks_righthand.dmi' - drink_type = ALCOHOL age_restricted = TRUE // wrryy can't set an init value to see if drink_type contains ALCOHOL so here we go ///Directly relates to the 'knockdown' duration. Lowered by armor (i.e. helmets) var/bottle_knockdown_duration = BOTTLE_KNOCKDOWN_DEFAULT_DURATION @@ -308,6 +307,7 @@ desc = "Brewed with \"Pure Ice Asteroid Spring Water\"." icon_state = "litebeer" list_reagents = list(/datum/reagent/consumable/ethanol/beer/light = 30) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/rootbeer name = "Two-Time root beer" @@ -333,47 +333,53 @@ desc = "A bottle of high quality gin, produced in the New London Space Station." icon_state = "ginbottle" list_reagents = list(/datum/reagent/consumable/ethanol/gin = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/whiskey name = "Uncle Git's special reserve" desc = "A premium single-malt whiskey, gently matured inside the tunnels of a nuclear shelter. TUNNEL WHISKEY RULES." icon_state = "whiskeybottle" list_reagents = list(/datum/reagent/consumable/ethanol/whiskey = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/kong name = "Kong" desc = "Makes You Go Ape!®" list_reagents = list(/datum/reagent/consumable/ethanol/whiskey/kong = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/candycornliquor name = "candy corn liquor" desc = "Like they drank in 2D speakeasies." list_reagents = list(/datum/reagent/consumable/ethanol/whiskey/candycorn = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/vodka name = "Tunguska triple distilled" desc = "Aah, vodka. Prime choice of drink AND fuel by Russians worldwide." icon_state = "vodkabottle" list_reagents = list(/datum/reagent/consumable/ethanol/vodka = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka name = "Badminka vodka" desc = "The label's written in Cyrillic. All you can make out is the name and a word that looks vaguely like 'Vodka'." icon_state = "badminka" list_reagents = list(/datum/reagent/consumable/ethanol/vodka = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/tequila name = "Caccavo guaranteed quality tequila" desc = "Made from premium petroleum distillates, pure thalidomide and other fine quality ingredients!" icon_state = "tequilabottle" list_reagents = list(/datum/reagent/consumable/ethanol/tequila = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/bottleofnothing name = "bottle of nothing" desc = "A bottle filled with nothing." icon_state = "bottleofnothing" list_reagents = list(/datum/reagent/consumable/nothing = 100) - drink_type = NONE age_restricted = FALSE /obj/item/reagent_containers/cup/glass/bottle/patron @@ -381,18 +387,21 @@ desc = "Silver laced tequila, served in space night clubs across the galaxy." icon_state = "patronbottle" list_reagents = list(/datum/reagent/consumable/ethanol/patron = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/rum name = "Captain Pete's Cuban spiced rum" desc = "This isn't just rum, oh no. It's practically GRIFF in a bottle." icon_state = "rumbottle" list_reagents = list(/datum/reagent/consumable/ethanol/rum = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/rum/aged name = "Captain Pete's Vintage spiced rum" desc = "Shiver me timbers, a vintage edition of Captain Pete's rum. It's pratically GRIFF in a bottle from over 50 years ago." icon_state = "rumbottle_gold" list_reagents = list(/datum/reagent/consumable/ethanol/rum/aged = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/maltliquor name = "\improper Rabid Bear malt liquor" @@ -400,6 +409,7 @@ icon_state = "maltliquorbottle" list_reagents = list(/datum/reagent/consumable/ethanol/beer/maltliquor = 100) custom_price = PAYCHECK_CREW + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/holywater name = "flask of holy water" @@ -409,7 +419,6 @@ inhand_icon_state = "holyflask" broken_inhand_icon_state = "broken_holyflask" list_reagents = list(/datum/reagent/water/holywater = 100) - drink_type = NONE /obj/item/reagent_containers/cup/glass/bottle/holywater/add_message_overlay() return //looks too weird... @@ -424,6 +433,7 @@ desc = "Sweet, sweet dryness~" icon_state = "vermouthbottle" list_reagents = list(/datum/reagent/consumable/ethanol/vermouth = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/kahlua name = "Robert Robust's coffee liqueur" @@ -437,12 +447,14 @@ desc = "Because they are the only ones who will drink 100 proof cinnamon schnapps." icon_state = "goldschlagerbottle" list_reagents = list(/datum/reagent/consumable/ethanol/goldschlager = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/cognac name = "Chateau de Baton premium cognac" desc = "A sweet and strongly alchoholic drink, made after numerous distillations and years of maturing. You might as well not scream 'SHITCURITY' this time." icon_state = "cognacbottle" list_reagents = list(/datum/reagent/consumable/ethanol/cognac = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/wine name = "Doublebeard's bearded special wine" @@ -490,6 +502,7 @@ desc = "A strong alcoholic drink brewed and distributed by" icon_state = "absinthebottle" list_reagents = list(/datum/reagent/consumable/ethanol/absinthe = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/absinthe/Initialize(mapload) . = ..() @@ -539,6 +552,7 @@ name = "Gwyn's premium absinthe" desc = "A potent alcoholic beverage, almost makes you forget the ash in your lungs." icon_state = "absinthepremium" + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium/redact() return @@ -556,24 +570,28 @@ icon_state = "hcider" volume = 50 list_reagents = list(/datum/reagent/consumable/ethanol/hcider = 50) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/amaretto name = "Luini Amaretto" desc = "A gentle, syrupy drink that tastes of almonds and apricots." icon_state = "disaronno" list_reagents = list(/datum/reagent/consumable/ethanol/amaretto = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/grappa name = "Phillipes well-aged Grappa" desc = "Bottle of Grappa." icon_state = "grappabottle" list_reagents = list(/datum/reagent/consumable/ethanol/grappa = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/sake name = "Ryo's traditional sake" desc = "Sweet as can be, and burns like fire going down." icon_state = "sakebottle" list_reagents = list(/datum/reagent/consumable/ethanol/sake = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/sake/Initialize(mapload) if(prob(10)) @@ -596,6 +614,7 @@ desc = "A bottle of pure Fernet Bronca, produced in Cordoba Space Station" icon_state = "fernetbottle" list_reagents = list(/datum/reagent/consumable/ethanol/fernet = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/bitters name = "Andromeda Bitters" @@ -603,12 +622,14 @@ icon_state = "bitters_bottle" volume = 30 list_reagents = list(/datum/reagent/consumable/ethanol/bitters = 30) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/curacao name = "Beekhof Blauw Curaçao" desc = "Still produced on the island of Curaçao, after all these years." icon_state = "curacao_bottle" list_reagents = list(/datum/reagent/consumable/ethanol/curacao = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/curacao/add_message_overlay() return //doesn't fit the sprite @@ -618,6 +639,7 @@ desc = "Ironically named, given it's made in Bermuda." icon_state = "navy_rum_bottle" list_reagents = list(/datum/reagent/consumable/ethanol/navy_rum = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/grenadine name = "Jester Grenadine" @@ -652,6 +674,7 @@ reagent_flags = TRANSPARENT spillable = FALSE list_reagents = list(/datum/reagent/consumable/ethanol/champagne = 100) + drink_type = ALCOHOL ///Used for sabrage; increases the chance of success per 1 force of the attacking sharp item var/sabrage_success_percentile = 5 ///Whether this bottle was a victim of a successful sabrage attempt @@ -805,6 +828,7 @@ desc = "You feel like you should give the bottle a good rub before opening." icon_state = "blazaambottle" list_reagents = list(/datum/reagent/consumable/ethanol/blazaam = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/trappist name = "Mont de Requin Trappistes Bleu" @@ -812,12 +836,14 @@ icon_state = "trappistbottle" volume = 50 list_reagents = list(/datum/reagent/consumable/ethanol/trappist = 50) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/hooch name = "hooch bottle" desc = "A bottle of rotgut. Its owner has applied some street wisdom to cleverly disguise it as a brown paper bag." icon_state = "hoochbottle" list_reagents = list(/datum/reagent/consumable/ethanol/hooch = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/hooch/add_message_overlay() return //doesn't fit the sprite @@ -827,6 +853,7 @@ desc = "It is said that the ancient Applalacians used these stoneware jugs to capture lightning in a bottle." icon_state = "moonshinebottle" list_reagents = list(/datum/reagent/consumable/ethanol/moonshine = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/moonshine/add_message_overlay() return //doesn't fit the sprite @@ -838,6 +865,7 @@ volume = 30 list_reagents = list(/datum/reagent/consumable/ethanol/mushi_kombucha = 30) isGlass = FALSE + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/hakka_mate name = "Hakka-Mate" @@ -850,18 +878,21 @@ desc = "A boozier form of shochu designed for mixing. Comes straight from Mars' Dusty City itself, Shu-Kouba." icon_state = "shochu_bottle" list_reagents = list(/datum/reagent/consumable/ethanol/shochu = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/yuyake name = "Moonlabor Yūyake" desc = "The distilled essence of disco and flared pants, captured like lightning in a bottle." icon_state = "yuyake_bottle" list_reagents = list(/datum/reagent/consumable/ethanol/yuyake = 100) + drink_type = ALCOHOL /obj/item/reagent_containers/cup/glass/bottle/coconut_rum name = "Breezy Shoals Coconut Rum" desc = "Live the breezy life with Breezy Shoals, made with only the *finest Caribbean rum." icon_state = "coconut_rum_bottle" list_reagents = list(/datum/reagent/consumable/ethanol/coconut_rum = 100) + drink_type = ALCOHOL ////////////////////////// MOLOTOV /////////////////////// /obj/item/reagent_containers/cup/glass/bottle/molotov From 47d57fdf397a3df7fde2ea267711a70b96d4624d Mon Sep 17 00:00:00 2001 From: "tgstation-ci[bot]" <179393467+tgstation-ci[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 06:32:14 +0000 Subject: [PATCH 06/11] Automatic changelog for PR #88355 [ci skip] --- html/changelogs/AutoChangeLog-pr-88355.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-88355.yml diff --git a/html/changelogs/AutoChangeLog-pr-88355.yml b/html/changelogs/AutoChangeLog-pr-88355.yml new file mode 100644 index 0000000000000..bcd6959751a78 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-88355.yml @@ -0,0 +1,4 @@ +author: "timothymtorres" +delete-after: True +changes: + - bugfix: "Fix drink labels for alcohol bottles" \ No newline at end of file From 0314511328f9dfc8425c3fc747715abe430718c5 Mon Sep 17 00:00:00 2001 From: SmArtKar <44720187+SmArtKar@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:33:31 +0300 Subject: [PATCH 07/11] AI laws and tape recorders no longer cause radio blips (#88285) ## About The Pull Request Closes #87423 ## Why It's Good For The Game The sound this produces when AI states laws on a non-common frequency is highly jarring and shouldn't be spammed every time someone from command asks AI to state laws (or security uses a tape recorder in the interrogation room). ## Changelog :cl: qol: AI laws and tape recorders no longer cause radio blips /:cl: --- code/__DEFINES/say.dm | 3 +++ code/game/objects/items/devices/radio/radio.dm | 4 ++-- code/game/objects/items/devices/taperecorder.dm | 4 ++-- code/modules/mob/living/silicon/silicon.dm | 10 +++++----- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm index 80c316f3585a9..d905129b19b74 100644 --- a/code/__DEFINES/say.dm +++ b/code/__DEFINES/say.dm @@ -53,6 +53,9 @@ #define MODE_VOCALCORDS "cords" #define MODE_KEY_VOCALCORDS "x" +/// Automatically playing a set of lines +#define MODE_SEQUENTIAL "sequential" + #define MODE_MAFIA "mafia" /// Applies singing characters to the message diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 72f3747b01121..ac9cbfec8211f 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -351,8 +351,8 @@ if(isliving(talking_movable)) var/mob/living/talking_living = talking_movable var/volume_modifier = (talking_living.client?.prefs.read_preference(/datum/preference/numeric/sound_radio_noise)) - if(radio_noise && talking_living.can_hear() && volume_modifier && signal.frequency != FREQ_COMMON) - var/sound/radio_noise = sound(sound('sound/items/radio/radio_talk.ogg', volume = volume_modifier)) + if(radio_noise && talking_living.can_hear() && volume_modifier && signal.frequency != FREQ_COMMON && !LAZYACCESS(message_mods, MODE_SEQUENTIAL)) + var/sound/radio_noise = sound('sound/items/radio/radio_talk.ogg', volume = volume_modifier) radio_noise.frequency = get_rand_frequency_low_range() SEND_SOUND(talking_living, radio_noise) diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index df0fbb928ed8c..433df9869d224 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -262,7 +262,7 @@ balloon_alert(usr, "recording ended") stoplag(1 SECONDS) //prevents multiple balloon alerts covering each other break - say("[mytape.storedinfo[i]]", sanitize=FALSE)//We want to display this properly, don't double encode + say("[mytape.storedinfo[i]]", sanitize=FALSE, message_mods = list(MODE_SEQUENTIAL = TRUE))//We want to display this properly, don't double encode if(mytape.storedinfo.len < i + 1) playsleepseconds = 1 sleep(1 SECONDS) @@ -270,7 +270,7 @@ playsleepseconds = mytape.timestamp[i + 1] - mytape.timestamp[i] if(playsleepseconds > 14 SECONDS) sleep(1 SECONDS) - say("Skipping [playsleepseconds/10] seconds of silence.") + say("Skipping [playsleepseconds/10] seconds of silence.", message_mods = list(MODE_SEQUENTIAL = TRUE)) playsleepseconds = 1 SECONDS i++ diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index d19526e584c79..210ed4798b99a 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -264,7 +264,7 @@ if (lawcache_zeroth) if (force || (lawcache_zeroth in lawcache_lawcheck)) - say("[radiomod] 0. [lawcache_zeroth]", forced = forced_log_message) + say("[radiomod] 0. [lawcache_zeroth]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE)) sleep(1 SECONDS) for (var/index in 1 to length(lawcache_hacked)) @@ -273,7 +273,7 @@ if (length(law) <= 0) continue if (force || (law in lawcache_hackedcheck)) - say("[radiomod] [num]. [law]", forced = forced_log_message) + say("[radiomod] [num]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE)) sleep(1 SECONDS) for (var/index in 1 to length(lawcache_ion)) @@ -282,7 +282,7 @@ if (length(law) <= 0) return if (force || (law in lawcache_ioncheck)) - say("[radiomod] [num]. [law]", forced = forced_log_message) + say("[radiomod] [num]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE)) sleep(1 SECONDS) var/number = 1 @@ -291,7 +291,7 @@ if (length(law) <= 0) continue if (force || (law in lawcache_lawcheck)) - say("[radiomod] [number]. [law]", forced = forced_log_message) + say("[radiomod] [number]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE)) number++ sleep(1 SECONDS) @@ -301,7 +301,7 @@ if (length(law) <= 0) continue if (force || (law in lawcache_lawcheck)) - say("[radiomod] [number]. [law]", forced = forced_log_message) + say("[radiomod] [number]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE)) number++ sleep(1 SECONDS) From f4e3efcf1e34c5dd3f712cda8de0a3941952009d Mon Sep 17 00:00:00 2001 From: "tgstation-ci[bot]" <179393467+tgstation-ci[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 06:33:50 +0000 Subject: [PATCH 08/11] Automatic changelog for PR #88285 [ci skip] --- html/changelogs/AutoChangeLog-pr-88285.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-88285.yml diff --git a/html/changelogs/AutoChangeLog-pr-88285.yml b/html/changelogs/AutoChangeLog-pr-88285.yml new file mode 100644 index 0000000000000..270c47117098f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-88285.yml @@ -0,0 +1,4 @@ +author: "SmArtKar" +delete-after: True +changes: + - qol: "AI laws and tape recorders no longer cause radio blips" \ No newline at end of file From 57eeb5290c9a9f42cb6abc8c9fb35dda74ed4cea Mon Sep 17 00:00:00 2001 From: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:04:26 +0530 Subject: [PATCH 09/11] Minor maintenance for borg Inducer (#88250) ## About The Pull Request - Moves borg inducer to the same file as other inducers for easy maintainence - Compressed code for its `get_cell()` proc - Fixes examines & screentips for borg inducer ## Changelog :cl: code: slightly improved code for borg inducer spellcheck: fixes examines & screentips for borg inducer /:cl: --------- Co-authored-by: AMyriad <143908044+AMyriad@users.noreply.github.com> --- code/game/objects/items/inducer.dm | 35 +++++++++++++++++++ .../objects/items/robot/robot_upgrades.dm | 19 ---------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/code/game/objects/items/inducer.dm b/code/game/objects/items/inducer.dm index 0e430589981e4..948606d6d4068 100644 --- a/code/game/objects/items/inducer.dm +++ b/code/game/objects/items/inducer.dm @@ -61,6 +61,20 @@ /obj/item/inducer/examine(mob/living/user) . = ..() + . += examine_hints(user) + +/** + * Gives description for this inducer + * Arguments + * + * * mob/living/user - the mob we are returning the description to + */ +/obj/item/inducer/proc/examine_hints(mob/living/user) + PROTECTED_PROC(TRUE) + SHOULD_BE_PURE(TRUE) + + . = list() + var/obj/item/stock_parts/power_store/our_cell = get_cell(src, user) if(!QDELETED(our_cell)) . += span_notice("Its display shows: [display_energy(our_cell.charge)].") @@ -231,3 +245,24 @@ desc = "A tool for inductively charging internal power cells. This one has a suspicious colour scheme, and seems to be rigged to transfer charge at a much faster rate." power_transfer_multiplier = 2 // 2x the base speed powerdevice = /obj/item/stock_parts/power_store/battery/super + +/obj/item/inducer/cyborg + name = "internal inducer" + icon = 'icons/obj/tools.dmi' + icon_state = "inducer-engi" + powerdevice = null + +/obj/item/inducer/cyborg/add_context(atom/source, list/context, obj/item/held_item, mob/user) + return NONE + +/obj/item/inducer/cyborg/examine_hints(mob/living/user) + return list() + +/obj/item/inducer/cyborg/get_cell(atom/movable/interface, mob/living/silicon/robot/silicon_friend) + return istype(silicon_friend) ? silicon_friend.cell : null + +/obj/item/inducer/cyborg/screwdriver_act(mob/living/user, obj/item/tool) + return ITEM_INTERACT_FAILURE + +/obj/item/inducer/cyborg/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + return ITEM_INTERACT_FAILURE diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index b65c4a7ae90df..bbebd91e7cb0c 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -647,27 +647,8 @@ require_model = TRUE model_type = list(/obj/item/robot_model/engineering, /obj/item/robot_model/saboteur) model_flags = BORG_MODEL_ENGINEERING - items_to_add = list(/obj/item/inducer/cyborg) -/obj/item/inducer/cyborg - name = "Internal inducer" - icon = 'icons/obj/tools.dmi' - icon_state = "inducer-engi" - powerdevice = null - -/obj/item/inducer/cyborg/get_cell() - var/obj/item/robot_model/possible_model = loc - var/mob/living/silicon/robot/silicon_friend = istype(possible_model) ? possible_model.robot : possible_model - if(istype(silicon_friend)) - . = silicon_friend.cell - -/obj/item/inducer/cyborg/screwdriver_act(mob/living/user, obj/item/tool) - return NONE - -/obj/item/inducer/cyborg/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - return ITEM_INTERACT_FAILURE - /obj/item/borg/upgrade/pinpointer name = "medical cyborg crew pinpointer" desc = "A crew pinpointer module for the medical cyborg. Permits remote access to the crew monitor." From 09e32073ba7327e14dcd9ddf45fcaeadd1c4ad92 Mon Sep 17 00:00:00 2001 From: "tgstation-ci[bot]" <179393467+tgstation-ci[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 06:34:46 +0000 Subject: [PATCH 10/11] Automatic changelog for PR #88250 [ci skip] --- html/changelogs/AutoChangeLog-pr-88250.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-88250.yml diff --git a/html/changelogs/AutoChangeLog-pr-88250.yml b/html/changelogs/AutoChangeLog-pr-88250.yml new file mode 100644 index 0000000000000..320cbf5a1e7ff --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-88250.yml @@ -0,0 +1,5 @@ +author: "SyncIt21" +delete-after: True +changes: + - code_imp: "slightly improved code for borg inducer" + - spellcheck: "fixes examines & screentips for borg inducer" \ No newline at end of file From 558e6528c1a1b58b344d66adaac4593f6ef5797a Mon Sep 17 00:00:00 2001 From: Ivory Date: Fri, 6 Dec 2024 07:35:31 +0100 Subject: [PATCH 11/11] Reorganizes and splits shuttle code (#88228) ## About The Pull Request refer to title No code changes were made here, i just copypasted code around The only real difference is that I removed a pretty useless define that depended on TESTING because it got in my way of splitting emergency.dm tbh i didnt want a 50k line refactor pr that nobody is going to review so im getting it out of the way in a separate PR ## Why It's Good For The Game Shuttle code is literally all over the place please help me oh gosh ## Changelog Nothing player facing or developer facing (at least I really hope so) --- code/__DEFINES/shuttles.dm | 10 + code/__HELPERS/shuttle.dm | 52 + code/modules/shuttle/emergency.dm | 888 -------------- .../modules/shuttle/{ => misc}/manipulator.dm | 0 code/modules/shuttle/{ => misc}/medisim.dm | 0 code/modules/shuttle/{ => misc}/ripple.dm | 0 .../{ => misc}/spaceship_navigation_beacon.dm | 0 code/modules/shuttle/{ => misc}/special.dm | 0 .../shuttle/mobile_port/mobile_port.dm | 781 +++++++++++++ .../shuttle_move.dm} | 3 +- .../shuttle_move_callbacks.dm} | 0 .../shuttle_rotate_callbacks.dm} | 0 .../{ => mobile_port/variants}/arrivals.dm | 0 .../{ => mobile_port/variants}/assault_pod.dm | 0 .../variants}/battlecruiser_starfury.dm | 0 .../{ => mobile_port/variants}/elevator.dm | 0 .../variants/emergency/emergency.dm | 311 +++++ .../variants/emergency/emergency_console.dm | 316 +++++ .../variants/emergency/emergency_types.dm | 39 + .../mobile_port/variants/emergency/pods.dm | 211 ++++ .../{ => mobile_port/variants}/ferry.dm | 0 .../{ => mobile_port/variants}/infiltrator.dm | 0 .../{ => mobile_port/variants}/supply.dm | 0 code/modules/shuttle/shuttle.dm | 1037 +---------------- .../{ => shuttle_consoles}/monastery.dm | 0 .../navigation_computer.dm | 0 .../shuttle_console.dm} | 0 .../{ => shuttle_consoles}/syndicate.dm | 0 .../{ => shuttle_consoles}/white_ship.dm | 0 .../shuttle/stationary_port/port_types.dm | 100 ++ .../stationary_port/stationary_port.dm | 91 ++ tgstation.dme | 49 +- 32 files changed, 1944 insertions(+), 1944 deletions(-) create mode 100644 code/__HELPERS/shuttle.dm delete mode 100644 code/modules/shuttle/emergency.dm rename code/modules/shuttle/{ => misc}/manipulator.dm (100%) rename code/modules/shuttle/{ => misc}/medisim.dm (100%) rename code/modules/shuttle/{ => misc}/ripple.dm (100%) rename code/modules/shuttle/{ => misc}/spaceship_navigation_beacon.dm (100%) rename code/modules/shuttle/{ => misc}/special.dm (100%) create mode 100644 code/modules/shuttle/mobile_port/mobile_port.dm rename code/modules/shuttle/{docking.dm => mobile_port/shuttle_move.dm} (98%) rename code/modules/shuttle/{on_move.dm => mobile_port/shuttle_move_callbacks.dm} (100%) rename code/modules/shuttle/{shuttle_rotate.dm => mobile_port/shuttle_rotate_callbacks.dm} (100%) rename code/modules/shuttle/{ => mobile_port/variants}/arrivals.dm (100%) rename code/modules/shuttle/{ => mobile_port/variants}/assault_pod.dm (100%) rename code/modules/shuttle/{ => mobile_port/variants}/battlecruiser_starfury.dm (100%) rename code/modules/shuttle/{ => mobile_port/variants}/elevator.dm (100%) create mode 100644 code/modules/shuttle/mobile_port/variants/emergency/emergency.dm create mode 100644 code/modules/shuttle/mobile_port/variants/emergency/emergency_console.dm create mode 100644 code/modules/shuttle/mobile_port/variants/emergency/emergency_types.dm create mode 100644 code/modules/shuttle/mobile_port/variants/emergency/pods.dm rename code/modules/shuttle/{ => mobile_port/variants}/ferry.dm (100%) rename code/modules/shuttle/{ => mobile_port/variants}/infiltrator.dm (100%) rename code/modules/shuttle/{ => mobile_port/variants}/supply.dm (100%) rename code/modules/shuttle/{ => shuttle_consoles}/monastery.dm (100%) rename code/modules/shuttle/{ => shuttle_consoles}/navigation_computer.dm (100%) rename code/modules/shuttle/{computer.dm => shuttle_consoles/shuttle_console.dm} (100%) rename code/modules/shuttle/{ => shuttle_consoles}/syndicate.dm (100%) rename code/modules/shuttle/{ => shuttle_consoles}/white_ship.dm (100%) create mode 100644 code/modules/shuttle/stationary_port/port_types.dm create mode 100644 code/modules/shuttle/stationary_port/stationary_port.dm diff --git a/code/__DEFINES/shuttles.dm b/code/__DEFINES/shuttles.dm index 759121e3b8dd8..12f15ab1e68dc 100644 --- a/code/__DEFINES/shuttles.dm +++ b/code/__DEFINES/shuttles.dm @@ -62,6 +62,7 @@ #define ENGINE_COEFF_MIN 0.5 #define ENGINE_COEFF_MAX 2 #define ENGINE_DEFAULT_MAXSPEED_ENGINES 5 +#define ENGINE_START_TIME 100 // Alert level related #define ALERT_COEFF_AUTOEVAC_NORMAL 2.5 @@ -120,3 +121,12 @@ #define SHUTTLE_EVENT_MISS_SHUTTLE 1 << 0 ///spawned stuff should hit the shuttle #define SHUTTLE_EVENT_HIT_SHUTTLE 1 << 1 + +// Hijack stages + +#define HIJACK_NOT_BEGUN 0 +#define HIJACK_STAGE_1 1 +#define HIJACK_STAGE_2 2 +#define HIJACK_STAGE_3 3 +#define HIJACK_STAGE_4 4 +#define HIJACK_COMPLETED 5 diff --git a/code/__HELPERS/shuttle.dm b/code/__HELPERS/shuttle.dm new file mode 100644 index 0000000000000..4f866e22384dd --- /dev/null +++ b/code/__HELPERS/shuttle.dm @@ -0,0 +1,52 @@ +/// Helper proc that tests to ensure all whiteship templates can spawn at their docking port, and logs their sizes +/// This should be a unit test, but too much of our other code breaks during shuttle movement, so not yet, not yet. +/proc/test_whiteship_sizes() + var/obj/docking_port/stationary/port_type = /obj/docking_port/stationary/picked/whiteship + var/datum/turf_reservation/docking_yard = SSmapping.request_turf_block_reservation( + initial(port_type.width), + initial(port_type.height), + 1, + ) + var/turf/bottom_left = docking_yard.bottom_left_turfs[1] + var/turf/spawnpoint = locate( + bottom_left.x + initial(port_type.dwidth), + bottom_left.y + initial(port_type.dheight), + bottom_left.z, + ) + + var/obj/docking_port/stationary/picked/whiteship/port = new(spawnpoint) + var/list/ids = port.shuttlekeys + var/height = 0 + var/width = 0 + var/dheight = 0 + var/dwidth = 0 + var/delta_height = 0 + var/delta_width = 0 + for(var/id in ids) + var/datum/map_template/shuttle/our_template = SSmapping.shuttle_templates[id] + // We do a standard load here so any errors will properly runtimes + var/obj/docking_port/mobile/ship = SSshuttle.action_load(our_template, port) + if(ship) + ship.jumpToNullSpace() + ship = null + // Yes this is very hacky, but we need to both allow loading a template that's too big to be an error state + // And actually get the sizing information from every shuttle + SSshuttle.load_template(our_template) + var/obj/docking_port/mobile/theoretical_ship = SSshuttle.preview_shuttle + if(theoretical_ship) + height = max(theoretical_ship.height, height) + width = max(theoretical_ship.width, width) + dheight = max(theoretical_ship.dheight, dheight) + dwidth = max(theoretical_ship.dwidth, dwidth) + delta_height = max(theoretical_ship.height - theoretical_ship.dheight, delta_height) + delta_width = max(theoretical_ship.width - theoretical_ship.dwidth, delta_width) + theoretical_ship.jumpToNullSpace() + qdel(port, TRUE) + log_world("Whiteship sizing information. Use this to set the docking port, and the map size\n\ + Max Height: [height] \n\ + Max Width: [width] \n\ + Max DHeight: [dheight] \n\ + Max DWidth: [dwidth] \n\ + The following are the safest bet for map sizing. Anything smaller then this could in the worst case not fit in the docking port\n\ + Max Combined Width: [height + dheight] \n\ + Max Combinded Height [width + dwidth]") diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm deleted file mode 100644 index 2753dfc65f4df..0000000000000 --- a/code/modules/shuttle/emergency.dm +++ /dev/null @@ -1,888 +0,0 @@ -#define TIME_LEFT (SSshuttle.emergency.timeLeft()) -#define ENGINES_START_TIME 100 -#define ENGINES_STARTED (SSshuttle.emergency.mode == SHUTTLE_IGNITING) -#define IS_DOCKED (SSshuttle.emergency.mode == SHUTTLE_DOCKED || (ENGINES_STARTED)) -#define SHUTTLE_CONSOLE_ACTION_DELAY (5 SECONDS) - -#define NOT_BEGUN 0 -#define STAGE_1 1 -#define STAGE_2 2 -#define STAGE_3 3 -#define STAGE_4 4 -#define HIJACKED 5 - -/obj/machinery/computer/emergency_shuttle - name = "emergency shuttle console" - desc = "For shuttle control." - icon_screen = "shuttle" - icon_keyboard = "tech_key" - resistance_flags = INDESTRUCTIBLE - var/auth_need = 3 - var/list/authorized = list() - var/list/acted_recently = list() - var/hijack_last_stage_increase = 0 SECONDS - var/hijack_stage_time = 5 SECONDS - var/hijack_stage_cooldown = 5 SECONDS - var/hijack_flight_time_increase = 30 SECONDS - var/hijack_completion_flight_time_set = 10 SECONDS //How long in deciseconds to set shuttle's timer after hijack is done. - var/hijack_hacking = FALSE - var/hijack_announce = TRUE - -/obj/machinery/computer/emergency_shuttle/examine(mob/user) - . = ..() - if(hijack_announce) - . += span_danger("Security systems present on console. Any unauthorized tampering will result in an emergency announcement.") - if(user?.mind?.get_hijack_speed()) - . += span_danger("Alt click on this to attempt to hijack the shuttle. This will take multiple tries (current: stage [SSshuttle.emergency.hijack_status]/[HIJACKED]).") - . += span_notice("It will take you [(hijack_stage_time * user.mind.get_hijack_speed()) / 10] seconds to reprogram a stage of the shuttle's navigational firmware, and the console will undergo automated timed lockout for [hijack_stage_cooldown/10] seconds after each stage.") - if(hijack_announce) - . += span_warning("It is probably best to fortify your position as to be uninterrupted during the attempt, given the automatic announcements..") - -/obj/machinery/computer/emergency_shuttle/attackby(obj/item/I, mob/user,params) - if(isidcard(I)) - say("Please equip your ID card into your ID slot to authenticate.") - . = ..() - -/obj/machinery/computer/emergency_shuttle/ui_state(mob/user) - return GLOB.human_adjacent_state - -/obj/machinery/computer/emergency_shuttle/ui_interact(mob/user, datum/tgui/ui) - . = ..() - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "EmergencyShuttleConsole", name) - ui.open() - -/obj/machinery/computer/emergency_shuttle/ui_data(user) - var/list/data = list() - - data["timer_str"] = SSshuttle.emergency.getTimerStr() - data["engines_started"] = ENGINES_STARTED - data["authorizations_remaining"] = max((auth_need - authorized.len), 0) - var/list/A = list() - for(var/i in authorized) - var/obj/item/card/id/ID = i - var/name = ID.registered_name - var/job = ID.assignment - - if(obj_flags & EMAGGED) - name = Gibberish(name) - job = Gibberish(job) - A += list(list("name" = name, "job" = job)) - data["authorizations"] = A - - data["enabled"] = (IS_DOCKED && !ENGINES_STARTED) && !(user in acted_recently) - data["emagged"] = obj_flags & EMAGGED ? 1 : 0 - return data - -/obj/machinery/computer/emergency_shuttle/ui_act(action, params, datum/tgui/ui) - . = ..() - if(.) - return - if(ENGINES_STARTED) // past the point of no return - return - if(!IS_DOCKED) // shuttle computer only has uses when onstation - return - if(SSshuttle.emergency.mode == SHUTTLE_DISABLED) // admins have disabled the shuttle. - return - if(!isliving(usr)) - return - - var/area/my_area = get_area(src) - if(!istype(my_area, /area/shuttle/escape)) - say("Error - Network connectivity: Console has lost connection to the shuttle.") - return - - var/mob/living/user = usr - . = FALSE - - var/obj/item/card/id/ID = user.get_idcard(TRUE) - - if(!ID) - to_chat(user, span_warning("You don't have an ID.")) - return - - if(!(ACCESS_COMMAND in ID.access)) - to_chat(user, span_warning("The access level of your card is not high enough.")) - return - - if (user in acted_recently) - return - - var/old_len = authorized.len - addtimer(CALLBACK(src, PROC_REF(clear_recent_action), user), SHUTTLE_CONSOLE_ACTION_DELAY) - - switch(action) - if("authorize") - . = authorize(user) - - if("repeal") - authorized -= ID - - if("abort") - if(authorized.len) - // Abort. The action for when heads are fighting over whether - // to launch early. - authorized.Cut() - . = TRUE - - if((old_len != authorized.len) && !ENGINES_STARTED) - var/alert = (authorized.len > old_len) - var/repeal = (authorized.len < old_len) - var/remaining = max(0, auth_need - authorized.len) - if(authorized.len && remaining) - minor_announce("[remaining] authorizations needed until shuttle is launched early", null, alert) - if(repeal) - minor_announce("Early launch authorization revoked, [remaining] authorizations needed") - - acted_recently += user - SStgui.update_user_uis(user, src) - -/obj/machinery/computer/emergency_shuttle/proc/authorize(mob/living/user, source) - var/obj/item/card/id/ID = user.get_idcard(TRUE) - - if(ID in authorized) - return FALSE - for(var/i in authorized) - var/obj/item/card/id/other = i - if(other.registered_name == ID.registered_name) - return FALSE // No using IDs with the same name - - authorized += ID - - message_admins("[ADMIN_LOOKUPFLW(user)] has authorized early shuttle launch") - log_shuttle("[key_name(user)] has authorized early shuttle launch in [COORD(src)]") - // Now check if we're on our way - . = TRUE - process(SSMACHINES_DT) - -/obj/machinery/computer/emergency_shuttle/proc/clear_recent_action(mob/user) - acted_recently -= user - if (!QDELETED(user)) - SStgui.update_user_uis(user, src) - -/obj/machinery/computer/emergency_shuttle/process() - // Launch check is in process in case auth_need changes for some reason - // probably external. - . = FALSE - if(!SSshuttle.emergency) - return - - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - authorized.Cut() - obj_flags &= ~(EMAGGED) - - if(ENGINES_STARTED || (!IS_DOCKED)) - return . - - // Check to see if we've reached criteria for early launch - if((authorized.len >= auth_need) || (obj_flags & EMAGGED)) - // shuttle timers use 1/10th seconds internally - SSshuttle.emergency.setTimer(ENGINES_START_TIME) - var/system_error = obj_flags & EMAGGED ? "SYSTEM ERROR:" : null - minor_announce("The emergency shuttle will launch in \ - [TIME_LEFT] seconds", system_error, alert=TRUE) - . = TRUE - -/obj/machinery/computer/emergency_shuttle/proc/increase_hijack_stage() - var/obj/docking_port/mobile/emergency/shuttle = SSshuttle.emergency - // Begin loading this early, prevents a delay when the shuttle goes to land - INVOKE_ASYNC(SSmapping, TYPE_PROC_REF(/datum/controller/subsystem/mapping, lazy_load_template), LAZY_TEMPLATE_KEY_NUKIEBASE) - - shuttle.hijack_status++ - if(hijack_announce) - announce_hijack_stage() - hijack_last_stage_increase = world.time - say("Navigational protocol error! Rebooting systems.") - if(shuttle.mode == SHUTTLE_ESCAPE) - if(shuttle.hijack_status == HIJACKED) - shuttle.setTimer(hijack_completion_flight_time_set) - else - shuttle.setTimer(shuttle.timeLeft(1) + hijack_flight_time_increase) //give the guy more time to hijack if it's already in flight. - return shuttle.hijack_status - -/obj/machinery/computer/emergency_shuttle/click_alt(mob/living/user) - if(!isliving(user)) - return NONE - attempt_hijack_stage(user) - return CLICK_ACTION_SUCCESS - -/obj/machinery/computer/emergency_shuttle/proc/attempt_hijack_stage(mob/living/user) - if(!user.CanReach(src)) - return - if(HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) - to_chat(user, span_warning("You need your hands free before you can manipulate [src].")) - return - var/area/my_area = get_area(src) - if(!istype(my_area, /area/shuttle/escape)) - say("Error - Network connectivity: Console has lost connection to the shuttle.") - return - if(!user?.mind?.get_hijack_speed()) - to_chat(user, span_warning("You manage to open a user-mode shell on [src], and hundreds of lines of debugging output fly through your vision. It is probably best to leave this alone.")) - return - if(!EMERGENCY_AT_LEAST_DOCKED) // prevent advancing hijack stages on BYOS shuttles until the shuttle has "docked" - to_chat(user, span_warning("The flight plans for the shuttle haven't been loaded yet, you can't hack this right now.")) - return - if(hijack_hacking == TRUE) - return - if(SSshuttle.emergency.hijack_status >= HIJACKED) - to_chat(user, span_warning("The emergency shuttle is already loaded with a corrupt navigational payload. What more do you want from it?")) - return - if(hijack_last_stage_increase >= world.time - hijack_stage_cooldown) - say("Error - Catastrophic software error detected. Input is currently on timeout.") - return - hijack_hacking = TRUE - to_chat(user, span_boldwarning("You [SSshuttle.emergency.hijack_status == NOT_BEGUN? "begin" : "continue"] to override [src]'s navigational protocols.")) - say("Software override initiated.") - var/turf/console_hijack_turf = get_turf(src) - message_admins("[src] is being overriden for hijack by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(console_hijack_turf)]") - user.log_message("is hijacking [src].", LOG_GAME) - . = FALSE - if(do_after(user, hijack_stage_time * (1 / user.mind.get_hijack_speed()), target = src)) - increase_hijack_stage() - console_hijack_turf = get_turf(src) - message_admins("[ADMIN_LOOKUPFLW(user)] has hijacked [src] in [ADMIN_VERBOSEJMP(console_hijack_turf)]. Hijack stage increased to stage [SSshuttle.emergency.hijack_status] out of [HIJACKED].") - user.log_message("has hijacked [src]. Hijack stage increased to stage [SSshuttle.emergency.hijack_status] out of [HIJACKED].", LOG_GAME) - . = TRUE - to_chat(user, span_notice("You reprogram some of [src]'s programming, putting it on timeout for [hijack_stage_cooldown/10] seconds.")) - visible_message( - span_warning("[user.name] appears to be tampering with [src]."), - blind_message = span_hear("You hear someone tapping computer keys."), - vision_distance = COMBAT_MESSAGE_RANGE, - ignored_mobs = user - ) - hijack_hacking = FALSE - -/obj/machinery/computer/emergency_shuttle/proc/announce_hijack_stage() - var/msg - switch(SSshuttle.emergency.hijack_status) - if(NOT_BEGUN) - return - if(STAGE_1) - msg = "AUTHENTICATING - FAIL. AUTHENTICATING - FAIL. AUTHENTICATING - FAI###### Welcome, technician JOHN DOE." - if(STAGE_2) - msg = "Warning: Navigational route fails \"IS_AUTHORIZED\". Please try againNN[scramble_message_replace_chars("againagainagainagainagain", 70)]." - if(STAGE_3) - msg = "CRC mismatch at ~h~ in calculated route buffer. Full reset initiated of FTL_NAVIGATION_SERVICES. Memory decrypted for automatic repair." - if(STAGE_4) - msg = "~ACS_directive module_load(cyberdyne.exploit.nanotrasen.shuttlenav)... NT key mismatch. Confirm load? Y...###Reboot complete. $SET transponder_state = 0; System link initiated with connected engines..." - if(HIJACKED) - msg = "SYSTEM OVERRIDE - Resetting course to \[[scramble_message_replace_chars("###########", 100)]\] \ - ([scramble_message_replace_chars("#######", 100)]/[scramble_message_replace_chars("#######", 100)]/[scramble_message_replace_chars("#######", 100)]) \ - {AUTH - ROOT (uid: 0)}.\ - [SSshuttle.emergency.mode == SHUTTLE_ESCAPE ? "Diverting from existing route - Bluespace exit in \ - [hijack_completion_flight_time_set >= INFINITY ? "[scramble_message_replace_chars("\[ERROR\]")]" : hijack_completion_flight_time_set/10] seconds." : ""]" - minor_announce(scramble_message_replace_chars(msg, replaceprob = 10), "Emergency Shuttle", TRUE) - -/obj/machinery/computer/emergency_shuttle/emag_act(mob/user, obj/item/card/emag/emag_card) - // How did you even get on the shuttle before it go to the station? - if(!IS_DOCKED) - return FALSE - - if((obj_flags & EMAGGED) || ENGINES_STARTED) //SYSTEM ERROR: THE SHUTTLE WILL LA-SYSTEM ERROR: THE SHUTTLE WILL LA-SYSTEM ERROR: THE SHUTTLE WILL LAUNCH IN 10 SECONDS - balloon_alert(user, "shuttle already about to launch!") - return FALSE - - var/time = TIME_LEFT - if (user) - message_admins("[ADMIN_LOOKUPFLW(user)] has emagged the emergency shuttle [time] seconds before launch.") - log_shuttle("[key_name(user)] has emagged the emergency shuttle in [COORD(src)] [time] seconds before launch.") - else - message_admins("The emergency shuttle was emagged [time] seconds before launch, with no emagger.") - log_shuttle("The emergency shuttle was emagged in [COORD(src)] [time] seconds before launch, with no emagger.") - - obj_flags |= EMAGGED - SSshuttle.emergency.movement_force = list("KNOCKDOWN" = 60, "THROW" = 20)//YOUR PUNY SEATBELTS can SAVE YOU NOW, MORTAL - for(var/i in 1 to 10) - // the shuttle system doesn't know who these people are, but they - // must be important, surely - var/obj/item/card/id/ID = new(src) - var/datum/job/J = pick(SSjob.joinable_occupations) - ID.registered_name = generate_random_name_species_based(species_type = /datum/species/human) - ID.assignment = J.title - - authorized += ID - - process(SSMACHINES_DT) - return TRUE - -/obj/machinery/computer/emergency_shuttle/Destroy() - // Our fake IDs that the emag generated are just there for colour - // They're not supposed to be accessible - - for(var/obj/item/card/id/ID in src) - qdel(ID) - if(authorized?.len) - authorized.Cut() - authorized = null - - . = ..() - -/obj/docking_port/mobile/emergency - name = "emergency shuttle" - shuttle_id = "emergency" - dir = EAST - port_direction = WEST - var/sound_played = 0 //If the launch sound has been sent to all players on the shuttle itself - var/hijack_status = NOT_BEGUN - -/obj/docking_port/mobile/emergency/Initialize(mapload) - . = ..() - - setup_shuttle_events() - -/obj/docking_port/mobile/emergency/canDock(obj/docking_port/stationary/S) - return SHUTTLE_CAN_DOCK //If the emergency shuttle can't move, the whole game breaks, so it will force itself to land even if it has to crush a few departments in the process - -/obj/docking_port/mobile/emergency/register() - . = ..() - SSshuttle.emergency = src - -/obj/docking_port/mobile/emergency/Destroy(force) - if(force) - // This'll make the shuttle subsystem use the backup shuttle. - if(src == SSshuttle.emergency) - // If we're the selected emergency shuttle - SSshuttle.emergencyDeregister() - - . = ..() - -/obj/docking_port/mobile/emergency/request(obj/docking_port/stationary/S, area/signal_origin, reason, red_alert, set_coefficient=null) - if(!isnum(set_coefficient)) - set_coefficient = SSsecurity_level.current_security_level.shuttle_call_time_mod - alert_coeff = set_coefficient - var/call_time = SSshuttle.emergency_call_time * alert_coeff * engine_coeff - switch(mode) - // The shuttle can not normally be called while "recalling", so - // if this proc is called, it's via admin fiat - if(SHUTTLE_RECALL, SHUTTLE_IDLE, SHUTTLE_CALL) - mode = SHUTTLE_CALL - setTimer(call_time) - else - return - - SSshuttle.emergencyCallAmount++ - - if(prob(70)) - SSshuttle.emergency_last_call_loc = signal_origin - else - SSshuttle.emergency_last_call_loc = null - - priority_announce( - text = "The emergency shuttle has been called. [red_alert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [(timeLeft(60 SECONDS))] minutes.[reason][SSshuttle.emergency_last_call_loc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ][SSshuttle.admin_emergency_no_recall ? "\n\nWarning: Shuttle recall subroutines disabled; Recall not possible." : ""]", - title = "Emergency Shuttle Dispatched", - sound = ANNOUNCER_SHUTTLECALLED, - sender_override = "Emergency Shuttle Uplink Alert", - color_override = "orange", - ) - -/obj/docking_port/mobile/emergency/cancel(area/signalOrigin) - if(mode != SHUTTLE_CALL) - return - if(SSshuttle.emergency_no_recall) - return - - invertTimer() - mode = SHUTTLE_RECALL - - if(prob(70)) - SSshuttle.emergency_last_call_loc = signalOrigin - else - SSshuttle.emergency_last_call_loc = null - priority_announce( - text = "The emergency shuttle has been recalled.[SSshuttle.emergency_last_call_loc ? " Recall signal traced. Results can be viewed on any communications console." : "" ]", - title = "Emergency Shuttle Recalled", - sound = ANNOUNCER_SHUTTLERECALLED, - sender_override = "Emergency Shuttle Uplink Alert", - color_override = "orange", - ) - - SSticker.emergency_reason = null - -/** - * Proc that handles checking if the emergency shuttle was successfully hijacked via being the only people present on the shuttle for the elimination hijack or highlander objective - * - * Checks for all mobs on the shuttle, checks their status, and checks if they're - * borgs or simple animals. Depending on the args, certain mobs may be ignored, - * and the presence of other antags may or may not invalidate a hijack. - * Args: - * filter_by_human, default TRUE, tells the proc that only humans should block a hijack. Borgs and animals are ignored and will not block if this is TRUE. - * solo_hijack, default FALSE, tells the proc to fail with multiple hijackers, such as for Highlander mode. - */ -/obj/docking_port/mobile/emergency/proc/elimination_hijack(filter_by_human = TRUE, solo_hijack = FALSE) - var/has_people = FALSE - var/hijacker_count = 0 - for(var/mob/living/player in GLOB.player_list) - if(player.mind) - if(player.stat != DEAD) - if(issilicon(player) && filter_by_human) //Borgs are technically dead anyways - continue - if(isanimal_or_basicmob(player) && filter_by_human) //animals don't count - continue - if(isbrain(player)) //also technically dead - continue - if(shuttle_areas[get_area(player)]) - has_people = TRUE - var/location = get_area(player.mind.current) - //Non-antag present. Can't hijack. - if(!(player.mind.has_antag_datum(/datum/antagonist)) && !istype(location, /area/shuttle/escape/brig)) - return FALSE - //Antag present, doesn't stop but let's see if we actually want to hijack - var/prevent = FALSE - for(var/datum/antagonist/A in player.mind.antag_datums) - if(A.can_elimination_hijack == ELIMINATION_ENABLED) - hijacker_count += 1 - prevent = FALSE - break //If we have both prevent and hijacker antags assume we want to hijack. - else if(A.can_elimination_hijack == ELIMINATION_PREVENT) - prevent = TRUE - if(prevent) - return FALSE - - //has people AND either there's only one hijacker or there's any but solo_hijack is disabled - return has_people && ((hijacker_count == 1) || (hijacker_count && !solo_hijack)) - -/obj/docking_port/mobile/emergency/proc/is_hijacked() - return hijack_status == HIJACKED - -/obj/docking_port/mobile/emergency/proc/ShuttleDBStuff() - set waitfor = FALSE - if(!SSdbcore.Connect()) - return - var/datum/db_query/query_round_shuttle_name = SSdbcore.NewQuery({" - UPDATE [format_table_name("round")] SET shuttle_name = :name WHERE id = :round_id - "}, list("name" = name, "round_id" = GLOB.round_id)) - query_round_shuttle_name.Execute() - qdel(query_round_shuttle_name) - -/obj/docking_port/mobile/emergency/check() - if(!timer) - return - var/time_left = timeLeft(1) - - // The emergency shuttle doesn't work like others so this - // ripple check is slightly different - if(!ripples.len && (time_left <= SHUTTLE_RIPPLE_TIME) && ((mode == SHUTTLE_CALL) || (mode == SHUTTLE_ESCAPE))) - var/destination - if(mode == SHUTTLE_CALL) - destination = SSshuttle.getDock("emergency_home") - else if(mode == SHUTTLE_ESCAPE) - destination = SSshuttle.getDock("emergency_away") - create_ripples(destination) - - switch(mode) - if(SHUTTLE_RECALL) - if(time_left <= 0) - mode = SHUTTLE_IDLE - timer = 0 - if(SHUTTLE_CALL) - if(time_left <= 0) - //move emergency shuttle to station - if(initiate_docking(SSshuttle.getDock("emergency_home")) != DOCKING_SUCCESS) - setTimer(20) - return - mode = SHUTTLE_DOCKED - setTimer(SSshuttle.emergency_dock_time) - send2adminchat("Server", "The Emergency Shuttle has docked with the station.") - priority_announce( - text = "[SSshuttle.emergency] has docked with the station. You have [DisplayTimeText(SSshuttle.emergency_dock_time)] to board the emergency shuttle.", - title = "Emergency Shuttle Arrival", - sound = ANNOUNCER_SHUTTLEDOCK, - sender_override = "Emergency Shuttle Uplink Alert", - color_override = "orange", - ) - ShuttleDBStuff() - addtimer(CALLBACK(src, PROC_REF(announce_shuttle_events)), 20 SECONDS) - - - if(SHUTTLE_DOCKED) - if(time_left <= ENGINES_START_TIME) - mode = SHUTTLE_IGNITING - SSshuttle.checkHostileEnvironment() - if(mode == SHUTTLE_STRANDED) - return - for(var/A in SSshuttle.mobile_docking_ports) - var/obj/docking_port/mobile/M = A - if(M.launch_status == UNLAUNCHED) //Pods will not launch from the mine/planet, and other ships won't launch unless we tell them to. - M.check_transit_zone() - - if(SHUTTLE_IGNITING) - var/success = TRUE - SSshuttle.checkHostileEnvironment() - if(mode == SHUTTLE_STRANDED) - return - - success &= (check_transit_zone() == TRANSIT_READY) - for(var/A in SSshuttle.mobile_docking_ports) - var/obj/docking_port/mobile/M = A - if(M.launch_status == UNLAUNCHED) - success &= (M.check_transit_zone() == TRANSIT_READY) - if(!success) - setTimer(ENGINES_START_TIME) - - if(time_left <= 50 && !sound_played) //4 seconds left:REV UP THOSE ENGINES BOYS. - should sync up with the launch - sound_played = 1 //Only rev them up once. - var/list/areas = list() - for(var/area/shuttle/escape/E in GLOB.areas) - areas += E - hyperspace_sound(HYPERSPACE_WARMUP, areas) - - if(time_left <= 0 && !SSshuttle.emergency_no_escape) - //move each escape pod (or applicable spaceship) to its corresponding transit dock - for(var/A in SSshuttle.mobile_docking_ports) - var/obj/docking_port/mobile/M = A - M.on_emergency_launch() - - //now move the actual emergency shuttle to its transit dock - var/list/areas = list() - for(var/area/shuttle/escape/E in GLOB.areas) - areas += E - hyperspace_sound(HYPERSPACE_LAUNCH, areas) - enterTransit() - - //Tell the events we're starting, so they can time their spawns or do some other stuff - for(var/datum/shuttle_event/event as anything in event_list) - event.start_up_event(SSshuttle.emergency_escape_time * engine_coeff) - - mode = SHUTTLE_ESCAPE - launch_status = ENDGAME_LAUNCHED - setTimer(SSshuttle.emergency_escape_time * engine_coeff) - priority_announce( - text = "The emergency shuttle has left the station. Estimate [timeLeft(60 SECONDS)] minutes until the shuttle docks at [command_name()].", - title = "Emergency Shuttle Departure", - sender_override = "Emergency Shuttle Uplink Alert", - color_override = "orange", - ) - INVOKE_ASYNC(SSticker, TYPE_PROC_REF(/datum/controller/subsystem/ticker, poll_hearts)) - INVOKE_ASYNC(SSvote, TYPE_PROC_REF(/datum/controller/subsystem/vote, initiate_vote), /datum/vote/map_vote, vote_initiator_name = "Map Rotation", forced = TRUE) - - if(!is_reserved_level(z)) - CRASH("Emergency shuttle did not move to transit z-level!") - - if(SHUTTLE_STRANDED, SHUTTLE_DISABLED) - SSshuttle.checkHostileEnvironment() - - - if(SHUTTLE_ESCAPE) - if(sound_played && time_left <= HYPERSPACE_END_TIME) - var/list/areas = list() - for(var/area/shuttle/escape/E in GLOB.areas) - areas += E - hyperspace_sound(HYPERSPACE_END, areas) - if(time_left <= PARALLAX_LOOP_TIME) - var/area_parallax = FALSE - for(var/place in shuttle_areas) - var/area/shuttle/shuttle_area = place - if(shuttle_area.parallax_movedir) - area_parallax = TRUE - break - if(area_parallax) - parallax_slowdown() - for(var/A in SSshuttle.mobile_docking_ports) - var/obj/docking_port/mobile/M = A - if(M.launch_status == ENDGAME_LAUNCHED) - if(istype(M, /obj/docking_port/mobile/pod)) - M.parallax_slowdown() - - process_events() - - if(time_left <= 0) - //move each escape pod to its corresponding escape dock - for(var/obj/docking_port/mobile/port as anything in SSshuttle.mobile_docking_ports) - port.on_emergency_dock() - - // now move the actual emergency shuttle to centcom - // unless the shuttle is "hijacked" - var/destination_dock = "emergency_away" - if(is_hijacked() || elimination_hijack()) - // just double check - SSmapping.lazy_load_template(LAZY_TEMPLATE_KEY_NUKIEBASE) - destination_dock = "emergency_syndicate" - minor_announce("Corruption detected in \ - shuttle navigation protocols. Please contact your \ - supervisor.", "SYSTEM ERROR:", sound_override = 'sound/announcer/announcement/announce_syndi.ogg') - - dock_id(destination_dock) - mode = SHUTTLE_ENDGAME - timer = 0 - -/obj/docking_port/mobile/emergency/transit_failure() - ..() - message_admins("Moving emergency shuttle directly to centcom dock to prevent deadlock.") - - mode = SHUTTLE_ESCAPE - launch_status = ENDGAME_LAUNCHED - setTimer(SSshuttle.emergency_escape_time) - priority_announce( - text = "The emergency shuttle is preparing for direct jump. Estimate [timeLeft(60 SECONDS)] minutes until the shuttle docks at [command_name()].", - title = "Emergency Shuttle Transit Failure", - sender_override = "Emergency Shuttle Uplink Alert", - color_override = "orange", - ) - -///Generate a list of events to run during the departure -/obj/docking_port/mobile/emergency/proc/setup_shuttle_events() - var/list/names = list() - for(var/datum/shuttle_event/event as anything in subtypesof(/datum/shuttle_event)) - if(prob(initial(event.event_probability))) - add_shuttle_event(event) - names += initial(event.name) - if(LAZYLEN(names)) - log_game("[capitalize(name)] has selected the following shuttle events: [english_list(names)].") - -/obj/docking_port/mobile/monastery - name = "monastery pod" - shuttle_id = "mining_common" //set so mining can call it down - launch_status = UNLAUNCHED //required for it to launch as a pod. - -/obj/docking_port/mobile/monastery/on_emergency_dock() - if(launch_status == ENDGAME_LAUNCHED) - initiate_docking(SSshuttle.getDock("pod_away")) //docks our shuttle as any pod would - mode = SHUTTLE_ENDGAME - -/obj/docking_port/mobile/pod - name = "escape pod" - shuttle_id = "pod" - launch_status = UNLAUNCHED - -/obj/docking_port/mobile/pod/request(obj/docking_port/stationary/S) - var/obj/machinery/computer/shuttle/connected_computer = get_control_console() - if(!istype(connected_computer, /obj/machinery/computer/shuttle/pod)) - return FALSE - if(!(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED) && !(connected_computer.obj_flags & EMAGGED)) - to_chat(usr, span_warning("Escape pods will only launch during \"Code Red\" security alert.")) - return FALSE - if(launch_status == UNLAUNCHED) - launch_status = EARLY_LAUNCHED - return ..() - -/obj/docking_port/mobile/pod/cancel() - return - -/obj/machinery/computer/shuttle/pod - name = "pod control computer" - locked = TRUE - possible_destinations = "pod_asteroid" - icon = 'icons/obj/machines/wallmounts.dmi' - icon_state = "pod_off" - circuit = /obj/item/circuitboard/computer/emergency_pod - light_color = LIGHT_COLOR_BLUE - density = FALSE - icon_keyboard = null - icon_screen = "pod_on" - -/obj/machinery/computer/shuttle/pod/Initialize(mapload) - . = ..() - RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(check_lock)) - -/obj/machinery/computer/shuttle/pod/emag_act(mob/user, obj/item/card/emag/emag_card) - if(obj_flags & EMAGGED) - return FALSE - obj_flags |= EMAGGED - locked = FALSE - balloon_alert(user, "alert level checking disabled") - icon_screen = "emagged_general" - update_appearance() - return TRUE - -/obj/machinery/computer/shuttle/pod/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock) - . = ..() - if(port) - //Checks if the computer has already added the shuttle destination with the initial id - //This has to be done because connect_to_shuttle is called again after its ID is updated - //due to conflicting id names - var/base_shuttle_destination = ";[initial(port.shuttle_id)]_lavaland" - var/shuttle_destination = ";[port.shuttle_id]_lavaland" - - var/position = findtext(possible_destinations, base_shuttle_destination) - if(position) - if(base_shuttle_destination == shuttle_destination) - return - possible_destinations = splicetext(possible_destinations, position, position + length(base_shuttle_destination), shuttle_destination) - return - - possible_destinations += shuttle_destination - -/** - * Signal handler for checking if we should lock or unlock escape pods accordingly to a newly set security level - * - * Arguments: - * * source The datum source of the signal - * * new_level The new security level that is in effect - */ -/obj/machinery/computer/shuttle/pod/proc/check_lock(datum/source, new_level) - SIGNAL_HANDLER - - if(obj_flags & EMAGGED) - return - locked = (new_level < SEC_LEVEL_RED) - -/obj/docking_port/stationary/random - name = "escape pod" - shuttle_id = "pod" - hidden = TRUE - override_can_dock_checks = TRUE - /// The area the pod tries to land at - var/target_area = /area/lavaland/surface/outdoors - /// Minimal distance from the map edge, setting this too low can result in shuttle landing on the edge and getting "sliced" - var/edge_distance = 16 - -/obj/docking_port/stationary/random/Initialize(mapload) - . = ..() - if(!mapload) - return - - var/list/turfs = get_area_turfs(target_area) - var/original_len = turfs.len - while(turfs.len) - var/turf/picked_turf = pick(turfs) - if(picked_turf.x stationary_dock.dwidth) + return SHUTTLE_DWIDTH_TOO_LARGE + + if(width-dwidth > stationary_dock.width-stationary_dock.dwidth) + return SHUTTLE_WIDTH_TOO_LARGE + + if(dheight > stationary_dock.dheight) + return SHUTTLE_DHEIGHT_TOO_LARGE + + if(height-dheight > stationary_dock.height-stationary_dock.dheight) + return SHUTTLE_HEIGHT_TOO_LARGE + + //check the dock isn't occupied + var/currently_docked = stationary_dock.get_docked() + if(currently_docked) + // by someone other than us + if(currently_docked != src) + return SHUTTLE_SOMEONE_ELSE_DOCKED + else + // This isn't an error, per se, but we can't let the shuttle code + // attempt to move us where we currently are, it will get weird. + return SHUTTLE_ALREADY_DOCKED + + return SHUTTLE_CAN_DOCK + +/obj/docking_port/mobile/proc/check_dock(obj/docking_port/stationary/S, silent = FALSE) + var/status = canDock(S) + if(status == SHUTTLE_CAN_DOCK) + return TRUE + else + if(status != SHUTTLE_ALREADY_DOCKED && !silent) // SHUTTLE_ALREADY_DOCKED is no cause for error + message_admins("Shuttle [src] cannot dock at [S], error: [status]") + // We're already docked there, don't need to do anything. + // Triggering shuttle movement code in place is weird + return FALSE + +/obj/docking_port/mobile/proc/transit_failure() + message_admins("Shuttle [src] repeatedly failed to create transit zone.") + +/** + * Calls the shuttle to the destination port, respecting its ignition and call timers + * + * Arguments: + * * destination_port - Stationary docking port to move the shuttle to + */ +/obj/docking_port/mobile/proc/request(obj/docking_port/stationary/destination_port) + if(!check_dock(destination_port)) + testing("check_dock failed on request for [src]") + return + + if(mode == SHUTTLE_IGNITING && destination == destination_port) + return + + switch(mode) + if(SHUTTLE_CALL) + if(destination_port == destination) + if(timeLeft(1) < callTime * engine_coeff) + setTimer(callTime * engine_coeff) + else + destination = destination_port + setTimer(callTime * engine_coeff) + if(SHUTTLE_RECALL) + if(destination_port == destination) + setTimer(callTime * engine_coeff - timeLeft(1)) + else + destination = destination_port + setTimer(callTime * engine_coeff) + mode = SHUTTLE_CALL + if(SHUTTLE_IDLE, SHUTTLE_IGNITING) + destination = destination_port + mode = SHUTTLE_IGNITING + setTimer(ignitionTime) + +//recall the shuttle to where it was previously +/obj/docking_port/mobile/proc/cancel() + if(mode != SHUTTLE_CALL) + return + + remove_ripples() + + invertTimer() + mode = SHUTTLE_RECALL + +/obj/docking_port/mobile/proc/enterTransit() + if((SSshuttle.lockdown && is_station_level(z)) || !canMove()) //emp went off, no escape + mode = SHUTTLE_IDLE + return + previous = null + if(!destination) + // sent to transit with no destination -> unlimited timer + timer = INFINITY + var/obj/docking_port/stationary/S0 = get_docked() + var/obj/docking_port/stationary/S1 = assigned_transit + if(S1) + if(initiate_docking(S1) != DOCKING_SUCCESS) + WARNING("shuttle \"[shuttle_id]\" could not enter transit space. Docked at [S0 ? S0.shuttle_id : "null"]. Transit dock [S1 ? S1.shuttle_id : "null"].") + else if(S0) + if(S0.delete_after) + qdel(S0, TRUE) + else + previous = S0 + else + WARNING("shuttle \"[shuttle_id]\" could not enter transit space. S0=[S0 ? S0.shuttle_id : "null"] S1=[S1 ? S1.shuttle_id : "null"]") + + +/obj/docking_port/mobile/proc/jumpToNullSpace() + // Destroys the docking port and the shuttle contents. + // Not in a fancy way, it just ceases. + var/obj/docking_port/stationary/current_dock = get_docked() + + var/underlying_area_type = SHUTTLE_DEFAULT_UNDERLYING_AREA + // If the shuttle is docked to a stationary port, restore its normal + // "empty" area and turf + if(current_dock?.area_type) + underlying_area_type = current_dock.area_type + + var/list/old_turfs = return_ordered_turfs(x, y, z, dir) + + var/area/underlying_area = GLOB.areas_by_type[underlying_area_type] + if(!underlying_area) + underlying_area = new underlying_area_type(null) + + for(var/i in 1 to old_turfs.len) + var/turf/oldT = old_turfs[i] + if(!oldT || !istype(oldT.loc, area_type)) + continue + oldT.change_area(oldT.loc, underlying_area) + oldT.empty(FALSE) + + // Here we locate the bottommost shuttle boundary and remove all turfs above it + var/shuttle_tile_depth = oldT.depth_to_find_baseturf(/turf/baseturf_skipover/shuttle) + if (!isnull(shuttle_tile_depth)) + oldT.ScrapeAway(shuttle_tile_depth) + + qdel(src, force=TRUE) + +/** + * Ghosts and marks as escaped (for greentext purposes) all mobs, then deletes the shuttle. + * Used by the Shuttle Manipulator + */ +/obj/docking_port/mobile/proc/intoTheSunset() + // Loop over mobs + for(var/turf/turfs as anything in return_turfs()) + for(var/mob/living/sunset_mobs in turfs.get_all_contents()) + // If they have a mind and they're not in the brig, they escaped + if(sunset_mobs.mind && !istype(get_area(sunset_mobs), /area/shuttle/escape/brig)) + sunset_mobs.mind.force_escaped = TRUE + // Ghostize them and put them in nullspace stasis (for stat & possession checks) + ADD_TRAIT(sunset_mobs, TRAIT_NO_TRANSFORM, REF(src)) + sunset_mobs.ghostize(FALSE) + sunset_mobs.moveToNullspace() + + // Now that mobs are stowed, delete the shuttle + jumpToNullSpace() + +/obj/docking_port/mobile/proc/create_ripples(obj/docking_port/stationary/S1, animate_time) + var/list/turfs = ripple_area(S1) + for(var/t in turfs) + ripples += new /obj/effect/abstract/ripple(t, animate_time) + +/obj/docking_port/mobile/proc/remove_ripples() + QDEL_LIST(ripples) + +/obj/docking_port/mobile/proc/ripple_area(obj/docking_port/stationary/S1) + var/list/L0 = return_ordered_turfs(x, y, z, dir) + var/list/L1 = return_ordered_turfs(S1.x, S1.y, S1.z, S1.dir) + + var/list/ripple_turfs = list() + var/stop = min(L0.len, L1.len) + for(var/i in 1 to stop) + var/turf/T0 = L0[i] + var/turf/T1 = L1[i] + if(!istype(T0.loc, area_type) || istype(T0.loc, /area/shuttle/transit)) + continue // not part of the shuttle + ripple_turfs += T1 + + return ripple_turfs + +/obj/docking_port/mobile/proc/check_poddoors() + for(var/obj/machinery/door/poddoor/shuttledock/pod as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/door/poddoor/shuttledock)) + pod.check() + +/obj/docking_port/mobile/proc/dock_id(id) + var/port = SSshuttle.getDock(id) + if(port) + . = initiate_docking(port) + else + . = null + +//used by shuttle subsystem to check timers +/obj/docking_port/mobile/proc/check() + check_effects() + //process_events() if you were to add events to non-escape shuttles, uncomment this + + if(mode == SHUTTLE_IGNITING) + check_transit_zone() + + if(timeLeft(1) > 0) + return + // If we can't dock or we don't have a transit slot, wait for 20 ds, + // then try again + switch(mode) + if(SHUTTLE_CALL, SHUTTLE_PREARRIVAL) + if(prearrivalTime && mode != SHUTTLE_PREARRIVAL) + mode = SHUTTLE_PREARRIVAL + setTimer(prearrivalTime) + return + var/error = initiate_docking(destination, preferred_direction) + if(error && error & (DOCKING_NULL_DESTINATION | DOCKING_NULL_SOURCE)) + var/msg = "A mobile dock in transit exited initiate_docking() with an error. This is most likely a mapping problem: Error: [error], ([src]) ([previous][ADMIN_JMP(previous)] -> [destination][ADMIN_JMP(destination)])" + WARNING(msg) + message_admins(msg) + mode = SHUTTLE_IDLE + return + else if(error) + setTimer(20) + return + if(rechargeTime) + mode = SHUTTLE_RECHARGING + setTimer(rechargeTime) + return + if(SHUTTLE_RECALL) + if(initiate_docking(previous) != DOCKING_SUCCESS) + setTimer(20) + return + if(SHUTTLE_IGNITING) + if(check_transit_zone() != TRANSIT_READY) + setTimer(20) + return + else + mode = SHUTTLE_CALL + setTimer(callTime * engine_coeff) + enterTransit() + return + + mode = SHUTTLE_IDLE + timer = 0 + destination = null + +/obj/docking_port/mobile/proc/check_effects() + if(!ripples.len) + if((mode == SHUTTLE_CALL) || (mode == SHUTTLE_RECALL)) + var/tl = timeLeft(1) + if(tl <= SHUTTLE_RIPPLE_TIME) + create_ripples(destination, tl) + + var/obj/docking_port/stationary/S0 = get_docked() + if(istype(S0, /obj/docking_port/stationary/transit) && timeLeft(1) <= PARALLAX_LOOP_TIME) + for(var/place in shuttle_areas) + var/area/shuttle/shuttle_area = place + if(shuttle_area.parallax_movedir) + parallax_slowdown() + +/obj/docking_port/mobile/proc/parallax_slowdown() + for(var/place in shuttle_areas) + var/area/shuttle/shuttle_area = place + shuttle_area.parallax_movedir = FALSE + if(assigned_transit?.assigned_area) + assigned_transit.assigned_area.parallax_movedir = FALSE + var/list/L0 = return_ordered_turfs(x, y, z, dir) + for (var/thing in L0) + var/turf/T = thing + if(!T || !istype(T.loc, area_type)) + continue + for (var/atom/movable/movable as anything in T) + if (movable.client_mobs_in_contents) + movable.update_parallax_contents() + +/obj/docking_port/mobile/proc/check_transit_zone() + if(assigned_transit) + return TRANSIT_READY + else + SSshuttle.request_transit_dock(src) + +/obj/docking_port/mobile/proc/setTimer(wait) + timer = world.time + wait + last_timer_length = wait + +/obj/docking_port/mobile/proc/modTimer(multiple) + var/time_remaining = timer - world.time + if(time_remaining < 0 || !last_timer_length) + return + time_remaining *= multiple + last_timer_length *= multiple + setTimer(time_remaining) + +/obj/docking_port/mobile/proc/alert_coeff_change(new_coeff) + if(isnull(new_coeff)) + return + + var/time_multiplier = new_coeff / alert_coeff + var/time_remaining = timer - world.time + if(time_remaining < 0 || !last_timer_length) + return + + time_remaining *= time_multiplier + last_timer_length *= time_multiplier + alert_coeff = new_coeff + setTimer(time_remaining) + +/obj/docking_port/mobile/proc/invertTimer() + if(!last_timer_length) + return + var/time_remaining = timer - world.time + if(time_remaining > 0) + var/time_passed = last_timer_length - time_remaining + setTimer(time_passed) + +//returns timeLeft +/obj/docking_port/mobile/proc/timeLeft(divisor) + if(divisor <= 0) + divisor = 10 + + var/ds_remaining + if(!timer) + ds_remaining = callTime * engine_coeff + else + ds_remaining = max(0, timer - world.time) + + . = round(ds_remaining / divisor, 1) + +// returns 3-letter mode string, used by status screens and mob status panel +/obj/docking_port/mobile/proc/getModeStr() + switch(mode) + if(SHUTTLE_IGNITING) + return "IGN" + if(SHUTTLE_RECALL) + return "RCL" + if(SHUTTLE_CALL) + return "ETA" + if(SHUTTLE_DOCKED) + return "ETD" + if(SHUTTLE_ESCAPE) + return "ESC" + if(SHUTTLE_STRANDED) + return "ERR" + if(SHUTTLE_RECHARGING) + return "RCH" + if(SHUTTLE_PREARRIVAL) + return "LDN" + if(SHUTTLE_DISABLED) + return "DIS" + return "" + +// returns 5-letter timer string, used by status screens and mob status panel +/obj/docking_port/mobile/proc/getTimerStr() + if(mode == SHUTTLE_STRANDED || mode == SHUTTLE_DISABLED) + return "--:--" + + var/timeleft = timeLeft() + if(timeleft > 1 HOURS) + return "--:--" + else if(timeleft > 0) + return "[add_leading(num2text((timeleft / 60) % 60), 2, "0")]:[add_leading(num2text(timeleft % 60), 2, "0")]" + else + return "00:00" + +/** + * Gets shuttle location status in a form of string for tgui interfaces + */ +/obj/docking_port/mobile/proc/get_status_text_tgui() + var/obj/docking_port/stationary/dockedAt = get_docked() + var/docked_at = dockedAt?.name || "Unknown" + if(!istype(dockedAt, /obj/docking_port/stationary/transit)) + return docked_at + if(timeLeft() > 1 HOURS) + return "Hyperspace" + else + var/obj/docking_port/stationary/dst = (mode == SHUTTLE_RECALL) ? previous : destination + return "In transit to [dst?.name || "unknown location"]" + +/obj/docking_port/mobile/proc/getStatusText() + var/obj/docking_port/stationary/dockedAt = get_docked() + var/docked_at = dockedAt?.name || "unknown" + if(istype(dockedAt, /obj/docking_port/stationary/transit)) + if (timeLeft() > 1 HOURS) + return "hyperspace" + else + var/obj/docking_port/stationary/dst + if(mode == SHUTTLE_RECALL) + dst = previous + else + dst = destination + . = "transit towards [dst?.name || "unknown location"] ([getTimerStr()])" + else if(mode == SHUTTLE_RECHARGING) + return "[docked_at], recharging [getTimerStr()]" + else + return docked_at + +/obj/docking_port/mobile/proc/getDbgStatusText() + var/obj/docking_port/stationary/dockedAt = get_docked() + . = (dockedAt?.name) ? dockedAt.name : "unknown" + if(istype(dockedAt, /obj/docking_port/stationary/transit)) + var/obj/docking_port/stationary/dst + if(mode == SHUTTLE_RECALL) + dst = previous + else + dst = destination + if(dst) + . = "(transit to) [dst.name || dst.shuttle_id]" + else + . = "(transit to) nowhere" + else if(dockedAt) + . = dockedAt.name || dockedAt.shuttle_id + else + . = "unknown" + + +// attempts to locate /obj/machinery/computer/shuttle with matching ID inside the shuttle +/obj/docking_port/mobile/proc/get_control_console() + for(var/area/shuttle/shuttle_area as anything in shuttle_areas) + var/obj/machinery/computer/shuttle/shuttle_computer = locate(/obj/machinery/computer/shuttle) in shuttle_area + if(!shuttle_computer) + continue + if(shuttle_computer.shuttleId == shuttle_id) + return shuttle_computer + return null + +/obj/docking_port/mobile/proc/hyperspace_sound(phase, list/areas) + var/selected_sound + switch(phase) + if(HYPERSPACE_WARMUP) + selected_sound = "hyperspace_begin" + if(HYPERSPACE_LAUNCH) + selected_sound = "hyperspace_progress" + if(HYPERSPACE_END) + selected_sound = "hyperspace_end" + else + CRASH("Invalid hyperspace sound phase: [phase]") + // This previously was played from each door at max volume, and was one of the worst things I had ever seen. + // Now it's instead played from the nearest engine if close, or the first engine in the list if far since it doesn't really matter. + // Or a door if for some reason the shuttle has no engine, fuck oh hi daniel fuck it + var/range = (engine_coeff * max(width, height)) + var/long_range = range * 2.5 + var/atom/distant_source + + if(engine_list.len) + distant_source = engine_list[1] + else + for(var/our_area in areas) + distant_source = locate(/obj/machinery/door) in our_area + if(distant_source) + break + + if(!distant_source) + return + for(var/mob/zlevel_mobs as anything in SSmobs.clients_by_zlevel[z]) + var/dist_far = get_dist(zlevel_mobs, distant_source) + if(dist_far <= long_range && dist_far > range) + zlevel_mobs.playsound_local(distant_source, "sound/runtime/hyperspace/[selected_sound]_distance.ogg", 100) + else if(dist_far <= range) + var/source + if(!engine_list.len) + source = distant_source + else + var/closest_dist = 10000 + for(var/obj/machinery/power/shuttle_engine/engines as anything in engine_list) + var/dist_near = get_dist(zlevel_mobs, engines) + if(dist_near < closest_dist) + source = engines + closest_dist = dist_near + zlevel_mobs.playsound_local(source, "sound/runtime/hyperspace/[selected_sound].ogg", 100) + +// Losing all initial engines should get you 2 +// Adding another set of engines at 0.5 time +/obj/docking_port/mobile/proc/alter_engines(mod) + if(!mod) + return + var/old_coeff = engine_coeff + engine_coeff = get_engine_coeff(mod) + current_engine_power = max(0, current_engine_power + mod) + if(in_flight()) + var/delta_coeff = engine_coeff / old_coeff + modTimer(delta_coeff) + +// Double initial engines to get to 0.5 minimum +// Lose all initial engines to get to 2 +//For 0 engine shuttles like BYOS 5 engines to get to doublespeed +/obj/docking_port/mobile/proc/get_engine_coeff(engine_mod) + var/new_value = max(0, current_engine_power + engine_mod) + if(new_value == initial_engine_power) + return 1 + if(new_value > initial_engine_power) + var/delta = new_value - initial_engine_power + var/change_per_engine = (1 - ENGINE_COEFF_MIN) / ENGINE_DEFAULT_MAXSPEED_ENGINES // 5 by default + if(initial_engine_power > 0) + change_per_engine = (1 - ENGINE_COEFF_MIN) / initial_engine_power // or however many it had + return clamp(1 - delta * change_per_engine,ENGINE_COEFF_MIN, ENGINE_COEFF_MAX) + if(new_value < initial_engine_power) + var/delta = initial_engine_power - new_value + var/change_per_engine = 1 //doesn't really matter should not be happening for 0 engine shuttles + if(initial_engine_power > 0) + change_per_engine = (ENGINE_COEFF_MAX - 1) / initial_engine_power //just linear drop to max delay + return clamp(1 + delta * change_per_engine, ENGINE_COEFF_MIN, ENGINE_COEFF_MAX) + + +/obj/docking_port/mobile/proc/in_flight() + switch(mode) + if(SHUTTLE_CALL,SHUTTLE_RECALL,SHUTTLE_PREARRIVAL) + return TRUE + if(SHUTTLE_IDLE,SHUTTLE_IGNITING) + return FALSE + return FALSE // hmm + +/obj/docking_port/mobile/emergency/in_flight() + switch(mode) + if(SHUTTLE_ESCAPE) + return TRUE + if(SHUTTLE_STRANDED,SHUTTLE_ENDGAME) + return FALSE + return ..() + +//Called when emergency shuttle leaves the station +/obj/docking_port/mobile/proc/on_emergency_launch() + if(launch_status == UNLAUNCHED) //Pods will not launch from the mine/planet, and other ships won't launch unless we tell them to. + launch_status = ENDGAME_LAUNCHED + enterTransit() + +///Let people know shits about to go down +/obj/docking_port/mobile/proc/announce_shuttle_events() + for(var/datum/shuttle_event/event as anything in event_list) + notify_ghosts("The [name] has selected: [event.name]") + +/obj/docking_port/mobile/emergency/on_emergency_launch() + return + +//Called when emergency shuttle docks at centcom +/obj/docking_port/mobile/proc/on_emergency_dock() + // Mapping a new docking point for each ship mappers could potentially want docking with centcom would take up lots of space, + // just let them keep flying off "into the sunset" for their greentext. + if(launch_status == ENDGAME_LAUNCHED) + launch_status = ENDGAME_TRANSIT + +/obj/docking_port/mobile/pod/on_emergency_dock() + if(launch_status == ENDGAME_LAUNCHED) + initiate_docking(SSshuttle.getDock("[shuttle_id]_away")) //Escape pods dock at centcom + mode = SHUTTLE_ENDGAME + +/obj/docking_port/mobile/emergency/on_emergency_dock() + return + +///Process all the shuttle events for every shuttle tick we get +/obj/docking_port/mobile/proc/process_events() + var/list/removees + for(var/datum/shuttle_event/event as anything in event_list) + if(event.event_process() == SHUTTLE_EVENT_CLEAR) //if we return SHUTTLE_EVENT_CLEAR, we clean them up + LAZYADD(removees, event) + for(var/item in removees) + event_list.Remove(item) + +/// Give a typepath of a shuttle event to add to the shuttle. If added during endgame transit, will insta start the event +/obj/docking_port/mobile/proc/add_shuttle_event(typepath) + var/datum/shuttle_event/event = new typepath (src) + event_list.Add(event) + if(launch_status == ENDGAME_LAUNCHED) + event.start_up_event(0) + return event diff --git a/code/modules/shuttle/docking.dm b/code/modules/shuttle/mobile_port/shuttle_move.dm similarity index 98% rename from code/modules/shuttle/docking.dm rename to code/modules/shuttle/mobile_port/shuttle_move.dm index 32a1ca4950afa..b7e125826dce2 100644 --- a/code/modules/shuttle/docking.dm +++ b/code/modules/shuttle/mobile_port/shuttle_move.dm @@ -1,4 +1,5 @@ -/// This is the main proc. It instantly moves our mobile port to stationary port `new_dock`. +/// This is the main proc. Despite what the name suggests, +/// it instantly moves our mobile port to stationary port `new_dock`. /obj/docking_port/mobile/proc/initiate_docking(obj/docking_port/stationary/new_dock, movement_direction, force=FALSE) // Crashing this ship with NO SURVIVORS diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm similarity index 100% rename from code/modules/shuttle/on_move.dm rename to code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm diff --git a/code/modules/shuttle/shuttle_rotate.dm b/code/modules/shuttle/mobile_port/shuttle_rotate_callbacks.dm similarity index 100% rename from code/modules/shuttle/shuttle_rotate.dm rename to code/modules/shuttle/mobile_port/shuttle_rotate_callbacks.dm diff --git a/code/modules/shuttle/arrivals.dm b/code/modules/shuttle/mobile_port/variants/arrivals.dm similarity index 100% rename from code/modules/shuttle/arrivals.dm rename to code/modules/shuttle/mobile_port/variants/arrivals.dm diff --git a/code/modules/shuttle/assault_pod.dm b/code/modules/shuttle/mobile_port/variants/assault_pod.dm similarity index 100% rename from code/modules/shuttle/assault_pod.dm rename to code/modules/shuttle/mobile_port/variants/assault_pod.dm diff --git a/code/modules/shuttle/battlecruiser_starfury.dm b/code/modules/shuttle/mobile_port/variants/battlecruiser_starfury.dm similarity index 100% rename from code/modules/shuttle/battlecruiser_starfury.dm rename to code/modules/shuttle/mobile_port/variants/battlecruiser_starfury.dm diff --git a/code/modules/shuttle/elevator.dm b/code/modules/shuttle/mobile_port/variants/elevator.dm similarity index 100% rename from code/modules/shuttle/elevator.dm rename to code/modules/shuttle/mobile_port/variants/elevator.dm diff --git a/code/modules/shuttle/mobile_port/variants/emergency/emergency.dm b/code/modules/shuttle/mobile_port/variants/emergency/emergency.dm new file mode 100644 index 0000000000000..b162620cf4815 --- /dev/null +++ b/code/modules/shuttle/mobile_port/variants/emergency/emergency.dm @@ -0,0 +1,311 @@ +/obj/docking_port/mobile/emergency + name = "emergency shuttle" + shuttle_id = "emergency" + dir = EAST + port_direction = WEST + var/sound_played = 0 //If the launch sound has been sent to all players on the shuttle itself + var/hijack_status = HIJACK_NOT_BEGUN + +/obj/docking_port/mobile/emergency/Initialize(mapload) + . = ..() + + setup_shuttle_events() + +/obj/docking_port/mobile/emergency/canDock(obj/docking_port/stationary/S) + return SHUTTLE_CAN_DOCK //If the emergency shuttle can't move, the whole game breaks, so it will force itself to land even if it has to crush a few departments in the process + +/obj/docking_port/mobile/emergency/register() + . = ..() + SSshuttle.emergency = src + +/obj/docking_port/mobile/emergency/Destroy(force) + if(force) + // This'll make the shuttle subsystem use the backup shuttle. + if(src == SSshuttle.emergency) + // If we're the selected emergency shuttle + SSshuttle.emergencyDeregister() + + . = ..() + +/obj/docking_port/mobile/emergency/request(obj/docking_port/stationary/S, area/signal_origin, reason, red_alert, set_coefficient=null) + if(!isnum(set_coefficient)) + set_coefficient = SSsecurity_level.current_security_level.shuttle_call_time_mod + alert_coeff = set_coefficient + var/call_time = SSshuttle.emergency_call_time * alert_coeff * engine_coeff + switch(mode) + // The shuttle can not normally be called while "recalling", so + // if this proc is called, it's via admin fiat + if(SHUTTLE_RECALL, SHUTTLE_IDLE, SHUTTLE_CALL) + mode = SHUTTLE_CALL + setTimer(call_time) + else + return + + SSshuttle.emergencyCallAmount++ + + if(prob(70)) + SSshuttle.emergency_last_call_loc = signal_origin + else + SSshuttle.emergency_last_call_loc = null + + priority_announce( + text = "The emergency shuttle has been called. [red_alert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [(timeLeft(60 SECONDS))] minutes.[reason][SSshuttle.emergency_last_call_loc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ][SSshuttle.admin_emergency_no_recall ? "\n\nWarning: Shuttle recall subroutines disabled; Recall not possible." : ""]", + title = "Emergency Shuttle Dispatched", + sound = ANNOUNCER_SHUTTLECALLED, + sender_override = "Emergency Shuttle Uplink Alert", + color_override = "orange", + ) + +/obj/docking_port/mobile/emergency/cancel(area/signalOrigin) + if(mode != SHUTTLE_CALL) + return + if(SSshuttle.emergency_no_recall) + return + + invertTimer() + mode = SHUTTLE_RECALL + + if(prob(70)) + SSshuttle.emergency_last_call_loc = signalOrigin + else + SSshuttle.emergency_last_call_loc = null + priority_announce( + text = "The emergency shuttle has been recalled.[SSshuttle.emergency_last_call_loc ? " Recall signal traced. Results can be viewed on any communications console." : "" ]", + title = "Emergency Shuttle Recalled", + sound = ANNOUNCER_SHUTTLERECALLED, + sender_override = "Emergency Shuttle Uplink Alert", + color_override = "orange", + ) + + SSticker.emergency_reason = null + +/** + * Proc that handles checking if the emergency shuttle was successfully hijacked via being the only people present on the shuttle for the elimination hijack or highlander objective + * + * Checks for all mobs on the shuttle, checks their status, and checks if they're + * borgs or simple animals. Depending on the args, certain mobs may be ignored, + * and the presence of other antags may or may not invalidate a hijack. + * Args: + * filter_by_human, default TRUE, tells the proc that only humans should block a hijack. Borgs and animals are ignored and will not block if this is TRUE. + * solo_hijack, default FALSE, tells the proc to fail with multiple hijackers, such as for Highlander mode. + */ +/obj/docking_port/mobile/emergency/proc/elimination_hijack(filter_by_human = TRUE, solo_hijack = FALSE) + var/has_people = FALSE + var/hijacker_count = 0 + for(var/mob/living/player in GLOB.player_list) + if(player.mind) + if(player.stat != DEAD) + if(issilicon(player) && filter_by_human) //Borgs are technically dead anyways + continue + if(isanimal_or_basicmob(player) && filter_by_human) //animals don't count + continue + if(isbrain(player)) //also technically dead + continue + if(shuttle_areas[get_area(player)]) + has_people = TRUE + var/location = get_area(player.mind.current) + //Non-antag present. Can't hijack. + if(!(player.mind.has_antag_datum(/datum/antagonist)) && !istype(location, /area/shuttle/escape/brig)) + return FALSE + //Antag present, doesn't stop but let's see if we actually want to hijack + var/prevent = FALSE + for(var/datum/antagonist/A in player.mind.antag_datums) + if(A.can_elimination_hijack == ELIMINATION_ENABLED) + hijacker_count += 1 + prevent = FALSE + break //If we have both prevent and hijacker antags assume we want to hijack. + else if(A.can_elimination_hijack == ELIMINATION_PREVENT) + prevent = TRUE + if(prevent) + return FALSE + + //has people AND either there's only one hijacker or there's any but solo_hijack is disabled + return has_people && ((hijacker_count == 1) || (hijacker_count && !solo_hijack)) + +/obj/docking_port/mobile/emergency/proc/is_hijacked() + return hijack_status == HIJACK_COMPLETED + +/obj/docking_port/mobile/emergency/proc/ShuttleDBStuff() + set waitfor = FALSE + if(!SSdbcore.Connect()) + return + var/datum/db_query/query_round_shuttle_name = SSdbcore.NewQuery({" + UPDATE [format_table_name("round")] SET shuttle_name = :name WHERE id = :round_id + "}, list("name" = name, "round_id" = GLOB.round_id)) + query_round_shuttle_name.Execute() + qdel(query_round_shuttle_name) + +/obj/docking_port/mobile/emergency/check() + if(!timer) + return + var/time_left = timeLeft(1) + + // The emergency shuttle doesn't work like others so this + // ripple check is slightly different + if(!ripples.len && (time_left <= SHUTTLE_RIPPLE_TIME) && ((mode == SHUTTLE_CALL) || (mode == SHUTTLE_ESCAPE))) + var/destination + if(mode == SHUTTLE_CALL) + destination = SSshuttle.getDock("emergency_home") + else if(mode == SHUTTLE_ESCAPE) + destination = SSshuttle.getDock("emergency_away") + create_ripples(destination) + + switch(mode) + if(SHUTTLE_RECALL) + if(time_left <= 0) + mode = SHUTTLE_IDLE + timer = 0 + if(SHUTTLE_CALL) + if(time_left <= 0) + //move emergency shuttle to station + if(initiate_docking(SSshuttle.getDock("emergency_home")) != DOCKING_SUCCESS) + setTimer(20) + return + mode = SHUTTLE_DOCKED + setTimer(SSshuttle.emergency_dock_time) + send2adminchat("Server", "The Emergency Shuttle has docked with the station.") + priority_announce( + text = "[SSshuttle.emergency] has docked with the station. You have [DisplayTimeText(SSshuttle.emergency_dock_time)] to board the emergency shuttle.", + title = "Emergency Shuttle Arrival", + sound = ANNOUNCER_SHUTTLEDOCK, + sender_override = "Emergency Shuttle Uplink Alert", + color_override = "orange", + ) + ShuttleDBStuff() + addtimer(CALLBACK(src, PROC_REF(announce_shuttle_events)), 20 SECONDS) + + + if(SHUTTLE_DOCKED) + if(time_left <= ENGINE_START_TIME) + mode = SHUTTLE_IGNITING + SSshuttle.checkHostileEnvironment() + if(mode == SHUTTLE_STRANDED) + return + for(var/A in SSshuttle.mobile_docking_ports) + var/obj/docking_port/mobile/M = A + if(M.launch_status == UNLAUNCHED) //Pods will not launch from the mine/planet, and other ships won't launch unless we tell them to. + M.check_transit_zone() + + if(SHUTTLE_IGNITING) + var/success = TRUE + SSshuttle.checkHostileEnvironment() + if(mode == SHUTTLE_STRANDED) + return + + success &= (check_transit_zone() == TRANSIT_READY) + for(var/A in SSshuttle.mobile_docking_ports) + var/obj/docking_port/mobile/M = A + if(M.launch_status == UNLAUNCHED) + success &= (M.check_transit_zone() == TRANSIT_READY) + if(!success) + setTimer(ENGINE_START_TIME) + + if(time_left <= 50 && !sound_played) //4 seconds left:REV UP THOSE ENGINES BOYS. - should sync up with the launch + sound_played = 1 //Only rev them up once. + var/list/areas = list() + for(var/area/shuttle/escape/E in GLOB.areas) + areas += E + hyperspace_sound(HYPERSPACE_WARMUP, areas) + + if(time_left <= 0 && !SSshuttle.emergency_no_escape) + //move each escape pod (or applicable spaceship) to its corresponding transit dock + for(var/A in SSshuttle.mobile_docking_ports) + var/obj/docking_port/mobile/M = A + M.on_emergency_launch() + + //now move the actual emergency shuttle to its transit dock + var/list/areas = list() + for(var/area/shuttle/escape/E in GLOB.areas) + areas += E + hyperspace_sound(HYPERSPACE_LAUNCH, areas) + enterTransit() + + //Tell the events we're starting, so they can time their spawns or do some other stuff + for(var/datum/shuttle_event/event as anything in event_list) + event.start_up_event(SSshuttle.emergency_escape_time * engine_coeff) + + mode = SHUTTLE_ESCAPE + launch_status = ENDGAME_LAUNCHED + setTimer(SSshuttle.emergency_escape_time * engine_coeff) + priority_announce( + text = "The emergency shuttle has left the station. Estimate [timeLeft(60 SECONDS)] minutes until the shuttle docks at [command_name()].", + title = "Emergency Shuttle Departure", + sender_override = "Emergency Shuttle Uplink Alert", + color_override = "orange", + ) + INVOKE_ASYNC(SSticker, TYPE_PROC_REF(/datum/controller/subsystem/ticker, poll_hearts)) + INVOKE_ASYNC(SSvote, TYPE_PROC_REF(/datum/controller/subsystem/vote, initiate_vote), /datum/vote/map_vote, vote_initiator_name = "Map Rotation", forced = TRUE) + + if(!is_reserved_level(z)) + CRASH("Emergency shuttle did not move to transit z-level!") + + if(SHUTTLE_STRANDED, SHUTTLE_DISABLED) + SSshuttle.checkHostileEnvironment() + + + if(SHUTTLE_ESCAPE) + if(sound_played && time_left <= HYPERSPACE_END_TIME) + var/list/areas = list() + for(var/area/shuttle/escape/E in GLOB.areas) + areas += E + hyperspace_sound(HYPERSPACE_END, areas) + if(time_left <= PARALLAX_LOOP_TIME) + var/area_parallax = FALSE + for(var/place in shuttle_areas) + var/area/shuttle/shuttle_area = place + if(shuttle_area.parallax_movedir) + area_parallax = TRUE + break + if(area_parallax) + parallax_slowdown() + for(var/A in SSshuttle.mobile_docking_ports) + var/obj/docking_port/mobile/M = A + if(M.launch_status == ENDGAME_LAUNCHED) + if(istype(M, /obj/docking_port/mobile/pod)) + M.parallax_slowdown() + + process_events() + + if(time_left <= 0) + //move each escape pod to its corresponding escape dock + for(var/obj/docking_port/mobile/port as anything in SSshuttle.mobile_docking_ports) + port.on_emergency_dock() + + // now move the actual emergency shuttle to centcom + // unless the shuttle is "hijacked" + var/destination_dock = "emergency_away" + if(is_hijacked() || elimination_hijack()) + // just double check + SSmapping.lazy_load_template(LAZY_TEMPLATE_KEY_NUKIEBASE) + destination_dock = "emergency_syndicate" + minor_announce("Corruption detected in \ + shuttle navigation protocols. Please contact your \ + supervisor.", "SYSTEM ERROR:", sound_override = 'sound/announcer/announcement/announce_syndi.ogg') + + dock_id(destination_dock) + mode = SHUTTLE_ENDGAME + timer = 0 + +/obj/docking_port/mobile/emergency/transit_failure() + ..() + message_admins("Moving emergency shuttle directly to centcom dock to prevent deadlock.") + + mode = SHUTTLE_ESCAPE + launch_status = ENDGAME_LAUNCHED + setTimer(SSshuttle.emergency_escape_time) + priority_announce( + text = "The emergency shuttle is preparing for direct jump. Estimate [timeLeft(60 SECONDS)] minutes until the shuttle docks at [command_name()].", + title = "Emergency Shuttle Transit Failure", + sender_override = "Emergency Shuttle Uplink Alert", + color_override = "orange", + ) + +///Generate a list of events to run during the departure +/obj/docking_port/mobile/emergency/proc/setup_shuttle_events() + var/list/names = list() + for(var/datum/shuttle_event/event as anything in subtypesof(/datum/shuttle_event)) + if(prob(initial(event.event_probability))) + add_shuttle_event(event) + names += initial(event.name) + if(LAZYLEN(names)) + log_game("[capitalize(name)] has selected the following shuttle events: [english_list(names)].") diff --git a/code/modules/shuttle/mobile_port/variants/emergency/emergency_console.dm b/code/modules/shuttle/mobile_port/variants/emergency/emergency_console.dm new file mode 100644 index 0000000000000..b46bfff274307 --- /dev/null +++ b/code/modules/shuttle/mobile_port/variants/emergency/emergency_console.dm @@ -0,0 +1,316 @@ +#define ENGINES_STARTED (SSshuttle.emergency.mode == SHUTTLE_IGNITING) +#define IS_DOCKED (SSshuttle.emergency.mode == SHUTTLE_DOCKED || (ENGINES_STARTED)) +#define SHUTTLE_CONSOLE_ACTION_DELAY (5 SECONDS) +#define TIME_LEFT (SSshuttle.emergency.timeLeft()) + +/obj/machinery/computer/emergency_shuttle + name = "emergency shuttle console" + desc = "For shuttle control." + icon_screen = "shuttle" + icon_keyboard = "tech_key" + resistance_flags = INDESTRUCTIBLE + var/auth_need = 3 + var/list/authorized = list() + var/list/acted_recently = list() + var/hijack_last_stage_increase = 0 SECONDS + var/hijack_stage_time = 5 SECONDS + var/hijack_stage_cooldown = 5 SECONDS + var/hijack_flight_time_increase = 30 SECONDS + var/hijack_completion_flight_time_set = 10 SECONDS //How long in deciseconds to set shuttle's timer after hijack is done. + var/hijack_hacking = FALSE + var/hijack_announce = TRUE + +/obj/machinery/computer/emergency_shuttle/Destroy() + // Our fake IDs that the emag generated are just there for colour + // They're not supposed to be accessible + + for(var/obj/item/card/id/ID in src) + qdel(ID) + if(authorized?.len) + authorized.Cut() + authorized = null + + . = ..() + +/obj/machinery/computer/emergency_shuttle/examine(mob/user) + . = ..() + if(hijack_announce) + . += span_danger("Security systems present on console. Any unauthorized tampering will result in an emergency announcement.") + if(user?.mind?.get_hijack_speed()) + . += span_danger("Alt click on this to attempt to hijack the shuttle. This will take multiple tries (current: stage [SSshuttle.emergency.hijack_status]/[HIJACK_COMPLETED]).") + . += span_notice("It will take you [(hijack_stage_time * user.mind.get_hijack_speed()) / 10] seconds to reprogram a stage of the shuttle's navigational firmware, and the console will undergo automated timed lockout for [hijack_stage_cooldown/10] seconds after each stage.") + if(hijack_announce) + . += span_warning("It is probably best to fortify your position as to be uninterrupted during the attempt, given the automatic announcements..") + +/obj/machinery/computer/emergency_shuttle/attackby(obj/item/I, mob/user,params) + if(isidcard(I)) + say("Please equip your ID card into your ID slot to authenticate.") + . = ..() + +/obj/machinery/computer/emergency_shuttle/ui_state(mob/user) + return GLOB.human_adjacent_state + +/obj/machinery/computer/emergency_shuttle/ui_interact(mob/user, datum/tgui/ui) + . = ..() + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "EmergencyShuttleConsole", name) + ui.open() + +/obj/machinery/computer/emergency_shuttle/ui_data(user) + var/list/data = list() + + data["timer_str"] = SSshuttle.emergency.getTimerStr() + data["engines_started"] = ENGINES_STARTED + data["authorizations_remaining"] = max((auth_need - authorized.len), 0) + var/list/A = list() + for(var/i in authorized) + var/obj/item/card/id/ID = i + var/name = ID.registered_name + var/job = ID.assignment + + if(obj_flags & EMAGGED) + name = Gibberish(name) + job = Gibberish(job) + A += list(list("name" = name, "job" = job)) + data["authorizations"] = A + + data["enabled"] = (IS_DOCKED && !ENGINES_STARTED) && !(user in acted_recently) + data["emagged"] = obj_flags & EMAGGED ? 1 : 0 + return data + +/obj/machinery/computer/emergency_shuttle/ui_act(action, params, datum/tgui/ui) + . = ..() + if(.) + return + if(ENGINES_STARTED) // past the point of no return + return + if(!IS_DOCKED) // shuttle computer only has uses when onstation + return + if(SSshuttle.emergency.mode == SHUTTLE_DISABLED) // admins have disabled the shuttle. + return + if(!isliving(usr)) + return + + var/area/my_area = get_area(src) + if(!istype(my_area, /area/shuttle/escape)) + say("Error - Network connectivity: Console has lost connection to the shuttle.") + return + + var/mob/living/user = usr + . = FALSE + + var/obj/item/card/id/ID = user.get_idcard(TRUE) + + if(!ID) + to_chat(user, span_warning("You don't have an ID.")) + return + + if(!(ACCESS_COMMAND in ID.access)) + to_chat(user, span_warning("The access level of your card is not high enough.")) + return + + if (user in acted_recently) + return + + var/old_len = authorized.len + addtimer(CALLBACK(src, PROC_REF(clear_recent_action), user), SHUTTLE_CONSOLE_ACTION_DELAY) + + switch(action) + if("authorize") + . = authorize(user) + + if("repeal") + authorized -= ID + + if("abort") + if(authorized.len) + // Abort. The action for when heads are fighting over whether + // to launch early. + authorized.Cut() + . = TRUE + + if((old_len != authorized.len) && !ENGINES_STARTED) + var/alert = (authorized.len > old_len) + var/repeal = (authorized.len < old_len) + var/remaining = max(0, auth_need - authorized.len) + if(authorized.len && remaining) + minor_announce("[remaining] authorizations needed until shuttle is launched early", null, alert) + if(repeal) + minor_announce("Early launch authorization revoked, [remaining] authorizations needed") + + acted_recently += user + SStgui.update_user_uis(user, src) + +/obj/machinery/computer/emergency_shuttle/proc/authorize(mob/living/user, source) + var/obj/item/card/id/ID = user.get_idcard(TRUE) + + if(ID in authorized) + return FALSE + for(var/i in authorized) + var/obj/item/card/id/other = i + if(other.registered_name == ID.registered_name) + return FALSE // No using IDs with the same name + + authorized += ID + + message_admins("[ADMIN_LOOKUPFLW(user)] has authorized early shuttle launch") + log_shuttle("[key_name(user)] has authorized early shuttle launch in [COORD(src)]") + // Now check if we're on our way + . = TRUE + process(SSMACHINES_DT) + +/obj/machinery/computer/emergency_shuttle/proc/clear_recent_action(mob/user) + acted_recently -= user + if (!QDELETED(user)) + SStgui.update_user_uis(user, src) + +/obj/machinery/computer/emergency_shuttle/process() + // Launch check is in process in case auth_need changes for some reason + // probably external. + . = FALSE + if(!SSshuttle.emergency) + return + + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + authorized.Cut() + obj_flags &= ~(EMAGGED) + + if(ENGINES_STARTED || (!IS_DOCKED)) + return . + + // Check to see if we've reached criteria for early launch + if((authorized.len >= auth_need) || (obj_flags & EMAGGED)) + // shuttle timers use 1/10th seconds internally + SSshuttle.emergency.setTimer(ENGINE_START_TIME) + var/system_error = obj_flags & EMAGGED ? "SYSTEM ERROR:" : null + minor_announce("The emergency shuttle will launch in \ + [TIME_LEFT] seconds", system_error, alert=TRUE) + . = TRUE + +/obj/machinery/computer/emergency_shuttle/proc/increase_hijack_stage() + var/obj/docking_port/mobile/emergency/shuttle = SSshuttle.emergency + // Begin loading this early, prevents a delay when the shuttle goes to land + INVOKE_ASYNC(SSmapping, TYPE_PROC_REF(/datum/controller/subsystem/mapping, lazy_load_template), LAZY_TEMPLATE_KEY_NUKIEBASE) + + shuttle.hijack_status++ + if(hijack_announce) + announce_hijack_stage() + hijack_last_stage_increase = world.time + say("Navigational protocol error! Rebooting systems.") + if(shuttle.mode == SHUTTLE_ESCAPE) + if(shuttle.hijack_status == HIJACK_COMPLETED) + shuttle.setTimer(hijack_completion_flight_time_set) + else + shuttle.setTimer(shuttle.timeLeft(1) + hijack_flight_time_increase) //give the guy more time to hijack if it's already in flight. + return shuttle.hijack_status + +/obj/machinery/computer/emergency_shuttle/click_alt(mob/living/user) + if(!isliving(user)) + return NONE + attempt_hijack_stage(user) + return CLICK_ACTION_SUCCESS + +/obj/machinery/computer/emergency_shuttle/proc/attempt_hijack_stage(mob/living/user) + if(!user.CanReach(src)) + return + if(HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) + to_chat(user, span_warning("You need your hands free before you can manipulate [src].")) + return + var/area/my_area = get_area(src) + if(!istype(my_area, /area/shuttle/escape)) + say("Error - Network connectivity: Console has lost connection to the shuttle.") + return + if(!user?.mind?.get_hijack_speed()) + to_chat(user, span_warning("You manage to open a user-mode shell on [src], and hundreds of lines of debugging output fly through your vision. It is probably best to leave this alone.")) + return + if(!EMERGENCY_AT_LEAST_DOCKED) // prevent advancing hijack stages on BYOS shuttles until the shuttle has "docked" + to_chat(user, span_warning("The flight plans for the shuttle haven't been loaded yet, you can't hack this right now.")) + return + if(hijack_hacking == TRUE) + return + if(SSshuttle.emergency.hijack_status >= HIJACK_COMPLETED) + to_chat(user, span_warning("The emergency shuttle is already loaded with a corrupt navigational payload. What more do you want from it?")) + return + if(hijack_last_stage_increase >= world.time - hijack_stage_cooldown) + say("Error - Catastrophic software error detected. Input is currently on timeout.") + return + hijack_hacking = TRUE + to_chat(user, span_boldwarning("You [SSshuttle.emergency.hijack_status == HIJACK_NOT_BEGUN? "begin" : "continue"] to override [src]'s navigational protocols.")) + say("Software override initiated.") + var/turf/console_hijack_turf = get_turf(src) + message_admins("[src] is being overriden for hijack by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(console_hijack_turf)]") + user.log_message("is hijacking [src].", LOG_GAME) + . = FALSE + if(do_after(user, hijack_stage_time * (1 / user.mind.get_hijack_speed()), target = src)) + increase_hijack_stage() + console_hijack_turf = get_turf(src) + message_admins("[ADMIN_LOOKUPFLW(user)] has hijacked [src] in [ADMIN_VERBOSEJMP(console_hijack_turf)]. Hijack stage increased to stage [SSshuttle.emergency.hijack_status] out of [HIJACK_COMPLETED].") + user.log_message("has hijacked [src]. Hijack stage increased to stage [SSshuttle.emergency.hijack_status] out of [HIJACK_COMPLETED].", LOG_GAME) + . = TRUE + to_chat(user, span_notice("You reprogram some of [src]'s programming, putting it on timeout for [hijack_stage_cooldown/10] seconds.")) + visible_message( + span_warning("[user.name] appears to be tampering with [src]."), + blind_message = span_hear("You hear someone tapping computer keys."), + vision_distance = COMBAT_MESSAGE_RANGE, + ignored_mobs = user + ) + hijack_hacking = FALSE + +/obj/machinery/computer/emergency_shuttle/proc/announce_hijack_stage() + var/msg + switch(SSshuttle.emergency.hijack_status) + if(HIJACK_NOT_BEGUN) + return + if(HIJACK_STAGE_1) + msg = "AUTHENTICATING - FAIL. AUTHENTICATING - FAIL. AUTHENTICATING - FAI###### Welcome, technician JOHN DOE." + if(HIJACK_STAGE_2) + msg = "Warning: Navigational route fails \"IS_AUTHORIZED\". Please try againNN[scramble_message_replace_chars("againagainagainagainagain", 70)]." + if(HIJACK_STAGE_3) + msg = "CRC mismatch at ~h~ in calculated route buffer. Full reset initiated of FTL_NAVIGATION_SERVICES. Memory decrypted for automatic repair." + if(HIJACK_STAGE_4) + msg = "~ACS_directive module_load(cyberdyne.exploit.nanotrasen.shuttlenav)... NT key mismatch. Confirm load? Y...###Reboot complete. $SET transponder_state = 0; System link initiated with connected engines..." + if(HIJACK_COMPLETED) + msg = "SYSTEM OVERRIDE - Resetting course to \[[scramble_message_replace_chars("###########", 100)]\] \ + ([scramble_message_replace_chars("#######", 100)]/[scramble_message_replace_chars("#######", 100)]/[scramble_message_replace_chars("#######", 100)]) \ + {AUTH - ROOT (uid: 0)}.\ + [SSshuttle.emergency.mode == SHUTTLE_ESCAPE ? "Diverting from existing route - Bluespace exit in \ + [hijack_completion_flight_time_set >= INFINITY ? "[scramble_message_replace_chars("\[ERROR\]")]" : hijack_completion_flight_time_set/10] seconds." : ""]" + minor_announce(scramble_message_replace_chars(msg, replaceprob = 10), "Emergency Shuttle", TRUE) + +/obj/machinery/computer/emergency_shuttle/emag_act(mob/user, obj/item/card/emag/emag_card) + // How did you even get on the shuttle before it go to the station? + if(!IS_DOCKED) + return FALSE + + if((obj_flags & EMAGGED) || ENGINES_STARTED) //SYSTEM ERROR: THE SHUTTLE WILL LA-SYSTEM ERROR: THE SHUTTLE WILL LA-SYSTEM ERROR: THE SHUTTLE WILL LAUNCH IN 10 SECONDS + balloon_alert(user, "shuttle already about to launch!") + return FALSE + + var/time = TIME_LEFT + if (user) + message_admins("[ADMIN_LOOKUPFLW(user)] has emagged the emergency shuttle [time] seconds before launch.") + log_shuttle("[key_name(user)] has emagged the emergency shuttle in [COORD(src)] [time] seconds before launch.") + else + message_admins("The emergency shuttle was emagged [time] seconds before launch, with no emagger.") + log_shuttle("The emergency shuttle was emagged in [COORD(src)] [time] seconds before launch, with no emagger.") + + obj_flags |= EMAGGED + SSshuttle.emergency.movement_force = list("KNOCKDOWN" = 60, "THROW" = 20)//YOUR PUNY SEATBELTS can SAVE YOU NOW, MORTAL + for(var/i in 1 to 10) + // the shuttle system doesn't know who these people are, but they + // must be important, surely + var/obj/item/card/id/ID = new(src) + var/datum/job/J = pick(SSjob.joinable_occupations) + ID.registered_name = generate_random_name_species_based(species_type = /datum/species/human) + ID.assignment = J.title + + authorized += ID + + process(SSMACHINES_DT) + return TRUE + +#undef TIME_LEFT +#undef ENGINES_STARTED +#undef IS_DOCKED +#undef SHUTTLE_CONSOLE_ACTION_DELAY diff --git a/code/modules/shuttle/mobile_port/variants/emergency/emergency_types.dm b/code/modules/shuttle/mobile_port/variants/emergency/emergency_types.dm new file mode 100644 index 0000000000000..6030999698b00 --- /dev/null +++ b/code/modules/shuttle/mobile_port/variants/emergency/emergency_types.dm @@ -0,0 +1,39 @@ +/// Fallback shuttle +/obj/docking_port/mobile/emergency/backup + name = "backup shuttle" + shuttle_id = "backup" + dir = EAST + +/obj/docking_port/mobile/emergency/backup/Initialize(mapload) + // We want to be a valid emergency shuttle + // but not be the main one, keep whatever's set + // valid. + // backup shuttle ignores `timid` because THERE SHOULD BE NO TOUCHING IT + var/current_emergency = SSshuttle.emergency + . = ..() + SSshuttle.emergency = current_emergency + SSshuttle.backup_shuttle = src + +/obj/docking_port/mobile/emergency/backup/Destroy(force) + if(SSshuttle.backup_shuttle == src) + SSshuttle.backup_shuttle = null + return ..() + +/// Monastery shuttle +/obj/docking_port/mobile/monastery + name = "monastery pod" + shuttle_id = "mining_common" //set so mining can call it down + launch_status = UNLAUNCHED //required for it to launch as a pod. + +/obj/docking_port/mobile/monastery/on_emergency_dock() + if(launch_status == ENDGAME_LAUNCHED) + initiate_docking(SSshuttle.getDock("pod_away")) //docks our shuttle as any pod would + mode = SHUTTLE_ENDGAME + +/// Build Your Own Shuttle (BYOS) kit +/obj/docking_port/mobile/emergency/shuttle_build + +/obj/docking_port/mobile/emergency/shuttle_build/postregister() + . = ..() + initiate_docking(SSshuttle.getDock("emergency_home")) + diff --git a/code/modules/shuttle/mobile_port/variants/emergency/pods.dm b/code/modules/shuttle/mobile_port/variants/emergency/pods.dm new file mode 100644 index 0000000000000..1d8e1bae6bc03 --- /dev/null +++ b/code/modules/shuttle/mobile_port/variants/emergency/pods.dm @@ -0,0 +1,211 @@ +// THIS FILE CONTAINS: Pod mobile/stationary docking port, pod control console, pod storage and pod items + +/obj/docking_port/mobile/pod + name = "escape pod" + shuttle_id = "pod" + launch_status = UNLAUNCHED + +/obj/docking_port/mobile/pod/request(obj/docking_port/stationary/S) + var/obj/machinery/computer/shuttle/connected_computer = get_control_console() + if(!istype(connected_computer, /obj/machinery/computer/shuttle/pod)) + return FALSE + if(!(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED) && !(connected_computer.obj_flags & EMAGGED)) + to_chat(usr, span_warning("Escape pods will only launch during \"Code Red\" security alert.")) + return FALSE + if(launch_status == UNLAUNCHED) + launch_status = EARLY_LAUNCHED + return ..() + +/obj/docking_port/mobile/pod/cancel() + return + +/obj/machinery/computer/shuttle/pod + name = "pod control computer" + locked = TRUE + possible_destinations = "pod_asteroid" + icon = 'icons/obj/machines/wallmounts.dmi' + icon_state = "pod_off" + circuit = /obj/item/circuitboard/computer/emergency_pod + light_color = LIGHT_COLOR_BLUE + density = FALSE + icon_keyboard = null + icon_screen = "pod_on" + +/obj/machinery/computer/shuttle/pod/Initialize(mapload) + . = ..() + RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(check_lock)) + +/obj/machinery/computer/shuttle/pod/emag_act(mob/user, obj/item/card/emag/emag_card) + if(obj_flags & EMAGGED) + return FALSE + obj_flags |= EMAGGED + locked = FALSE + balloon_alert(user, "alert level checking disabled") + icon_screen = "emagged_general" + update_appearance() + return TRUE + +/obj/machinery/computer/shuttle/pod/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock) + . = ..() + if(port) + //Checks if the computer has already added the shuttle destination with the initial id + //This has to be done because connect_to_shuttle is called again after its ID is updated + //due to conflicting id names + var/base_shuttle_destination = ";[initial(port.shuttle_id)]_lavaland" + var/shuttle_destination = ";[port.shuttle_id]_lavaland" + + var/position = findtext(possible_destinations, base_shuttle_destination) + if(position) + if(base_shuttle_destination == shuttle_destination) + return + possible_destinations = splicetext(possible_destinations, position, position + length(base_shuttle_destination), shuttle_destination) + return + + possible_destinations += shuttle_destination + +/** + * Signal handler for checking if we should lock or unlock escape pods accordingly to a newly set security level + * + * Arguments: + * * source The datum source of the signal + * * new_level The new security level that is in effect + */ +/obj/machinery/computer/shuttle/pod/proc/check_lock(datum/source, new_level) + SIGNAL_HANDLER + + if(obj_flags & EMAGGED) + return + locked = (new_level < SEC_LEVEL_RED) + +/obj/docking_port/stationary/random + name = "escape pod" + shuttle_id = "pod" + hidden = TRUE + override_can_dock_checks = TRUE + /// The area the pod tries to land at + var/target_area = /area/lavaland/surface/outdoors + /// Minimal distance from the map edge, setting this too low can result in shuttle landing on the edge and getting "sliced" + var/edge_distance = 16 + +/obj/docking_port/stationary/random/Initialize(mapload) + . = ..() + if(!mapload) + return + + var/list/turfs = get_area_turfs(target_area) + var/original_len = turfs.len + while(turfs.len) + var/turf/picked_turf = pick(turfs) + if(picked_turf.x stationary_dock.dwidth) - return SHUTTLE_DWIDTH_TOO_LARGE - - if(width-dwidth > stationary_dock.width-stationary_dock.dwidth) - return SHUTTLE_WIDTH_TOO_LARGE - - if(dheight > stationary_dock.dheight) - return SHUTTLE_DHEIGHT_TOO_LARGE - - if(height-dheight > stationary_dock.height-stationary_dock.dheight) - return SHUTTLE_HEIGHT_TOO_LARGE - - //check the dock isn't occupied - var/currently_docked = stationary_dock.get_docked() - if(currently_docked) - // by someone other than us - if(currently_docked != src) - return SHUTTLE_SOMEONE_ELSE_DOCKED - else - // This isn't an error, per se, but we can't let the shuttle code - // attempt to move us where we currently are, it will get weird. - return SHUTTLE_ALREADY_DOCKED - - return SHUTTLE_CAN_DOCK - -/obj/docking_port/mobile/proc/check_dock(obj/docking_port/stationary/S, silent = FALSE) - var/status = canDock(S) - if(status == SHUTTLE_CAN_DOCK) - return TRUE - else - if(status != SHUTTLE_ALREADY_DOCKED && !silent) // SHUTTLE_ALREADY_DOCKED is no cause for error - message_admins("Shuttle [src] cannot dock at [S], error: [status]") - // We're already docked there, don't need to do anything. - // Triggering shuttle movement code in place is weird - return FALSE - -/obj/docking_port/mobile/proc/transit_failure() - message_admins("Shuttle [src] repeatedly failed to create transit zone.") - -/** - * Calls the shuttle to the destination port, respecting its ignition and call timers - * - * Arguments: - * * destination_port - Stationary docking port to move the shuttle to - */ -/obj/docking_port/mobile/proc/request(obj/docking_port/stationary/destination_port) - if(!check_dock(destination_port)) - testing("check_dock failed on request for [src]") - return - - if(mode == SHUTTLE_IGNITING && destination == destination_port) - return - - switch(mode) - if(SHUTTLE_CALL) - if(destination_port == destination) - if(timeLeft(1) < callTime * engine_coeff) - setTimer(callTime * engine_coeff) - else - destination = destination_port - setTimer(callTime * engine_coeff) - if(SHUTTLE_RECALL) - if(destination_port == destination) - setTimer(callTime * engine_coeff - timeLeft(1)) - else - destination = destination_port - setTimer(callTime * engine_coeff) - mode = SHUTTLE_CALL - if(SHUTTLE_IDLE, SHUTTLE_IGNITING) - destination = destination_port - mode = SHUTTLE_IGNITING - setTimer(ignitionTime) - -//recall the shuttle to where it was previously -/obj/docking_port/mobile/proc/cancel() - if(mode != SHUTTLE_CALL) - return - - remove_ripples() - - invertTimer() - mode = SHUTTLE_RECALL - -/obj/docking_port/mobile/proc/enterTransit() - if((SSshuttle.lockdown && is_station_level(z)) || !canMove()) //emp went off, no escape - mode = SHUTTLE_IDLE - return - previous = null - if(!destination) - // sent to transit with no destination -> unlimited timer - timer = INFINITY - var/obj/docking_port/stationary/S0 = get_docked() - var/obj/docking_port/stationary/S1 = assigned_transit - if(S1) - if(initiate_docking(S1) != DOCKING_SUCCESS) - WARNING("shuttle \"[shuttle_id]\" could not enter transit space. Docked at [S0 ? S0.shuttle_id : "null"]. Transit dock [S1 ? S1.shuttle_id : "null"].") - else if(S0) - if(S0.delete_after) - qdel(S0, TRUE) - else - previous = S0 - else - WARNING("shuttle \"[shuttle_id]\" could not enter transit space. S0=[S0 ? S0.shuttle_id : "null"] S1=[S1 ? S1.shuttle_id : "null"]") - - -/obj/docking_port/mobile/proc/jumpToNullSpace() - // Destroys the docking port and the shuttle contents. - // Not in a fancy way, it just ceases. - var/obj/docking_port/stationary/current_dock = get_docked() - - var/underlying_area_type = SHUTTLE_DEFAULT_UNDERLYING_AREA - // If the shuttle is docked to a stationary port, restore its normal - // "empty" area and turf - if(current_dock?.area_type) - underlying_area_type = current_dock.area_type - - var/list/old_turfs = return_ordered_turfs(x, y, z, dir) - - var/area/underlying_area = GLOB.areas_by_type[underlying_area_type] - if(!underlying_area) - underlying_area = new underlying_area_type(null) - - for(var/i in 1 to old_turfs.len) - var/turf/oldT = old_turfs[i] - if(!oldT || !istype(oldT.loc, area_type)) - continue - oldT.change_area(oldT.loc, underlying_area) - oldT.empty(FALSE) - - // Here we locate the bottommost shuttle boundary and remove all turfs above it - var/shuttle_tile_depth = oldT.depth_to_find_baseturf(/turf/baseturf_skipover/shuttle) - if (!isnull(shuttle_tile_depth)) - oldT.ScrapeAway(shuttle_tile_depth) - - qdel(src, force=TRUE) - -/** - * Ghosts and marks as escaped (for greentext purposes) all mobs, then deletes the shuttle. - * Used by the Shuttle Manipulator - */ -/obj/docking_port/mobile/proc/intoTheSunset() - // Loop over mobs - for(var/turf/turfs as anything in return_turfs()) - for(var/mob/living/sunset_mobs in turfs.get_all_contents()) - // If they have a mind and they're not in the brig, they escaped - if(sunset_mobs.mind && !istype(get_area(sunset_mobs), /area/shuttle/escape/brig)) - sunset_mobs.mind.force_escaped = TRUE - // Ghostize them and put them in nullspace stasis (for stat & possession checks) - ADD_TRAIT(sunset_mobs, TRAIT_NO_TRANSFORM, REF(src)) - sunset_mobs.ghostize(FALSE) - sunset_mobs.moveToNullspace() - - // Now that mobs are stowed, delete the shuttle - jumpToNullSpace() - -/obj/docking_port/mobile/proc/create_ripples(obj/docking_port/stationary/S1, animate_time) - var/list/turfs = ripple_area(S1) - for(var/t in turfs) - ripples += new /obj/effect/abstract/ripple(t, animate_time) - -/obj/docking_port/mobile/proc/remove_ripples() - QDEL_LIST(ripples) - -/obj/docking_port/mobile/proc/ripple_area(obj/docking_port/stationary/S1) - var/list/L0 = return_ordered_turfs(x, y, z, dir) - var/list/L1 = return_ordered_turfs(S1.x, S1.y, S1.z, S1.dir) - - var/list/ripple_turfs = list() - var/stop = min(L0.len, L1.len) - for(var/i in 1 to stop) - var/turf/T0 = L0[i] - var/turf/T1 = L1[i] - if(!istype(T0.loc, area_type) || istype(T0.loc, /area/shuttle/transit)) - continue // not part of the shuttle - ripple_turfs += T1 - - return ripple_turfs - -/obj/docking_port/mobile/proc/check_poddoors() - for(var/obj/machinery/door/poddoor/shuttledock/pod as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/door/poddoor/shuttledock)) - pod.check() - -/obj/docking_port/mobile/proc/dock_id(id) - var/port = SSshuttle.getDock(id) - if(port) - . = initiate_docking(port) - else - . = null - -//used by shuttle subsystem to check timers -/obj/docking_port/mobile/proc/check() - check_effects() - //process_events() if you were to add events to non-escape shuttles, uncomment this - - if(mode == SHUTTLE_IGNITING) - check_transit_zone() - - if(timeLeft(1) > 0) - return - // If we can't dock or we don't have a transit slot, wait for 20 ds, - // then try again - switch(mode) - if(SHUTTLE_CALL, SHUTTLE_PREARRIVAL) - if(prearrivalTime && mode != SHUTTLE_PREARRIVAL) - mode = SHUTTLE_PREARRIVAL - setTimer(prearrivalTime) - return - var/error = initiate_docking(destination, preferred_direction) - if(error && error & (DOCKING_NULL_DESTINATION | DOCKING_NULL_SOURCE)) - var/msg = "A mobile dock in transit exited initiate_docking() with an error. This is most likely a mapping problem: Error: [error], ([src]) ([previous][ADMIN_JMP(previous)] -> [destination][ADMIN_JMP(destination)])" - WARNING(msg) - message_admins(msg) - mode = SHUTTLE_IDLE - return - else if(error) - setTimer(20) - return - if(rechargeTime) - mode = SHUTTLE_RECHARGING - setTimer(rechargeTime) - return - if(SHUTTLE_RECALL) - if(initiate_docking(previous) != DOCKING_SUCCESS) - setTimer(20) - return - if(SHUTTLE_IGNITING) - if(check_transit_zone() != TRANSIT_READY) - setTimer(20) - return - else - mode = SHUTTLE_CALL - setTimer(callTime * engine_coeff) - enterTransit() - return - - mode = SHUTTLE_IDLE - timer = 0 - destination = null - -/obj/docking_port/mobile/proc/check_effects() - if(!ripples.len) - if((mode == SHUTTLE_CALL) || (mode == SHUTTLE_RECALL)) - var/tl = timeLeft(1) - if(tl <= SHUTTLE_RIPPLE_TIME) - create_ripples(destination, tl) - - var/obj/docking_port/stationary/S0 = get_docked() - if(istype(S0, /obj/docking_port/stationary/transit) && timeLeft(1) <= PARALLAX_LOOP_TIME) - for(var/place in shuttle_areas) - var/area/shuttle/shuttle_area = place - if(shuttle_area.parallax_movedir) - parallax_slowdown() - -/obj/docking_port/mobile/proc/parallax_slowdown() - for(var/place in shuttle_areas) - var/area/shuttle/shuttle_area = place - shuttle_area.parallax_movedir = FALSE - if(assigned_transit?.assigned_area) - assigned_transit.assigned_area.parallax_movedir = FALSE - var/list/L0 = return_ordered_turfs(x, y, z, dir) - for (var/thing in L0) - var/turf/T = thing - if(!T || !istype(T.loc, area_type)) - continue - for (var/atom/movable/movable as anything in T) - if (movable.client_mobs_in_contents) - movable.update_parallax_contents() - -/obj/docking_port/mobile/proc/check_transit_zone() - if(assigned_transit) - return TRANSIT_READY - else - SSshuttle.request_transit_dock(src) - -/obj/docking_port/mobile/proc/setTimer(wait) - timer = world.time + wait - last_timer_length = wait - -/obj/docking_port/mobile/proc/modTimer(multiple) - var/time_remaining = timer - world.time - if(time_remaining < 0 || !last_timer_length) - return - time_remaining *= multiple - last_timer_length *= multiple - setTimer(time_remaining) - -/obj/docking_port/mobile/proc/alert_coeff_change(new_coeff) - if(isnull(new_coeff)) - return - - var/time_multiplier = new_coeff / alert_coeff - var/time_remaining = timer - world.time - if(time_remaining < 0 || !last_timer_length) - return - - time_remaining *= time_multiplier - last_timer_length *= time_multiplier - alert_coeff = new_coeff - setTimer(time_remaining) - -/obj/docking_port/mobile/proc/invertTimer() - if(!last_timer_length) - return - var/time_remaining = timer - world.time - if(time_remaining > 0) - var/time_passed = last_timer_length - time_remaining - setTimer(time_passed) - -//returns timeLeft -/obj/docking_port/mobile/proc/timeLeft(divisor) - if(divisor <= 0) - divisor = 10 - - var/ds_remaining - if(!timer) - ds_remaining = callTime * engine_coeff - else - ds_remaining = max(0, timer - world.time) - - . = round(ds_remaining / divisor, 1) - -// returns 3-letter mode string, used by status screens and mob status panel -/obj/docking_port/mobile/proc/getModeStr() - switch(mode) - if(SHUTTLE_IGNITING) - return "IGN" - if(SHUTTLE_RECALL) - return "RCL" - if(SHUTTLE_CALL) - return "ETA" - if(SHUTTLE_DOCKED) - return "ETD" - if(SHUTTLE_ESCAPE) - return "ESC" - if(SHUTTLE_STRANDED) - return "ERR" - if(SHUTTLE_RECHARGING) - return "RCH" - if(SHUTTLE_PREARRIVAL) - return "LDN" - if(SHUTTLE_DISABLED) - return "DIS" - return "" - -// returns 5-letter timer string, used by status screens and mob status panel -/obj/docking_port/mobile/proc/getTimerStr() - if(mode == SHUTTLE_STRANDED || mode == SHUTTLE_DISABLED) - return "--:--" - - var/timeleft = timeLeft() - if(timeleft > 1 HOURS) - return "--:--" - else if(timeleft > 0) - return "[add_leading(num2text((timeleft / 60) % 60), 2, "0")]:[add_leading(num2text(timeleft % 60), 2, "0")]" - else - return "00:00" - -/** - * Gets shuttle location status in a form of string for tgui interfaces - */ -/obj/docking_port/mobile/proc/get_status_text_tgui() - var/obj/docking_port/stationary/dockedAt = get_docked() - var/docked_at = dockedAt?.name || "Unknown" - if(!istype(dockedAt, /obj/docking_port/stationary/transit)) - return docked_at - if(timeLeft() > 1 HOURS) - return "Hyperspace" - else - var/obj/docking_port/stationary/dst = (mode == SHUTTLE_RECALL) ? previous : destination - return "In transit to [dst?.name || "unknown location"]" - -/obj/docking_port/mobile/proc/getStatusText() - var/obj/docking_port/stationary/dockedAt = get_docked() - var/docked_at = dockedAt?.name || "unknown" - if(istype(dockedAt, /obj/docking_port/stationary/transit)) - if (timeLeft() > 1 HOURS) - return "hyperspace" - else - var/obj/docking_port/stationary/dst - if(mode == SHUTTLE_RECALL) - dst = previous - else - dst = destination - . = "transit towards [dst?.name || "unknown location"] ([getTimerStr()])" - else if(mode == SHUTTLE_RECHARGING) - return "[docked_at], recharging [getTimerStr()]" - else - return docked_at - -/obj/docking_port/mobile/proc/getDbgStatusText() - var/obj/docking_port/stationary/dockedAt = get_docked() - . = (dockedAt?.name) ? dockedAt.name : "unknown" - if(istype(dockedAt, /obj/docking_port/stationary/transit)) - var/obj/docking_port/stationary/dst - if(mode == SHUTTLE_RECALL) - dst = previous - else - dst = destination - if(dst) - . = "(transit to) [dst.name || dst.shuttle_id]" - else - . = "(transit to) nowhere" - else if(dockedAt) - . = dockedAt.name || dockedAt.shuttle_id - else - . = "unknown" - - -// attempts to locate /obj/machinery/computer/shuttle with matching ID inside the shuttle -/obj/docking_port/mobile/proc/get_control_console() - for(var/area/shuttle/shuttle_area as anything in shuttle_areas) - var/obj/machinery/computer/shuttle/shuttle_computer = locate(/obj/machinery/computer/shuttle) in shuttle_area - if(!shuttle_computer) - continue - if(shuttle_computer.shuttleId == shuttle_id) - return shuttle_computer - return null - -/obj/docking_port/mobile/proc/hyperspace_sound(phase, list/areas) - var/selected_sound - switch(phase) - if(HYPERSPACE_WARMUP) - selected_sound = "hyperspace_begin" - if(HYPERSPACE_LAUNCH) - selected_sound = "hyperspace_progress" - if(HYPERSPACE_END) - selected_sound = "hyperspace_end" - else - CRASH("Invalid hyperspace sound phase: [phase]") - // This previously was played from each door at max volume, and was one of the worst things I had ever seen. - // Now it's instead played from the nearest engine if close, or the first engine in the list if far since it doesn't really matter. - // Or a door if for some reason the shuttle has no engine, fuck oh hi daniel fuck it - var/range = (engine_coeff * max(width, height)) - var/long_range = range * 2.5 - var/atom/distant_source - - if(engine_list.len) - distant_source = engine_list[1] - else - for(var/our_area in areas) - distant_source = locate(/obj/machinery/door) in our_area - if(distant_source) - break - - if(!distant_source) - return - for(var/mob/zlevel_mobs as anything in SSmobs.clients_by_zlevel[z]) - var/dist_far = get_dist(zlevel_mobs, distant_source) - if(dist_far <= long_range && dist_far > range) - zlevel_mobs.playsound_local(distant_source, "sound/runtime/hyperspace/[selected_sound]_distance.ogg", 100) - else if(dist_far <= range) - var/source - if(!engine_list.len) - source = distant_source - else - var/closest_dist = 10000 - for(var/obj/machinery/power/shuttle_engine/engines as anything in engine_list) - var/dist_near = get_dist(zlevel_mobs, engines) - if(dist_near < closest_dist) - source = engines - closest_dist = dist_near - zlevel_mobs.playsound_local(source, "sound/runtime/hyperspace/[selected_sound].ogg", 100) - -// Losing all initial engines should get you 2 -// Adding another set of engines at 0.5 time -/obj/docking_port/mobile/proc/alter_engines(mod) - if(!mod) - return - var/old_coeff = engine_coeff - engine_coeff = get_engine_coeff(mod) - current_engine_power = max(0, current_engine_power + mod) - if(in_flight()) - var/delta_coeff = engine_coeff / old_coeff - modTimer(delta_coeff) - -// Double initial engines to get to 0.5 minimum -// Lose all initial engines to get to 2 -//For 0 engine shuttles like BYOS 5 engines to get to doublespeed -/obj/docking_port/mobile/proc/get_engine_coeff(engine_mod) - var/new_value = max(0, current_engine_power + engine_mod) - if(new_value == initial_engine_power) - return 1 - if(new_value > initial_engine_power) - var/delta = new_value - initial_engine_power - var/change_per_engine = (1 - ENGINE_COEFF_MIN) / ENGINE_DEFAULT_MAXSPEED_ENGINES // 5 by default - if(initial_engine_power > 0) - change_per_engine = (1 - ENGINE_COEFF_MIN) / initial_engine_power // or however many it had - return clamp(1 - delta * change_per_engine,ENGINE_COEFF_MIN, ENGINE_COEFF_MAX) - if(new_value < initial_engine_power) - var/delta = initial_engine_power - new_value - var/change_per_engine = 1 //doesn't really matter should not be happening for 0 engine shuttles - if(initial_engine_power > 0) - change_per_engine = (ENGINE_COEFF_MAX - 1) / initial_engine_power //just linear drop to max delay - return clamp(1 + delta * change_per_engine, ENGINE_COEFF_MIN, ENGINE_COEFF_MAX) - - -/obj/docking_port/mobile/proc/in_flight() - switch(mode) - if(SHUTTLE_CALL,SHUTTLE_RECALL,SHUTTLE_PREARRIVAL) - return TRUE - if(SHUTTLE_IDLE,SHUTTLE_IGNITING) - return FALSE - return FALSE // hmm - -/obj/docking_port/mobile/emergency/in_flight() - switch(mode) - if(SHUTTLE_ESCAPE) - return TRUE - if(SHUTTLE_STRANDED,SHUTTLE_ENDGAME) - return FALSE - return ..() - -//Called when emergency shuttle leaves the station -/obj/docking_port/mobile/proc/on_emergency_launch() - if(launch_status == UNLAUNCHED) //Pods will not launch from the mine/planet, and other ships won't launch unless we tell them to. - launch_status = ENDGAME_LAUNCHED - enterTransit() - -///Let people know shits about to go down -/obj/docking_port/mobile/proc/announce_shuttle_events() - for(var/datum/shuttle_event/event as anything in event_list) - notify_ghosts("The [name] has selected: [event.name]") - -/obj/docking_port/mobile/emergency/on_emergency_launch() - return - -//Called when emergency shuttle docks at centcom -/obj/docking_port/mobile/proc/on_emergency_dock() - // Mapping a new docking point for each ship mappers could potentially want docking with centcom would take up lots of space, - // just let them keep flying off "into the sunset" for their greentext. - if(launch_status == ENDGAME_LAUNCHED) - launch_status = ENDGAME_TRANSIT - -/obj/docking_port/mobile/pod/on_emergency_dock() - if(launch_status == ENDGAME_LAUNCHED) - initiate_docking(SSshuttle.getDock("[shuttle_id]_away")) //Escape pods dock at centcom - mode = SHUTTLE_ENDGAME - -/obj/docking_port/mobile/emergency/on_emergency_dock() - return - -///Process all the shuttle events for every shuttle tick we get -/obj/docking_port/mobile/proc/process_events() - var/list/removees - for(var/datum/shuttle_event/event as anything in event_list) - if(event.event_process() == SHUTTLE_EVENT_CLEAR) //if we return SHUTTLE_EVENT_CLEAR, we clean them up - LAZYADD(removees, event) - for(var/item in removees) - event_list.Remove(item) - -/// Give a typepath of a shuttle event to add to the shuttle. If added during endgame transit, will insta start the event -/obj/docking_port/mobile/proc/add_shuttle_event(typepath) - var/datum/shuttle_event/event = new typepath (src) - event_list.Add(event) - if(launch_status == ENDGAME_LAUNCHED) - event.start_up_event(0) - return event - -#ifdef TESTING -#undef DOCKING_PORT_HIGHLIGHT -#endif diff --git a/code/modules/shuttle/monastery.dm b/code/modules/shuttle/shuttle_consoles/monastery.dm similarity index 100% rename from code/modules/shuttle/monastery.dm rename to code/modules/shuttle/shuttle_consoles/monastery.dm diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/shuttle_consoles/navigation_computer.dm similarity index 100% rename from code/modules/shuttle/navigation_computer.dm rename to code/modules/shuttle/shuttle_consoles/navigation_computer.dm diff --git a/code/modules/shuttle/computer.dm b/code/modules/shuttle/shuttle_consoles/shuttle_console.dm similarity index 100% rename from code/modules/shuttle/computer.dm rename to code/modules/shuttle/shuttle_consoles/shuttle_console.dm diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/shuttle_consoles/syndicate.dm similarity index 100% rename from code/modules/shuttle/syndicate.dm rename to code/modules/shuttle/shuttle_consoles/syndicate.dm diff --git a/code/modules/shuttle/white_ship.dm b/code/modules/shuttle/shuttle_consoles/white_ship.dm similarity index 100% rename from code/modules/shuttle/white_ship.dm rename to code/modules/shuttle/shuttle_consoles/white_ship.dm diff --git a/code/modules/shuttle/stationary_port/port_types.dm b/code/modules/shuttle/stationary_port/port_types.dm new file mode 100644 index 0000000000000..047856566c2db --- /dev/null +++ b/code/modules/shuttle/stationary_port/port_types.dm @@ -0,0 +1,100 @@ +/// Subtype for escape pod ports so that we can give them trait behaviour +/obj/docking_port/stationary/escape_pod + name = "escape pod loader" + height = 5 + width = 3 + dwidth = 1 + roundstart_template = /datum/map_template/shuttle/escape_pod/default + /// Set to true if you have a snowflake escape pod dock which needs to always have the normal pod or some other one + var/enforce_specific_pod = FALSE + +/obj/docking_port/stationary/escape_pod/Initialize(mapload) + . = ..() + if (enforce_specific_pod) + return + + if (HAS_TRAIT(SSstation, STATION_TRAIT_SMALLER_PODS)) + roundstart_template = /datum/map_template/shuttle/escape_pod/cramped + return + if (HAS_TRAIT(SSstation, STATION_TRAIT_BIGGER_PODS)) + roundstart_template = /datum/map_template/shuttle/escape_pod/luxury + +// should fit the syndicate infiltrator, and smaller ships like the battlecruiser corvettes and fighters +/obj/docking_port/stationary/syndicate + name = "near the station" + dheight = 1 + dwidth = 12 + height = 17 + width = 23 + shuttle_id = "syndicate_nearby" + +/obj/docking_port/stationary/syndicate/northwest + name = "northwest of station" + shuttle_id = "syndicate_nw" + +/obj/docking_port/stationary/syndicate/northeast + name = "northeast of station" + shuttle_id = "syndicate_ne" + +/obj/docking_port/stationary/transit + name = "In Transit" + override_can_dock_checks = TRUE + /// The turf reservation returned by the transit area request + var/datum/turf_reservation/reserved_area + /// The area created during the transit area reservation + var/area/shuttle/transit/assigned_area + /// The mobile port that owns this transit port + var/obj/docking_port/mobile/owner + +/obj/docking_port/stationary/transit/Initialize(mapload) + . = ..() + SSshuttle.transit_docking_ports += src + +/obj/docking_port/stationary/transit/Destroy(force=FALSE) + if(force) + if(get_docked()) + log_world("A transit dock was destroyed while something was docked to it.") + SSshuttle.transit_docking_ports -= src + if(owner) + if(owner.assigned_transit == src) + owner.assigned_transit = null + owner = null + if(!QDELETED(reserved_area)) + qdel(reserved_area) + reserved_area = null + return ..() + +/obj/docking_port/stationary/picked + ///Holds a list of map name strings for the port to pick from + var/list/shuttlekeys + +/obj/docking_port/stationary/picked/Initialize(mapload) + . = ..() + if(!LAZYLEN(shuttlekeys)) + WARNING("Random docking port [shuttle_id] loaded with no shuttle keys") + return + var/selectedid = pick(shuttlekeys) + roundstart_template = SSmapping.shuttle_templates[selectedid] + +/obj/docking_port/stationary/picked/whiteship + name = "Deep Space" + shuttle_id = "whiteship_away" + height = 45 //Width and height need to remain in sync with the size of whiteshipdock.dmm, otherwise we'll get overflow + width = 44 + dheight = 18 + dwidth = 18 + dir = 2 + shuttlekeys = list( + "whiteship_meta", + "whiteship_pubby", + "whiteship_box", + "whiteship_cere", + "whiteship_kilo", + "whiteship_donut", + "whiteship_delta", + "whiteship_tram", + "whiteship_personalshuttle", + "whiteship_obelisk", + "whiteship_birdshot", + ) + diff --git a/code/modules/shuttle/stationary_port/stationary_port.dm b/code/modules/shuttle/stationary_port/stationary_port.dm new file mode 100644 index 0000000000000..49437730cb071 --- /dev/null +++ b/code/modules/shuttle/stationary_port/stationary_port.dm @@ -0,0 +1,91 @@ + +/obj/docking_port/stationary + name = "dock" + + var/last_dock_time + + /// Map template to load when the dock is loaded + var/datum/map_template/shuttle/roundstart_template + /// Used to check if the shuttle template is enabled in the config file + var/json_key + ///If true, the shuttle can always dock at this docking port, despite its area checks, or if something is already docked + var/override_can_dock_checks = FALSE + +/obj/docking_port/stationary/Initialize(mapload) + . = ..() + register() + if(!area_type) + var/area/place = get_area(src) + area_type = place?.type // We might be created in nullspace + + if(mapload) + for(var/turf/T in return_turfs()) + T.turf_flags |= NO_RUINS + + if(SSshuttle.initialized) + INVOKE_ASYNC(SSshuttle, TYPE_PROC_REF(/datum/controller/subsystem/shuttle, setup_shuttles), list(src)) + +#ifdef TESTING + highlight("#f00") +#endif + +/obj/docking_port/stationary/Destroy(force) + if(force) + unregister() + return ..() + +/obj/docking_port/stationary/register(replace = FALSE) + . = ..() + if(!shuttle_id) + shuttle_id = "dock" + else + port_destinations = shuttle_id + + if(!name) + name = "dock" + + var/counter = SSshuttle.assoc_stationary[shuttle_id] + if(!replace || !counter) + if(counter) + counter++ + SSshuttle.assoc_stationary[shuttle_id] = counter + shuttle_id = "[shuttle_id]_[counter]" + name = "[name] [counter]" + else + SSshuttle.assoc_stationary[shuttle_id] = 1 + + if(!port_destinations) + port_destinations = shuttle_id + + SSshuttle.stationary_docking_ports += src + +/obj/docking_port/stationary/unregister() + . = ..() + SSshuttle.stationary_docking_ports -= src + +/obj/docking_port/stationary/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) + . = ..() + if(area_type) // We already have one + return + var/area/newarea = get_area(src) + area_type = newarea?.type + +/obj/docking_port/stationary/proc/load_roundstart() + if(json_key) + var/sid = SSmapping.current_map.shuttles[json_key] + roundstart_template = SSmapping.shuttle_templates[sid] + if(!roundstart_template) + CRASH("json_key:[json_key] value \[[sid]\] resulted in a null shuttle template for [src]") + else if(roundstart_template) // passed a PATH + var/sid = "[initial(roundstart_template.port_id)]_[initial(roundstart_template.suffix)]" + + roundstart_template = SSmapping.shuttle_templates[sid] + if(!roundstart_template) + CRASH("Invalid path ([sid]/[roundstart_template]) passed to docking port.") + + if(roundstart_template) + SSshuttle.action_load(roundstart_template, src) + +//returns first-found touching shuttleport +/obj/docking_port/stationary/get_docked() + . = locate(/obj/docking_port/mobile) in loc diff --git a/tgstation.dme b/tgstation.dme index 8256e959611e4..135f7be0446e2 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -466,6 +466,7 @@ #include "code\__HELPERS\screen_objs.dm" #include "code\__HELPERS\see_through_maps.dm" #include "code\__HELPERS\shell.dm" +#include "code\__HELPERS\shuttle.dm" #include "code\__HELPERS\spatial_info.dm" #include "code\__HELPERS\spawns.dm" #include "code\__HELPERS\stack_trace.dm" @@ -5937,28 +5938,32 @@ #include "code\modules\research\xenobiology\vatgrowing\samples\viruses\_virus.dm" #include "code\modules\security_levels\keycard_authentication.dm" #include "code\modules\security_levels\security_level_datums.dm" -#include "code\modules\shuttle\arrivals.dm" -#include "code\modules\shuttle\assault_pod.dm" -#include "code\modules\shuttle\battlecruiser_starfury.dm" -#include "code\modules\shuttle\computer.dm" -#include "code\modules\shuttle\docking.dm" -#include "code\modules\shuttle\elevator.dm" -#include "code\modules\shuttle\emergency.dm" -#include "code\modules\shuttle\ferry.dm" -#include "code\modules\shuttle\infiltrator.dm" -#include "code\modules\shuttle\manipulator.dm" -#include "code\modules\shuttle\medisim.dm" -#include "code\modules\shuttle\monastery.dm" -#include "code\modules\shuttle\navigation_computer.dm" -#include "code\modules\shuttle\on_move.dm" -#include "code\modules\shuttle\ripple.dm" #include "code\modules\shuttle\shuttle.dm" -#include "code\modules\shuttle\shuttle_rotate.dm" -#include "code\modules\shuttle\spaceship_navigation_beacon.dm" -#include "code\modules\shuttle\special.dm" -#include "code\modules\shuttle\supply.dm" -#include "code\modules\shuttle\syndicate.dm" -#include "code\modules\shuttle\white_ship.dm" +#include "code\modules\shuttle\misc\manipulator.dm" +#include "code\modules\shuttle\misc\medisim.dm" +#include "code\modules\shuttle\misc\ripple.dm" +#include "code\modules\shuttle\misc\spaceship_navigation_beacon.dm" +#include "code\modules\shuttle\misc\special.dm" +#include "code\modules\shuttle\mobile_port\mobile_port.dm" +#include "code\modules\shuttle\mobile_port\shuttle_move.dm" +#include "code\modules\shuttle\mobile_port\shuttle_move_callbacks.dm" +#include "code\modules\shuttle\mobile_port\shuttle_rotate_callbacks.dm" +#include "code\modules\shuttle\mobile_port\variants\arrivals.dm" +#include "code\modules\shuttle\mobile_port\variants\assault_pod.dm" +#include "code\modules\shuttle\mobile_port\variants\battlecruiser_starfury.dm" +#include "code\modules\shuttle\mobile_port\variants\elevator.dm" +#include "code\modules\shuttle\mobile_port\variants\ferry.dm" +#include "code\modules\shuttle\mobile_port\variants\infiltrator.dm" +#include "code\modules\shuttle\mobile_port\variants\supply.dm" +#include "code\modules\shuttle\mobile_port\variants\emergency\emergency.dm" +#include "code\modules\shuttle\mobile_port\variants\emergency\emergency_console.dm" +#include "code\modules\shuttle\mobile_port\variants\emergency\emergency_types.dm" +#include "code\modules\shuttle\mobile_port\variants\emergency\pods.dm" +#include "code\modules\shuttle\shuttle_consoles\monastery.dm" +#include "code\modules\shuttle\shuttle_consoles\navigation_computer.dm" +#include "code\modules\shuttle\shuttle_consoles\shuttle_console.dm" +#include "code\modules\shuttle\shuttle_consoles\syndicate.dm" +#include "code\modules\shuttle\shuttle_consoles\white_ship.dm" #include "code\modules\shuttle\shuttle_events\_shuttle_events.dm" #include "code\modules\shuttle\shuttle_events\blackhole.dm" #include "code\modules\shuttle\shuttle_events\carp.dm" @@ -5968,6 +5973,8 @@ #include "code\modules\shuttle\shuttle_events\player_controlled.dm" #include "code\modules\shuttle\shuttle_events\projectile.dm" #include "code\modules\shuttle\shuttle_events\turbulence.dm" +#include "code\modules\shuttle\stationary_port\port_types.dm" +#include "code\modules\shuttle\stationary_port\stationary_port.dm" #include "code\modules\spatial_grid\cell_tracker.dm" #include "code\modules\spells\spell.dm" #include "code\modules\spells\spell_types\madness_curse.dm"