From 54f0097679ad0871f8dcc7cba4a6fd43eb1e365a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:14:03 +0100 Subject: [PATCH 01/36] Bump axios from 0.21.4 to 1.6.0 in /tgui (#2580) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tgui/packages/tgui-dev-server/package.json | 2 +- tgui/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json index 2835e47288d..a45a38c163d 100644 --- a/tgui/packages/tgui-dev-server/package.json +++ b/tgui/packages/tgui-dev-server/package.json @@ -4,7 +4,7 @@ "version": "4.3.0", "type": "module", "dependencies": { - "axios": "^1.3.4", + "axios": "^1.6.0", "glob": "^7.1.7", "source-map": "^0.7.4", "stacktrace-parser": "^0.1.10", diff --git a/tgui/yarn.lock b/tgui/yarn.lock index 25b63109d37..7c1f9d0aeea 100644 --- a/tgui/yarn.lock +++ b/tgui/yarn.lock @@ -3014,14 +3014,14 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.3.4": - version: 1.3.4 - resolution: "axios@npm:1.3.4" +"axios@npm:^1.6.0": + version: 1.6.1 + resolution: "axios@npm:1.6.1" dependencies: follow-redirects: ^1.15.0 form-data: ^4.0.0 proxy-from-env: ^1.1.0 - checksum: 7440edefcf8498bc3cdf39de00443e8101f249972c83b739c6e880d9d669fea9486372dbe8739e88b3bf8bb1ad15f6106693f206f078f4516fe8fd47b1c3093c + checksum: 573f03f59b7487d54551b16f5e155d1d130ad4864ed32d1da93d522b78a57123b34e3bde37f822a65ee297e79f1db840f9ad6514addff50d3cbf5caeed39e8dc languageName: node linkType: hard @@ -9217,7 +9217,7 @@ __metadata: version: 0.0.0-use.local resolution: "tgui-dev-server@workspace:packages/tgui-dev-server" dependencies: - axios: ^1.3.4 + axios: ^1.6.0 glob: ^7.1.7 source-map: ^0.7.4 stacktrace-parser: ^0.1.10 From 039f97e3202f3adc7787d6495919fa6ae232ff9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Nov 2023 01:16:37 +0100 Subject: [PATCH 02/36] Bump loader-utils from 2.0.2 to 2.0.4 in /tgui (#2583) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tgui/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tgui/yarn.lock b/tgui/yarn.lock index 7c1f9d0aeea..ec9e3716ed0 100644 --- a/tgui/yarn.lock +++ b/tgui/yarn.lock @@ -6751,13 +6751,13 @@ __metadata: linkType: hard "loader-utils@npm:^2.0.0": - version: 2.0.2 - resolution: "loader-utils@npm:2.0.2" + version: 2.0.4 + resolution: "loader-utils@npm:2.0.4" dependencies: big.js: ^5.2.2 emojis-list: ^3.0.0 json5: ^2.1.2 - checksum: 9078d1ed47cadc57f4c6ddbdb2add324ee7da544cea41de3b7f1128e8108fcd41cd3443a85b7ee8d7d8ac439148aa221922774efe4cf87506d4fb054d5889303 + checksum: a5281f5fff1eaa310ad5e1164095689443630f3411e927f95031ab4fb83b4a98f388185bb1fe949e8ab8d4247004336a625e9255c22122b815bb9a4c5d8fc3b7 languageName: node linkType: hard From bd41cac748dfb4e8ec34645ffddc093dfdb50124 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Thu, 23 Nov 2023 00:13:34 +0000 Subject: [PATCH 03/36] Automatic changelog compile [ci skip] --- html/changelog.html | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index 595a46cc8cb..3c7cb1ec98b 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -183,33 +183,6 @@

Absolucy updated:

  • LOOC is much less janky now. You can properly both hear LOOC as a ghost, and both hear and talk on LOOC while inside another object (including cases such as split personalities, recalled holoparasites, desynchronized mobs, jaunting mobs)!
  • Fixed admins seeing LOOC message senders as themselves rather the actual sender
  • - -

    21 September 2023

    -

    BlueHNT updated:

    - -

    DeltaFire15 updated:

    - -

    Ikalpo updated:

    - -

    bruhlookatthisdood updated:

    - -

    someone543 updated:

    - GoonStation 13 Development Team From b09b8bbd48bfc73ed25eb1a132165daacd7bb606 Mon Sep 17 00:00:00 2001 From: Bokkiewokkie <43698041+Bokkiewokkie@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:49:47 +0100 Subject: [PATCH 04/36] Syndicate bomb plushie traitor item (#2571) --- .../code/game/objects/items/nsv13_plushes.dm | 5 +++++ .../code/game/objects/items/storage_items.dm | 8 ++++++++ nsv13/code/modules/uplink/uplink_items.dm | 8 ++++++++ nsv13/icons/obj/custom_plushes.dmi | Bin 1031 -> 1286 bytes 4 files changed, 21 insertions(+) diff --git a/nsv13/code/game/objects/items/nsv13_plushes.dm b/nsv13/code/game/objects/items/nsv13_plushes.dm index ab35636fe29..e648207b27a 100644 --- a/nsv13/code/game/objects/items/nsv13_plushes.dm +++ b/nsv13/code/game/objects/items/nsv13_plushes.dm @@ -25,6 +25,11 @@ attack_verb = list("bumped", "rammed", "missiled") squeak_override = list('nsv13/sound/effects/fighters/autocannon.ogg'=1) +/obj/item/toy/plush/lfighter/synlfighter + name = "Syndicate light fighter plush" + desc = "An adorable stuffed toy shaped like a Su-818 Rapier light fighter with Syndicate livery." + icon_state = "synlightfighter" + /obj/item/toy/plush/transport name = "utility craft plush" desc = "An adorable stuffed toy shaped like a Su-437 Sabre utility vessel." diff --git a/nsv13/code/game/objects/items/storage_items.dm b/nsv13/code/game/objects/items/storage_items.dm index 50c2c0f8d3c..3bbae9f0c02 100644 --- a/nsv13/code/game/objects/items/storage_items.dm +++ b/nsv13/code/game/objects/items/storage_items.dm @@ -63,3 +63,11 @@ new /obj/item/clothing/gloves/maid(src) new /obj/item/clothing/under/costume/maid(src) +/obj/item/storage/box/syndie_kit/plushie + name = "\improper DIY plushbomb kit" + +/obj/item/storage/box/syndie_kit/plushie/PopulateContents() + new /obj/item/toy/plush/lfighter/synlfighter(src) + new /obj/item/kitchen/knife/combat/survival(src) + new /obj/item/grenade/syndieminibomb(src) + new /obj/item/screwdriver(src) diff --git a/nsv13/code/modules/uplink/uplink_items.dm b/nsv13/code/modules/uplink/uplink_items.dm index 2ea0e0785d9..fd5ac403f4c 100644 --- a/nsv13/code/modules/uplink/uplink_items.dm +++ b/nsv13/code/modules/uplink/uplink_items.dm @@ -23,3 +23,11 @@ cost = 20 cant_discount = TRUE surplus = 0 + +/datum/uplink_item/explosives/fighterplushie + name = "Plushie Bomb Kit" + desc = "A kit with all of the tools and items required to assemble your very own plushie bomb! \ + Take out the unsuspecting Nanotrasen scum with a gift they'll never expect. \ + Contains a Syndicate light fighter plush, syndicate minibomb, screwdriver and a survival knife" + item = /obj/item/storage/box/syndie_kit/plushie + cost = 8 diff --git a/nsv13/icons/obj/custom_plushes.dmi b/nsv13/icons/obj/custom_plushes.dmi index 07af6871b37f26acb758ef2d24bc132d094fe7a0..63b3474c15dc06ed9291c5e15bcbc59d96e96ecb 100644 GIT binary patch delta 1194 zcmV;b1XcTo2!;xOiBL{Q4GJ0x0000DNk~Le0001B0000$2m=5B0MhD$JOBUya!^cE zMZmzoR$Ot8kC-_;JtKCHyT!+VfPg%>rJbRqU0q#y_~uPbO;A;2FfTG_QZH4f+2AxZ zB1e8ddBM6RC3O)Rsv9ibTy3COSil!6ZU6wQA|k-Rz&kZDA3!-Eab!1>fIp~~Ns3aA zuVS=TH&{$fQkGz!0m$lT00001bW%=J06^y0W&i*Hj**6Ke_WYIlKPUO#Ju8y{Gt+q zDswaQGV{`LDN|N(^>YD-1pouOQB=L|7c&3=1E@(vK~z|U?N|+Sn=lN;CI(YXN$9$) zxj1~3{QtkWC%Ic&u+0ItyWLh12U4f+@gz$YNs@og#Gl5O3k*v)kS-A%;s`>;=hJL~ z;gBEV1bTs>e^?@TSRhz%VOs7b*sDO~aR8%DbiOL8IKiWQjN;2$#u%#dSjHK|DXKu| zI_jgeIL3V>bQ1*-s)?@CGUU1PIqne2?OT-^w}FiCufzba}o$cS|sP}M|MsE3yA?JrU)qn)$8sLpmr^)_*=A`8~MumVa!N=M3f zT+bAg7Xs!3a>}^^q1cQ&&1eJ)xfCc?2Jg6@e+4;xf&WWvIC#hPTtLGG$mOxtjaNpM z8XQ%y7FBPDclPvF9o%WUNSswv$kOnZwKPu_-*Ug~%l-y43+wcXW0xV(qVx7@_u)(b ze)}8$*539o2;OtU4EA=l*F6k^6+{SF!5#vM2au=&%2 ze{Ffb-AI7FKp)V19<=d>!P;xf^X+w07-%zhO}nJceU6XtBET@5w}?P5f`$jHh}s{| z)^9s7^jJkb@VoT{gsRc9DKv1@yH6PiwY3A@28*}#61XK1XdG27xHXv)0JXD2ck2dd z=~fd^>r4%arc9H@Oc`GLVI0Xhlxh7qe-8ah1_(gU<2oyW-e*Q|ANujmGFp2d`{Dk& zmH-{sdkmDB5ZHF?KE$9K*ZPQ_)38VzxTq2C!)Jn9OI4_ci+bxmtRtX0N6j-oUQm-h z3o@2JqD&!xl1&wl@j1`tYltZbpYv?}8v2C~=L^OCzb}(Ja=xWdY+|7LfX0=FaBo|q z`hYneo3=If5Uo+=_mNZ3Vr=!1Q(&9|&%9GmIRzW=>ETA=?rC@*b8N?Ver=ItgAef&|^zh_#{)qh|=0nq6?Tnn)z2LJ#707*qo IM6N<$g1iM9cK`qY delta 911 zcmV;A191F?3Wo?&iBL{Q4GJ0x0000DNk~Le0000$0000$2m=5B0G+pi>;M1&NKi~v zMZmzoR$Ot8kC-ENkGsXkJh-Kup`>~E=1^5+Xi_g#r`aM$em{A^pjcRueI6B{&C@3U z0004WQchCLQrLHW?p7qIxc0(3a)-GV7~zXgpER5 z#Q8zs0008mNklfpE0|jr|$K7YO4t_5u7Az}p4Dm5G-CR}5(CPY6Er0p8tzdspAhu8%;s zE(7v&-v;tacLMR!+(%Mzi_0&6#2~9W=Uo7UfZjP9C4fQg1JxB_PdqcB7ZH?VA{dNd zPy&-vWyV)c$gcuX%ydJT-ll-Q;FYbGRfE5wLGquzvn4-#@WdZO!_T7Nu(Aub4?z@h zG$E2IYELeXnOl)Ujsw7d(YOhKIdf|iVS0-X&TW2@G}&_fQ5nQ=9y|UkPwq_lX%5@&45@XNU zantdw0f1cKN7TO22h$BP-ZvfJ?xq_gS>~edBzYRkuMrM}ptndMS0mwL7bN`~sWt9E zIIs%`^!FM-v^+|G4x<1`Z7&f-Yl|lyXT+nXpedVB*mx#Q-IWD9);;%iKfz_M0HpLv z!uAqLE`$={^Eguy@)Ai$O6O4^fB^dlEKrfe8!cAEgiF6dW+&i1|n91S#Blg^==pTc0S|et1z_04C;%Qhkl1 zdJnP{yGD{C7~@*}QB_&%zZHqQ7$3zICW6Fo2tk{bmLz^tO4`h{q!!2?vTZ|Vu%WeV z$P7YeV2o!5DKjv}<#OV%x-|2e5_esF6$WNTpcfz5yYTdZol+c>aTUkIvH&(f8RGyj lEqMK>)Xr%d(Rlj-@(W&mAqFg30>=OV002ovPDHLkV1nUllqCQF From 4fd23e494951c5bd2ff6a7bea50e598f7b7e2caa Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:50:40 -0700 Subject: [PATCH 05/36] Automatic changelog generation for PR #2571 [ci skip] --- html/changelogs/AutoChangeLog-pr-2571.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2571.yml diff --git a/html/changelogs/AutoChangeLog-pr-2571.yml b/html/changelogs/AutoChangeLog-pr-2571.yml new file mode 100644 index 00000000000..d43b73e9dd4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2571.yml @@ -0,0 +1,5 @@ +author: Bokkiewokkie +delete-after: true +changes: + - rscadd: Added Syndicate light fighter plushie + - rscadd: Added DIY fighter plush bomb kit traitor item From e050ad9a0dc673a7089a6649bce8521627731a03 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:03:46 +0000 Subject: [PATCH 06/36] Automatic changelog compile [ci skip] --- html/changelog.html | 7 +++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-2571.yml | 5 ----- 3 files changed, 11 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-2571.yml diff --git a/html/changelog.html b/html/changelog.html index 3c7cb1ec98b..a9634bdf819 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,13 @@ -->
    +

    24 November 2023

    +

    Bokkiewokkie updated:

    +
      +
    • Added Syndicate light fighter plushie
    • +
    • Added DIY fighter plush bomb kit traitor item
    • +
    +

    10 November 2023

    DuskLight9978 updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index a438826d218..7d125e65150 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -1815,3 +1815,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - rscadd: Ghost controlled ships can now use gauss - rscadd: Added coffee machines to PVP ships - tweak: Syndicate coffee drinkers now get a altered mood event +2023-11-24: + Bokkiewokkie: + - rscadd: Added Syndicate light fighter plushie + - rscadd: Added DIY fighter plush bomb kit traitor item diff --git a/html/changelogs/AutoChangeLog-pr-2571.yml b/html/changelogs/AutoChangeLog-pr-2571.yml deleted file mode 100644 index d43b73e9dd4..00000000000 --- a/html/changelogs/AutoChangeLog-pr-2571.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Bokkiewokkie -delete-after: true -changes: - - rscadd: Added Syndicate light fighter plushie - - rscadd: Added DIY fighter plush bomb kit traitor item From 7e082e3d2d8ada52b664169046ff8ee71ff37108 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 1 Dec 2023 00:15:34 +0000 Subject: [PATCH 07/36] Automatic changelog compile [ci skip] --- html/changelog.html | 7 ------- 1 file changed, 7 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index a9634bdf819..04fc26131ac 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -183,13 +183,6 @@

      SerynEngi updated:

    • Nucleum tank to the Gladius
    • Added nucleum gas to the nucleum tanks on the Gladius, Galactica, Tycoon and Aetherwhisp
    - -

    29 September 2023

    -

    Absolucy updated:

    -
      -
    • LOOC is much less janky now. You can properly both hear LOOC as a ghost, and both hear and talk on LOOC while inside another object (including cases such as split personalities, recalled holoparasites, desynchronized mobs, jaunting mobs)!
    • -
    • Fixed admins seeing LOOC message senders as themselves rather the actual sender
    • -
    GoonStation 13 Development Team From a0613c355421a000f746b03f65cea58da91d5096 Mon Sep 17 00:00:00 2001 From: Corvid Date: Fri, 1 Dec 2023 15:49:42 -0500 Subject: [PATCH 08/36] Updates client versions (#2411) Co-authored-by: Bokkiewokkie <43698041+Bokkiewokkie@users.noreply.github.com> Co-authored-by: Kyle Spier-Swenson Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> --- .../configuration/entries/general.dm | 8 ++ code/modules/client/client_procs.dm | 104 +++++++++++------- config/config.txt | 16 ++- 3 files changed, 80 insertions(+), 48 deletions(-) diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 2934a830c8d..052b64f035f 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -413,6 +413,10 @@ config_entry_value = null min_val = 500 +/datum/config_entry/number/client_warn_build + default = null + min_val = 0 + /datum/config_entry/string/client_warn_message config_entry_value = "Your version of byond may have issues or be blocked from accessing this server in the future." @@ -429,6 +433,10 @@ config_entry_value = null min_val = 0 +/datum/config_entry/number/client_max_build + config_entry_value = null + min_val = 0 + /datum/config_entry/number/minute_topic_limit config_entry_value = null min_val = 0 diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 8091627245b..62c19659dec 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -4,7 +4,6 @@ #define UPLOAD_LIMIT 10485760 //Restricts client uploads to the server to 1MB //Could probably do with being lower. -#define MAX_RECOMMENDED_CLIENT 1589 GLOBAL_LIST_INIT(blacklisted_builds, list( "1407" = "bug preventing client display overrides from working leads to clients being able to see things/mobs they shouldn't be able to see", @@ -264,28 +263,44 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( log_access("Login: [key_name(src)] from [address ? address : "localhost"]-[computer_id] || BYOND v[full_version]") var/alert_mob_dupe_login = FALSE + var/alert_admin_multikey = FALSE if(CONFIG_GET(flag/log_access)) - for(var/I in GLOB.clients) - if(!I || I == src) + var/list/joined_players = list() + for(var/player_ckey in GLOB.joined_player_list) + joined_players[player_ckey] = 1 + + for(var/joined_player_ckey in (GLOB.directory | joined_players)) + if (!joined_player_ckey || joined_player_ckey == ckey) continue - var/client/C = I - if(C.key && (C.key != key) ) - var/matches - if( (C.address == address) ) - matches += "IP ([address])" - if( (C.computer_id == computer_id) ) - if(matches) - matches += " and " - matches += "ID ([computer_id])" - alert_mob_dupe_login = TRUE - if(matches) - if(C) - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)].") - log_access("Notice: [key_name(src)] has the same [matches] as [key_name(C)].") - else - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)] (no longer logged in). ") - log_access("Notice: [key_name(src)] has the same [matches] as [key_name(C)] (no longer logged in).") + var/datum/preferences/joined_player_preferences = GLOB.preferences_datums[joined_player_ckey] + if(!joined_player_preferences) + continue //this shouldn't happen. + + var/client/C = GLOB.directory[joined_player_ckey] + var/in_round = "" + if (joined_players[joined_player_ckey]) + in_round = " who has played in the current round" + var/message_type = "Notice" + + var/matches + if(joined_player_preferences.last_ip == address) + matches += "IP ([address])" + if(joined_player_preferences.last_id == computer_id) + if(matches) + matches = "BOTH [matches] and " + alert_admin_multikey = TRUE + message_type = "MULTIKEY" + matches += "Computer ID ([computer_id])" + alert_mob_dupe_login = TRUE + + if(matches) + if(C) + message_admins("[message_type]: Connecting player [key_name_admin(src)] has the same [matches] as [key_name_admin(C)][in_round].") + log_admin_private("[message_type]: Connecting player [key_name(src)] has the same [matches] as [key_name(C)][in_round].") + else + message_admins("[message_type]: Connecting player [key_name_admin(src)] has the same [matches] as [joined_player_ckey](no longer logged in)[in_round]. ") + log_admin_private("[message_type]: Connecting player [key_name(src)] has the same [matches] as [joined_player_ckey](no longer logged in)[in_round].") if(GLOB.player_details[ckey]) player_details = GLOB.player_details[ckey] player_details.byond_version = full_version @@ -310,12 +325,14 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( to_chat_immediate(src, "Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].") to_chat_immediate(src, "Please download a new version of byond. If [byond_build] is the latest, you can go to BYOND's website to download other versions.") if(connecting_admin) - to_chat(src, "As an admin, you are being allowed to continue using this version, but please consider changing byond versions") + to_chat_immediate(src, "As an admin, you are being allowed to continue using this version, but please consider changing byond versions") else qdel(src) return - if(byond_build > MAX_RECOMMENDED_CLIENT) - to_chat(src, "Your version of byond is over the maximum recommended version for clients (build [MAX_RECOMMENDED_CLIENT]) and may be unstable.") + + var/max_recommended_client = CONFIG_GET(number/client_max_build) + if(byond_build > max_recommended_client) + to_chat(src, "Your version of byond is over the maximum recommended version for clients (build [max_recommended_client]) and may be unstable.") to_chat(src, "Please download an older version of byond. You can go to BYOND's website to download other versions.") if(SSinput.initialized) set_macros() @@ -323,41 +340,50 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( // Initialize tgui panel tgui_panel.Initialize() - if(alert_mob_dupe_login) - spawn() - alert(mob, "You have logged in already with another key this round, please log out of this one NOW or risk being banned!") + if(alert_mob_dupe_login && !holder) + var/dupe_login_message = "Your ComputerID has already logged in with another key this round, please log out of this one NOW or risk being banned!" + if (alert_admin_multikey) + dupe_login_message += "\nAdmins have been informed." + message_admins("MULTIKEYING: [key_name_admin(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.") + log_admin_private("MULTIKEYING: [key_name(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.") + spawn(0.5 SECONDS) //needs to run during world init, do not convert to add timer + alert(mob, dupe_login_message) //players get banned if they don't see this message, do not convert to tgui_alert (or even tg_alert) please. + to_chat_immediate(mob, "[dupe_login_message]") + connection_time = world.time connection_realtime = world.realtime connection_timeofday = world.timeofday winset(src, null, "command=\".configure graphics-hwmode on\"") - var/cev = CONFIG_GET(number/client_error_version) - var/ceb = CONFIG_GET(number/client_error_build) - var/cwv = CONFIG_GET(number/client_warn_version) - if (byond_version < cev || byond_build < ceb) //Out of date client. + var/breaking_version = CONFIG_GET(number/client_error_version) + var/breaking_build = CONFIG_GET(number/client_error_build) + var/warn_version = CONFIG_GET(number/client_warn_version) + var/warn_build = CONFIG_GET(number/client_warn_build) + + if (byond_version < breaking_version || (byond_version == breaking_version && byond_build < breaking_build)) //Out of date client. to_chat_immediate(src, "Your version of BYOND is too old:") to_chat_immediate(src, CONFIG_GET(string/client_error_message)) to_chat_immediate(src, "Your version: [byond_version].[byond_build]") - to_chat_immediate(src, "Required version: [cev].[ceb] or later") + to_chat_immediate(src, "Required version: [breaking_version].[breaking_build] or later") to_chat_immediate(src, "Visit BYOND's website to get the latest version of BYOND.") if (connecting_admin) - to_chat(src, "Because you are an admin, you are being allowed to walk past this limitation, But it is still STRONGLY suggested you upgrade") + to_chat_immediate(src, "Because you are an admin, you are being allowed to walk past this limitation, But it is still STRONGLY suggested you upgrade") else qdel(src) - return 0 - else if (byond_version < cwv) //We have words for this client. + return + else if (byond_version < warn_version || (byond_version == warn_version && byond_build < warn_build)) //We have words for this client. if(CONFIG_GET(flag/client_warn_popup)) var/msg = "Your version of byond may be getting out of date:
    " msg += CONFIG_GET(string/client_warn_message) + "

    " - msg += "Your version: [byond_version]
    " - msg += "Required version to remove this message: [cwv] or later
    " + msg += "Your version: [byond_version].[byond_build]
    " + msg += "Required version to remove this message: [warn_version].[warn_build] or later
    " msg += "Visit BYOND's website to get the latest version of BYOND.
    " src << browse(msg, "window=warning_popup") else to_chat(src, "Your version of byond may be getting out of date:") to_chat(src, CONFIG_GET(string/client_warn_message)) - to_chat(src, "Your version: [byond_version]") - to_chat(src, "Required version to remove this message: [cwv] or later") + to_chat(src, "Your version: [byond_version].[byond_build]") + to_chat(src, "Required version to remove this message: [warn_version].[warn_build] or later") to_chat(src, "Visit BYOND's website to get the latest version of BYOND.") if (connection == "web" && !connecting_admin) @@ -831,7 +857,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( var/url = winget(src, null, "url") //special javascript to make them reconnect under a new window. src << browse({"byond://[url]?token=[token]"}, "border=0;titlebar=0;size=1x1;window=redirect") - to_chat(src, {"You will be automatically taken to the game, if not, click here to be taken manually"}) + to_chat_immediate(src, {"You will be automatically taken to the game, if not, click here to be taken manually"}) /client/proc/note_randomizer_user() add_system_note("CID-Error", "Detected as using a cid randomizer.") diff --git a/config/config.txt b/config/config.txt index 8c02e315f2d..bcd3e56e5c5 100644 --- a/config/config.txt +++ b/config/config.txt @@ -456,18 +456,16 @@ AUTOADMIN_RANK Admin ## These trigger for any version below (non-inclusive) the given version, so 510 triggers on 509 or lower. ## These messages will be followed by one stating the clients current version and the required version for clarity. ## If CLIENT_WARN_POPUP is uncommented a popup window with the message will be displayed instead - -#Warn, Allows connection but warns about possibile instability. -CLIENT_WARN_POPUP -CLIENT_WARN_VERSION 0 -#Wish we had a build check for this -CLIENT_WARN_MESSAGE Currently Unused! If you see this message yell at Francinum! - -#Error, Blocks non-admin connection. +#CLIENT_WARN_VERSION 513 +#CLIENT_WARN_BUILD 1421 +#CLIENT_WARN_POPUP +#CLIENT_WARN_MESSAGE 512 is no longer being directly supported as version 513 is set to become the new stable version soon. We've made a number of changes to take advantage of the improvements made in 513 which should make for a smoother experience. We will be removing support for 512 when this new version replaces it as stable, so it's recommended that you upgrade now. (You can update to the BETA via the website or directly in the BYOND client) CLIENT_ERROR_VERSION 514 CLIENT_ERROR_MESSAGE Your version of byond is not supported. Please upgrade. ## The minimum build needed for joining the server, if using 513, a good minimum build would be 1526. -CLIENT_ERROR_BUILD 1583 +CLIENT_ERROR_BUILD 1589 +## The maximum build recommended for clients +CLIENT_MAX_BUILD 1614 ## TOPIC RATE LIMITING ## This allows you to limit how many topic calls (clicking on an interface window) the client can do in any given game second and/or game minute. From af849053ada4b2e727610c992e21e651df268bb2 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:50:28 -0700 Subject: [PATCH 09/36] Automatic changelog generation for PR #2411 [ci skip] --- html/changelogs/AutoChangeLog-pr-2411.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2411.yml diff --git a/html/changelogs/AutoChangeLog-pr-2411.yml b/html/changelogs/AutoChangeLog-pr-2411.yml new file mode 100644 index 00000000000..248479918ee --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2411.yml @@ -0,0 +1,5 @@ +author: covertcorvid +delete-after: true +changes: + - config: Made repo config client version match the server + - server: Increased max recommended client version to 515.1608 From cbdbc7528fa67f2d3c77234cc172800337b190b9 Mon Sep 17 00:00:00 2001 From: Bokkiewokkie <43698041+Bokkiewokkie@users.noreply.github.com> Date: Fri, 1 Dec 2023 21:51:34 +0100 Subject: [PATCH 10/36] Some munitions accessories (#2574) --- nsv13.dme | 3 ++- .../mob/dead/new_player/sprite_accessories.dm | 20 ++++++++++++++++++ nsv13/icons/mob/underwear.dmi | Bin 0 -> 1029 bytes 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 nsv13/code/modules/mob/dead/new_player/sprite_accessories.dm create mode 100644 nsv13/icons/mob/underwear.dmi diff --git a/nsv13.dme b/nsv13.dme index d7ab5d32414..ebba4f603ad 100644 --- a/nsv13.dme +++ b/nsv13.dme @@ -3845,8 +3845,8 @@ #include "nsv13\code\modules\antagonists\boarders\boarders.dm" #include "nsv13\code\modules\antagonists\boarders\pirate_boarders.dm" #include "nsv13\code\modules\atmospherics\gasmixtures\reactions.dm" -#include "nsv13\code\modules\atmospherics\machinery\components\unary_devices\outlet_injector.dm" #include "nsv13\code\modules\atmospherics\machinery\components\binary_devices\constrictor.dm" +#include "nsv13\code\modules\atmospherics\machinery\components\unary_devices\outlet_injector.dm" #include "nsv13\code\modules\atmospherics\machinery\components\unary_devices\tank.dm" #include "nsv13\code\modules\atmospherics\machinery\components\unary_devices\vent_pump.dm" #include "nsv13\code\modules\cargo\mission_rewards.dm" @@ -3910,6 +3910,7 @@ #include "nsv13\code\modules\mining\asteroid.dm" #include "nsv13\code\modules\mob\mob.dm" #include "nsv13\code\modules\mob\mob_helpers.dm" +#include "nsv13\code\modules\mob\dead\new_player\sprite_accessories.dm" #include "nsv13\code\modules\mob\dead\observer\oberserver.dm" #include "nsv13\code\modules\mob\living\carbon\carbon.dm" #include "nsv13\code\modules\mob\living\carbon\examine_tgui.dm" diff --git a/nsv13/code/modules/mob/dead/new_player/sprite_accessories.dm b/nsv13/code/modules/mob/dead/new_player/sprite_accessories.dm new file mode 100644 index 00000000000..603431e0bb2 --- /dev/null +++ b/nsv13/code/modules/mob/dead/new_player/sprite_accessories.dm @@ -0,0 +1,20 @@ + +/datum/sprite_accessory/undershirt/munitions + icon = 'nsv13/icons/mob/underwear.dmi' + name = "Shirt (Munitions)" + icon_state = "shirt_mun" + +/datum/sprite_accessory/undershirt/tank_munitions + icon = 'nsv13/icons/mob/underwear.dmi' + name = "Tank Top (Munitions)" + icon_state = "tank_mun" + +/datum/sprite_accessory/socks/munitions_thigh + icon = 'nsv13/icons/mob/underwear.dmi' + name = "Thigh-high (Munitions)" + icon_state = "mun_thigh" + +/datum/sprite_accessory/socks/munitions_knee + icon = 'nsv13/icons/mob/underwear.dmi' + name = "Knee-high (Munitions)" + icon_state = "mun_knee" diff --git a/nsv13/icons/mob/underwear.dmi b/nsv13/icons/mob/underwear.dmi new file mode 100644 index 0000000000000000000000000000000000000000..5da0ca1ca69432f7de0769d511229570892f54cb GIT binary patch literal 1029 zcmV+g1p51lP)004jp0{{R3ySfFDZ*Bkpc$`yKaB_9`^iy#0_2eo` zEh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LYR3K9+w=^$4J1;d=iHkEO zv#1!PkBc*{C^0t`#5P1#SdsxWfuPFbjLf2vc%XXhDuFIZNzF;D1ZmPi(pHj~myKJO zA(yg(tDg(lj{pStLux5v?k@lU0^UhPK~#90?V8bY+aM4{r6gcL2(17A*)GCPJyUkq z4VKZg_oN{+U8KFNrH7o!Gywnr0000000935Gf|h5^6~lkQJ0f4e|^n$VfY~G^0Ru~ zA)a5{piWRbfqKHdC_X{6bh$o3{6w+RX(oTUkB_owK$ z6SU7z3i~t2)b~B?)ekM0vev&IV7~wO`}%#4G+?Rx%>4lX0Dx0tjQL)fdgYy=9{bzN zg}PrZww=IO=e+gKG4-PfizO~)X(li>dLLa`mhkp;ly6;=tx$$P6BZeEd zbF*ge)D2x6?OwV$+Fo^o3HEaK2_EhLdO6zvb#t`;0{{R3PQWv2j^ESo|Fr&B?-hMm z1d6*dwQm2X_21~%)}|W!-GlCKooOcQ_kU`C+K{#+?4W%9>e~Td;Q!SATh3cfygQkS zATw9f4%qMi)c(`9O*t*n1T7V>?Pu@>{!i;)pbfmkHr7g4zrz2C{cjr*yCHZbV88#9 z`HX`5#BWGSz&`({_AfhRKNmuILHUoM z)Bmac0RR910001BIFi15SlLS@f7a(8YnAtZdH_?jjNpFEBzu>Aj~6B&`oB`PX8-1I zUi_b!A!-APVp;HiL@?Htf4t(|{hyJ3u?=(pLGSsfn+a|0=%#(?==yPY9yO00000NkvXXu0mjfG#&5d literal 0 HcmV?d00001 From 805e231883e131075394618d1e0943ebc9d931f6 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:52:17 -0700 Subject: [PATCH 11/36] Automatic changelog generation for PR #2574 [ci skip] --- html/changelogs/AutoChangeLog-pr-2574.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2574.yml diff --git a/html/changelogs/AutoChangeLog-pr-2574.yml b/html/changelogs/AutoChangeLog-pr-2574.yml new file mode 100644 index 00000000000..d91663779e1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2574.yml @@ -0,0 +1,4 @@ +author: Bokkiewokkie +delete-after: true +changes: + - imageadd: Added munitions shirts, tank tops and socks From 7c4bcda55ff737d3218fac0e5b35c937a33581e4 Mon Sep 17 00:00:00 2001 From: DeltaFire <46569814+DeltaFire15@users.noreply.github.com> Date: Fri, 1 Dec 2023 21:52:34 +0100 Subject: [PATCH 12/36] Just some small Galactica tweaks. Nothing to see here. (#2579) --- _maps/map_files/Galactica/Galactica2.dmm | 12 +- code/controllers/subsystem/explosion.dm | 2 + nsv13/code/__DEFINES/overmap.dm | 6 + nsv13/code/datums/holocall.dm | 6 +- nsv13/code/modules/overmap/pdsr.dm | 320 ++++++++++++------ nsv13/code/modules/overmap/shieldgen.dm | 18 +- nsv13/code/modules/overmap/weapons/damage.dm | 35 +- .../modules/overmap/weapons/projectiles_fx.dm | 9 +- nsv13/icons/obj/shield_components.dmi | Bin 4192 -> 4340 bytes tgui/packages/tgui/interfaces/FTLComputer.js | 2 +- .../packages/tgui/interfaces/PDSRMainframe.js | 27 +- .../tgui/interfaces/PDSRManipulator.js | 50 ++- 12 files changed, 335 insertions(+), 152 deletions(-) diff --git a/_maps/map_files/Galactica/Galactica2.dmm b/_maps/map_files/Galactica/Galactica2.dmm index 671564e3edb..a94c783bad5 100644 --- a/_maps/map_files/Galactica/Galactica2.dmm +++ b/_maps/map_files/Galactica/Galactica2.dmm @@ -8596,9 +8596,9 @@ /obj/effect/turf_decal/stripes/line{ dir = 1 }, -/obj/machinery/atmospherics/components/binary/valve/digital/on/layer4{ - dir = 8; - name = "To Fueltank" +/obj/machinery/atmospherics/components/binary/volume_pump/layer4{ + dir = 4; + name = "Nucleium Tank To FTL" }, /turf/open/floor/durasteel/techfloor_grid, /area/engine/engineering/reactor_core) @@ -38352,9 +38352,9 @@ /obj/machinery/atmospherics/pipe/simple/orange/visible/layer2{ dir = 4 }, -/obj/machinery/atmospherics/components/binary/valve/digital/on/layer4{ - dir = 8; - name = "To Fueltank" +/obj/machinery/atmospherics/components/binary/volume_pump/layer4{ + name = "Nucleium Tank Out"; + dir = 8 }, /turf/open/floor/durasteel/techfloor_grid, /area/engine/engineering/reactor_core) diff --git a/code/controllers/subsystem/explosion.dm b/code/controllers/subsystem/explosion.dm index 88560ae1d4f..f038003535e 100644 --- a/code/controllers/subsystem/explosion.dm +++ b/code/controllers/subsystem/explosion.dm @@ -415,6 +415,8 @@ SUBSYSTEM_DEF(explosions) var/turf/T = locate(epicenter.x, epicenter.y, affecting_z) if(!T) continue + if(devastation_range - z_reduction <= 0 && heavy_impact_range - z_reduction <= 0 && light_impact_range - z_reduction <= 0) //NSV13 - explosions still relaying with 0-0-0 can cause REALLY weird behavior. + continue SSexplosions.explode(T, max(devastation_range - z_reduction, 0), max(heavy_impact_range - z_reduction, 0), diff --git a/nsv13/code/__DEFINES/overmap.dm b/nsv13/code/__DEFINES/overmap.dm index beb76f66c13..8bb8ce50961 100644 --- a/nsv13/code/__DEFINES/overmap.dm +++ b/nsv13/code/__DEFINES/overmap.dm @@ -113,3 +113,9 @@ GLOBAL_LIST_INIT(overmap_impact_sounds, list('nsv13/sound/effects/ship/freespace #define MASS_LARGE 7 //20-40 Players - Medium Capital Ships #define MASS_TITAN 150 //40+ Players - Large Capital Ships #define MASS_IMMOBILE 200 //Things that should not be moving. See: stations + +//Fun tools +#define SHIELD_NOEFFECT 0 //!Shield failed to absorb hit. +#define SHIELD_ABSORB 1 //!Shield absorbed hit. +#define SHIELD_FORCE_DEFLECT 2 //!Shield absorbed hit and is redirecting projectile with slightly turned vector. +#define SHIELD_FORCE_REFLECT 3 //!Shield absorbed hit and is redirecting projectile in reverse direction. diff --git a/nsv13/code/datums/holocall.dm b/nsv13/code/datums/holocall.dm index 9f7b712eab5..8b6d7711b09 100644 --- a/nsv13/code/datums/holocall.dm +++ b/nsv13/code/datums/holocall.dm @@ -26,7 +26,7 @@ DELAY 20 PRESET /datum/preset_holoimage/corgi NAME Burst Data - LANGUAGE /datum/language/eal + LANGUAGE /datum/language/machine SAY START NTINTEL METADATA SAY RECORDED 12-17-0000 SAY SECURITY CLASS UNCLASSIFIED @@ -320,7 +320,7 @@ DELAY 50 SAY If you need to shut down the reactor, lower the nucleium injection rate slowly. You can cycle coolant in an emergency for a quick cooling boost. DELAY 50 - SAY The reaction can be terminated when the reactor core is under 100 Celsius. Ensure cooling is adequate to achieve this. + SAY The reaction can be terminated when the reactor core is under 200 Celsius. Ensure cooling is adequate to achieve this. DELAY 50 SAY Finally. If your minimum input power ever starts to converge on the maximum, you are heading towards an emission. Rectify this immediately, or shut down the reactor safely. DELAY 50 @@ -328,7 +328,7 @@ DELAY 50 SAY Do your duty. This tape should be destroyed after use. Shield technology does not exist. Glory to Nanotrasen. NAME Burst Data - LANGUAGE /datum/language/eal + LANGUAGE /datum/language/machine DELAY 20 SAY START METADATA SAY RECORDED 5-25-0000 diff --git a/nsv13/code/modules/overmap/pdsr.dm b/nsv13/code/modules/overmap/pdsr.dm index 36ee7c4b56f..6c52dcb62d7 100644 --- a/nsv13/code/modules/overmap/pdsr.dm +++ b/nsv13/code/modules/overmap/pdsr.dm @@ -9,8 +9,11 @@ #define REACTOR_STATE_SHUTTING_DOWN 4 #define REACTOR_STATE_EMISSION 5 +#define DENSITY_LOW 0 //! Deflects only heavy hits. +#define DENSITY_HIGH 1 //! Deflects all hits. + /obj/machinery/atmospherics/components/trinary/defence_screen_reactor - name = "mk I Prototype Defence Screen Reactor" + name = "mk II Prototype Defence Screen Reactor" desc = "A highly experimental, unstable and highly illegal nucleium driven reactor for the generation of defensive screens." icon = 'nsv13/icons/obj/machinery/pdsr.dmi' icon_state = "idle" @@ -46,10 +49,20 @@ var/last_coolant_time = 0 //Last time we called to flush coolant var/flushing_coolant = 0 //Are we currently flushing coolant var/emission_tracker = 0 //Used to track emission timers + ///Time when our reactor was last shutdown. + var/powerdown_time = 0 + ///If this is already detonating + var/detonating = FALSE //!Shield Vars - var/list/shield = list("integrity" = 0, "max_integrity" = 0, "stability" = 0) + var/list/shield = list("integrity" = 0, "max_integrity" = 0, "stability" = 0, "density" = DENSITY_HIGH) var/power_input = 0 //How much power is currently allocated + ///Did we get enough power last power tick? + var/power_demand_met = FALSE + ///How much power did we use during the power tick? + var/last_power_use = 0 + ///How much power was in the grid during power tick? + var/last_avail_power = 0 var/screen_regen = 50 //Allocation to regenerate the !shields var/screen_hardening = 50 //Allocation to strengthen the !shields var/min_power_input = 0 //Minimum power required to sustain !shield integrity @@ -95,17 +108,28 @@ /obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/try_use_power(amount) var/turf/T = get_turf(src) C = T.get_cable_node() - if(C?.surplus() > amount) + if(C?.surplus() >= amount) C.powernet.load += amount + last_power_use = amount + power_demand_met = TRUE + last_avail_power = C?.surplus() return TRUE + power_demand_met = FALSE + last_power_use = 0 + last_avail_power = C?.surplus() return FALSE -/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/process() +/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/process_atmos() update_parents() - if(next_slowprocess < world.time) + if(next_slowprocess <= world.time) slowprocess() next_slowprocess = world.time + 1 SECONDS +/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/process() + if(state == REACTOR_STATE_IDLE) + return + try_use_power(power_input) + /obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/slowprocess() var/datum/gas_mixture/nucleium_input = airs[2] var/datum/gas_mixture/coolant_input = airs[1] @@ -124,31 +148,32 @@ return current_uptime ++ - reaction_containment += 5 + reaction_containment += 20 // ~5 seconds as opposed to 20 for core start. if(reaction_containment >= 100) reaction_containment = 100 if(reaction_injection_rate < 2.5) say("Error: Unable to initialise reaction, insufficient nucleium injection.") - reaction_containment = 0 - current_uptime = 0 - state = REACTOR_STATE_IDLE + handle_shutdown() return if(nuc_in < reaction_injection_rate) say("Error: Unable to initialise reaction, insufficient nucleium available.") - reaction_containment = 0 - current_uptime = 0 - state = REACTOR_STATE_IDLE + handle_shutdown() + return + if(!power_demand_met) + say("Error: Power allocation exceeding grid capacity. Failed to initiate reaction.") + handle_shutdown() return - var/errors = rand(20, 200) + var/errors = rand(20, 199) say("Initiating Reaction - Injecting Nucleium.") say("Reaction Initialized - [errors] runtimes supressed.") reaction_temperature = 100 //Flash start to 100 + shield["stability"] = 50 //begin at 50 during startup. state = REACTOR_STATE_RUNNING if(state == REACTOR_STATE_RUNNING) - if(nuc_in >= reaction_injection_rate) //If we are running in nominal conditions... + if(nuc_in >= reaction_injection_rate && reaction_injection_rate >= 2.5) //If we are running in nominal conditions... nucleium_input.adjust_moles(GAS_NUCLEIUM, -reaction_injection_rate) //Handle reaction rate adjustments here var/target_reaction_rate = ((0.5 + (1e-03 * (reaction_injection_rate ** 2))) + (current_uptime / 2000)) * 16 @@ -157,7 +182,7 @@ reaction_temperature += reaction_rate * 0.35 //Function goes handle_polarity(TRUE) - else if(nuc_in < reaction_injection_rate) //If we are running without sufficient nucleium... + else //If we are running without sufficient nucleium... if(nuc_in <= 0) //...and none at all var/target_reaction_rate = 0 var/delta_reaction_rate = target_reaction_rate - reaction_rate @@ -176,7 +201,7 @@ handle_polarity(TRUE) if(reaction_rate > 5) //TEMP USE FUNCTIONS - reaction_energy_output = (reaction_rate + (reaction_injection_rate / 2)) * (2 - (current_uptime / 20000)) //FUNCTIONS + reaction_energy_output = (reaction_rate + (min(nuc_in, reaction_injection_rate) / 2)) * (2 - (current_uptime / 20000)) //FUNCTIONS radiation_pulse(src, reaction_energy_output) else @@ -205,21 +230,21 @@ state = REACTOR_STATE_SHUTTING_DOWN else say("Error: Reaction Prematurely Terminated - Inspect all systems for damage.") - state = REACTOR_STATE_IDLE + var/list/overload_candidate = list() + for(var/obj/machinery/defence_screen_relay/DSR in GLOB.machines) + if(DSR.powered() && DSR.overloaded == FALSE) + overload_candidate += DSR for(var/I = 0, I < 3, I++) //Overload Three Relays - var/list/overload_candidate = list() - for(var/obj/machinery/defence_screen_relay/DSR in GLOB.machines) - if(DSR.powered() && DSR.overloaded == FALSE) - overload_candidate += DSR - if(overload_candidate.len > 0) - var/obj/machinery/defence_screen_relay/DSRC = pick(overload_candidate) - DSRC.overload() + if(overload_candidate.len > 0) + var/obj/machinery/defence_screen_relay/DSRC = pick_n_take(overload_candidate) + DSRC.overload() depower_shield() OM.take_quadrant_hit(rand(100, 200), "forward_port") OM.take_quadrant_hit(rand(100, 200), "forward_starboard") OM.take_quadrant_hit(rand(100, 200), "aft_port") OM.take_quadrant_hit(rand(100, 200), "aft_starboard") + state = REACTOR_STATE_SHUTTING_DOWN handle_screens() handle_temperature() @@ -283,22 +308,23 @@ //////Reactor Procs////// /obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_containment() //We manage poweruse and containment here - if(try_use_power(power_input)) - if(power_input > max_power_input && power_input <= 1.25 * max_power_input) //Overloading Containment - Rapid Rise + if(power_demand_met) + if(last_power_use > max_power_input && last_power_use <= 1.25 * max_power_input) //Overloading Containment - Rapid Rise var/overloading_containment = reaction_containment if(overloading_containment < 25) overloading_containment = 25 var/overloading_function = ((382 * NUM_E **(0.0764 * overloading_containment)) / ((50 + NUM_E ** (0.0764 * overloading_containment)) ** 2)) * 14 - reaction_containment += overloading_function * (power_input / max_power_input) + reaction_containment += overloading_function * (last_power_use / max_power_input) current_uptime ++ //Overloading has a cost - else if(power_input >= min_power_input && power_input <= max_power_input) //Nominal Containment - Maintain Containment + else if(last_power_use >= min_power_input && last_power_use <= max_power_input) //Nominal Containment - Maintain Containment var/containment_function = ((382 * NUM_E **(0.0764 * reaction_containment)) / ((50 + NUM_E ** (0.0764 * reaction_containment)) ** 2)) * 10 - reaction_containment += containment_function * (power_input / max_power_input) + reaction_containment += containment_function * (last_power_use / max_power_input) - else if(power_input < min_power_input && power_input >= 0.75 * min_power_input) //Insufficient Power for Containment - Slow Loss + else if(last_power_use < min_power_input && last_power_use >= 0.75 * min_power_input) //Insufficient Power for Containment - Slow Loss var/loss_function = ((382 * NUM_E **(0.0764 * reaction_containment)) / ((50 + NUM_E ** (0.0764 * reaction_containment)) ** 2)) * 4 - reaction_containment += loss_function * (power_input / max_power_input) + reaction_containment += loss_function * (last_power_use / max_power_input) + if(reaction_containment > 100) reaction_containment = 100 @@ -307,7 +333,7 @@ reaction_containment = 0 emission_tracker = world.time say("Error: Catatstropic Containment Failure - Initializing Emergency Termination Protocols") - playsound(src, 'sound/magic/lightning_chargeup.ogg', 100, 0, 15, 10, 10) //Replace me later? + playsound(src, 'sound/magic/lightning_chargeup.ogg', 100, FALSE, 15) //Replace me later? state = REACTOR_STATE_EMISSION //Whoops /obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_power_reqs() //How much power is required @@ -320,7 +346,7 @@ if(reaction_containment < 33 && state == REACTOR_STATE_RUNNING) if(next_alarm_sfx < world.time) next_alarm_sfx = world.time + 3 SECONDS - playsound(src, 'nsv13/sound/effects/ship/pdsr_warning.ogg', 100, 0, 10, 10) + playsound(src, 'nsv13/sound/effects/ship/pdsr_warning.ogg', 100, FALSE, 10) if(next_alarm_message < world.time) next_alarm_message = world.time + 15 SECONDS say("DANGER: Reaction Containment Critical. Emission Imminent.") @@ -328,7 +354,7 @@ if(state == REACTOR_STATE_EMISSION) if(next_alarm_sfx < world.time) next_alarm_sfx = world.time + 3 SECONDS - playsound(src, 'nsv13/sound/effects/ship/pdsr_warning.ogg', 100, 0, 10, 10) + playsound(src, 'nsv13/sound/effects/ship/pdsr_warning.ogg', 100, FALSE, 10) /obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_polarity(var/injecting = FALSE) if(reaction_polarity_timer < world.time) //Handle the trend @@ -343,11 +369,7 @@ else reaction_polarity -= 0.027 - if(reaction_polarity > 1) - reaction_polarity = 1 - - if(reaction_polarity < -1) - reaction_polarity = -1 + reaction_polarity = clamp(reaction_polarity, -1, 1) var/polarity_function = abs(0.5 * (reaction_polarity ** 2)) //RECHECK THIS WHEN NOT DEAD reaction_containment -= polarity_function @@ -360,7 +382,7 @@ if(in_view_range(M, src)) to_chat(M, "A stream of particles erupts from the [src]!") M.flash_act(1, 0, 1) - playsound(src, 'sound/magic/repulse.ogg', 100, 0, 5, 5) + playsound(src, 'sound/magic/repulse.ogg', 100, FALSE, 5) //more goes here /obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_emission_release() @@ -368,13 +390,13 @@ if(DSR.powered() && DSR.overloaded == FALSE) DSR.overload() - var/emission_energy = reaction_energy_output * (1 + (current_uptime / 1000)) + var/emission_energy = max(10, reaction_energy_output) * (1 + (current_uptime / 1000)) radiation_pulse(src, emission_energy ** 2, 10, 1, 1) for(var/mob/living/M in OM.mobs_in_ship) if(M.client) - M.flash_act((emission_energy / 10), 0 , 1) - M.Knockdown(emission_energy / 10) + M.flash_act(clamp(emission_energy, 30, 100), TRUE, TRUE) + M.Knockdown(clamp(emission_energy, 20, 100)) M.adjust_disgust(rand(20, 50)) to_chat(M, "A wash of radiation passes through you!") @@ -390,6 +412,7 @@ say("Inspect all systems for damage.") /obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_temperature() + reaction_containment -= (reaction_temperature / 50) + (current_uptime / 2000) var/turf/open/L = get_turf(src) if(!istype(L) || !(L.air)) return @@ -401,7 +424,6 @@ env.set_temperature(temperature += delta_env / 2) air_update_turf() - reaction_containment -= (reaction_temperature / 50) + (current_uptime / 2000) /obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_atmos_check() for(var/obj/machinery/defence_screen_relay/DSR in GLOB.machines) @@ -418,6 +440,10 @@ flushing_coolant = 0 reaction_energy_output = 0 emission_tracker = 0 + powerdown_time = world.time + last_power_use = 0 + power_demand_met = FALSE + last_avail_power = 0 depower_shield() /obj/machinery/atmospherics/components/trinary/defence_screen_reactor/update_icon() @@ -442,22 +468,28 @@ //////Shield Procs////// -/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/absorb_hit(damage) +/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/absorb_hit(obj/item/projectile/proj) + var/damage = proj.damage if(!active) - return FALSE //!shields not raised + return SHIELD_NOEFFECT //!shields not raised + + if(shield["density"] == DENSITY_LOW && (proj.flag != "overmap_heavy" || proj.damage_type == BURN)) //Low density mode does not get hit by low impact projectiles, but also does not help vs. energy weapons. + return SHIELD_NOEFFECT if(shield["integrity"] >= damage) shield["integrity"] -= damage //Deduct from !shield var/current_hit = world.time - if(current_hit <= last_hit + 10) //1 Second - shield["stability"] -= rand((damage / 10), (damage / 5)) //Rapid hits will reduce stability greatly + if(current_hit <= last_hit + 1 SECONDS) //1 Second + shield["stability"] -= min(30, rand((damage / 10), (damage / 5))) //Rapid hits will reduce stability greatly else - shield["stability"] -= rand((damage / 50), (damage / 25)) //Reduce !shield stability + shield["stability"] -= min(10, rand((damage / 50), (damage / 25))) //Reduce !shield stability last_hit = current_hit //Set our last hit check_stability() - return TRUE + return SHIELD_FORCE_DEFLECT + + return SHIELD_NOEFFECT /obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/check_stability() if(shield["stability"] <= 0) @@ -483,12 +515,16 @@ active = TRUE //Renable !shields else if(active) - shield["stability"] += power_input / ((max_power_input * 1.5) - max(min_power_input, 0)) - if(shield["stability"] > 100) - shield["stability"] = 100 var/hardening_allocation = max(((screen_hardening / 100) * reaction_energy_output), 0) shield["max_integrity"] = hardening_allocation * (connected_relays * 10) //Each relay is worth 10 base var/regen_allocation = max(((screen_regen / 100) * reaction_energy_output), 0) + + var/stability_recovery = last_power_use / ((max_power_input * 1.5) - max(min_power_input, 0)) + if(screen_regen == 100) //Stopping field emission entirely helps with stabilization. + stability_recovery *= 5 + shield["stability"] += stability_recovery + if(shield["stability"] > 100) + shield["stability"] = 100 shield["integrity"] += regen_allocation if(shield["integrity"] > shield["max_integrity"]) shield["integrity"] = shield["max_integrity"] @@ -507,10 +543,26 @@ shield["stability"] = 0 active = FALSE +/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/ex_act(severity, target) + if(CHECK_BITFIELD(resistance_flags, INDESTRUCTIBLE)) + return + if(QDELETED(src)) + return + if(detonating) + return + detonating = TRUE + visible_message("[src] destabilizes violently.") + radiation_pulse(src, 5000) + explosion(get_turf(src), 5, 8, 0, 10, ignorecap = TRUE, flame_range = 10) + qdel(src) + + + + //////MAINFRAME CONSOLE////// /obj/machinery/computer/ship/defence_screen_mainframe_reactor //For controlling the reactor - name = "mk I Prototype Defence Screen Mainframe" + name = "mk II Prototype Defence Screen Mainframe" desc = "The mainframe controller for the PDSR" icon_screen = "idhos" //temp req_access = list(ACCESS_ENGINE) @@ -536,12 +588,12 @@ /obj/machinery/computer/ship/defence_screen_mainframe_reactor/attack_hand(mob/user) if(!allowed(user)) var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg') - playsound(src, sound, 100, 1) + playsound(src, sound, 100, TRUE) to_chat(user, "Access denied") return if(!reactor) var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg') - playsound(src, sound, 100, 1) + playsound(src, sound, 100, TRUE) to_chat(user, "Unable to detect linked reactor") return @@ -551,7 +603,7 @@ . = ..() if(!reactor) var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg') - playsound(src, sound, 100, 1) + playsound(src, sound, 100, TRUE) to_chat(user, "Unable to detect linked reactor") return ui_interact(user) @@ -560,7 +612,7 @@ . = ..() if(!reactor) var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg') - playsound(src, sound, 100, 1) + playsound(src, sound, 100, TRUE) to_chat(user, "Unable to detect linked reactor") return ui_interact(user) @@ -591,12 +643,8 @@ return var/adjust = text2num(params["adjust"]) if(action == "injection_allocation") - if(adjust && isnum(adjust)) - reactor.reaction_injection_rate = adjust - if(reactor.reaction_injection_rate > 25) - reactor.reaction_injection_rate = 25 - if(reactor.reaction_injection_rate < 0) - reactor.reaction_injection_rate = 0 + if(isnum(adjust)) + reactor.reaction_injection_rate = clamp(adjust, 0, 25) switch(action) if("polarity") @@ -604,6 +652,9 @@ return if("ignition") + if(world.time < reactor.powerdown_time + 30 SECONDS) + reactor.say("Realigning Particle Emitter - Field Unavailable.") + return if(reactor.state == REACTOR_STATE_IDLE) if(reactor.power_input >= reactor.min_power_input) reactor.say("Initiating Reaction - Charging Containment Field") @@ -633,6 +684,7 @@ reactor.say("Error: Unable to comply - Reactor parameters outside safe shutdown limits") return + /obj/machinery/computer/ship/defence_screen_mainframe_reactor/ui_data(mob/user) var/list/data = list() data["r_temp"] = reactor.reaction_temperature @@ -654,13 +706,13 @@ return data /obj/item/circuitboard/computer/defence_screen_mainframe_reactor - name = "mk I Prototype Defence Screen Mainframe (Computer Board)" + name = "mk II Prototype Defence Screen Mainframe (Computer Board)" build_path = /obj/machinery/computer/ship/defence_screen_mainframe_reactor //////SCREEN MANIPULATOR////// /obj/machinery/computer/ship/defence_screen_mainframe_shield //For controlling the !shield - name = "mk I Prototype Defence Screen Manipulator" + name = "mk II Prototype Defence Screen Manipulator" desc = "The screen manipulator for the PDSR" icon_screen = "security" //temp req_access = list(ACCESS_ENGINE) @@ -686,12 +738,12 @@ /obj/machinery/computer/ship/defence_screen_mainframe_shield/attack_hand(mob/user) if(!allowed(user)) var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg') - playsound(src, sound, 100, 1) + playsound(src, sound, 100, TRUE) to_chat(user, "Access denied") return if(!reactor) var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg') - playsound(src, sound, 100, 1) + playsound(src, sound, 100, TRUE) to_chat(user, "Unable to detect linked reactor") return @@ -701,7 +753,7 @@ . = ..() if(!reactor) var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg') - playsound(src, sound, 100, 1) + playsound(src, sound, 100, TRUE) to_chat(user, "Unable to detect linked reactor") return ui_interact(user) @@ -710,7 +762,7 @@ . = ..() if(!reactor) var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg') - playsound(src, sound, 100, 1) + playsound(src, sound, 100, TRUE) to_chat(user, "Unable to detect linked reactor") return ui_interact(user) @@ -764,12 +816,7 @@ reactor.adjust_tracker = world.time if("power_allocation") - reactor.power_input = adjust - if(reactor.power_input > (reactor.max_power_input * 1.25)) - reactor.power_input = reactor.max_power_input * 1.25 - - if(reactor.power_input < 0) - reactor.power_input = 0 + reactor.power_input = clamp(adjust, 0, reactor.max_power_input * 1.25) if(reactor.state == REACTOR_STATE_RUNNING && reactor.active) if(world.time >= (reactor.adjust_tracker + 1 SECONDS)) @@ -777,9 +824,19 @@ reactor.check_stability() reactor.adjust_tracker = world.time + if("density") + reactor.shield["density"] = !(reactor.shield["density"]) + if(reactor.state == REACTOR_STATE_RUNNING && reactor.active) + if(world.time >= (reactor.adjust_tracker + 1 SECONDS)) + reactor.shield["stability"] -= rand(5, 10) + reactor.check_stability() + reactor.adjust_tracker = world.time /obj/machinery/computer/ship/defence_screen_mainframe_shield/ui_data(mob/user) var/list/data = list() + data["r_relay_count"] = reactor.connected_relays + data["r_has_enough_power"] = reactor.power_demand_met + data["r_temp"] = reactor.reaction_temperature data["r_power_input"] = reactor.power_input data["r_min_power_input"] = reactor.min_power_input data["r_max_power_input"] = reactor.max_power_input @@ -789,13 +846,19 @@ data["s_integrity"] = reactor.shield["integrity"] data["s_max_integrity"] = reactor.shield["max_integrity"] data["s_stability"] = reactor.shield["stability"] + data["s_density"] = reactor.shield["density"] data["records"] = reactor.records data["available_power"] = 0 var/turf/T = get_turf(reactor) reactor.C = T.get_cable_node() - if(reactor.C) - if(reactor.C.powernet) - data["available_power"] = reactor.C.powernet.avail-reactor.C.powernet.load + + if(reactor.last_power_use || reactor.last_avail_power) + data["available_power"] = reactor.last_avail_power + else if(reactor.C) + data["available_power"] = reactor.C.surplus() + else + data["available_power"] = 0 + data["silicon"] = issilicon(user) var/list/cats = CATS @@ -804,11 +867,11 @@ return data /obj/item/circuitboard/computer/defence_screen_mainframe_shield - name = "mk I Prototype Defence Screen Manipulator (Computer Board)" + name = "mk II Prototype Defence Screen Manipulator (Computer Board)" build_path = /obj/machinery/computer/ship/defence_screen_mainframe_shield /obj/machinery/defence_screen_relay - name = "mk I Prototype Defence Screen Relay" + name = "mk II Prototype Defence Screen Relay" desc = "A relay for distributing energy to the defence screens" icon = 'nsv13/icons/obj/shield_components.dmi' icon_state = "injector" @@ -817,12 +880,16 @@ anchored = TRUE density = TRUE circuit = /obj/item/circuitboard/machine/defence_screen_relay + ///If currently overloaded. Needs wires and welding to fix. var/overloaded = FALSE + ///If this has suffered critical damage. Needs plasteel & followup repairs. + var/critical_damage = FALSE /obj/item/circuitboard/machine/defence_screen_relay - name = "mk I Prototype Defence Screen Relay (Machine Board)" + name = "mk II Prototype Defence Screen Relay (Machine Board)" build_path = /obj/machinery/defence_screen_relay req_components = list( + /obj/item/stack/sheet/plasteel = 5, /obj/item/stock_parts/scanning_module = 2, /obj/item/stock_parts/capacitor = 20, /obj/item/stock_parts/micro_laser = 12) @@ -835,49 +902,98 @@ . = ..() if(issilicon(user)) . += "Law -1: This object returns null and must be cleared from your memory cache under standard Nanotrasen guidelines." + else + if(critical_damage) + . += "Its protective housing is almost unrecognizable.. Maybe you could jury-rig a fix with some plasteel?" + else if(overloaded) + . += "Its wiring has seen better days.." /obj/machinery/defence_screen_relay/proc/overload() if(!overloaded) overloaded = TRUE do_sparks(4, FALSE, src) src.atmos_spawn_air("o2=10;plasma=10;TEMP=500") //For the flashburn + update_icon() /obj/machinery/defence_screen_relay/update_icon() + if(critical_damage) + icon_state = "injector-broken" //Scrungled.. + return if(overloaded) icon_state = "injector-damaged" return - if(!overloaded && powered()) + if(powered()) icon_state = "injector-on" return - if(!overloaded && !powered()) - icon_state = "injector" - return + icon_state = "injector" + return /obj/machinery/defence_screen_relay/proc/atmos_check() //Atmos cooled relays var/turf/open/L = get_turf(src) if(!istype(L) || !(L.air)) return var/datum/gas_mixture/E = L.return_air() - if(E.total_moles() < 20 || E.return_pressure() < 80) + if(E.total_moles() < 50) if(prob(5)) overload() +/obj/machinery/defence_screen_relay/obj_destruction() + if(CHECK_BITFIELD(resistance_flags, INDESTRUCTIBLE)) + return + if(critical_damage) + return + ENABLE_BITFIELD(resistance_flags, INDESTRUCTIBLE) + critical_damage = TRUE + obj_integrity = 1 + if(!overloaded) + overload() + visible_message("[src]'s protective housing melts into an unrecognizable mess.") + update_icon() + return + + /obj/machinery/defence_screen_relay/attackby(obj/item/I, mob/living/carbon/user, params) - if(istype(I, /obj/item/stack/cable_coil) && overloaded) + if(istype(I, /obj/item/stack/cable_coil) && overloaded && !critical_damage) var/obj/item/stack/cable_coil/C = I if(C.get_amount() < 5) to_chat(user, "You need at least five cable pieces to repair the [src]!") return - else - to_chat(user, "You start rewiring the [src]...") - if(!do_after(user, 5 SECONDS, target=src)) - return - C.use(5) - to_chat(user, "You rewire the [src].") - overloaded = FALSE + to_chat(user, "You start rewiring the [src]...") + if(!do_after(user, 5 SECONDS, target=src)) + return + if(!overloaded || critical_damage) + return + if(!C.use(5)) + return + to_chat(user, "You rewire the [src].") + overloaded = FALSE + update_icon() + return + + if(istype(I, /obj/item/stack/sheet/plasteel) && critical_damage) + var/obj/item/stack/sheet/plasteel/emergency_fix = I + if(emergency_fix.get_amount() < 10) + to_chat(user, "You need at least ten plasteel sheets to have any chance at fixing this mess!") + return + to_chat(user, "You start improvised housing repairs on [src]") + if(!do_after(user, 8 SECONDS, target=src)) + return + if(!critical_damage) + return + if(!emergency_fix.use(10)) + return + to_chat(user, "You repair [src]'s housing.. Hopefully that thing won't explode in your face.") + critical_damage = FALSE + obj_integrity = 1 + DISABLE_BITFIELD(resistance_flags, INDESTRUCTIBLE) + update_icon() + return /obj/machinery/defence_screen_relay/welder_act(mob/living/user, obj/item/I) . = ..() + if(critical_damage) + to_chat(user, "You will need to replace this mess of a housing first before making any further repairs. Maybe some plasteel would help?") + return while(obj_integrity < max_integrity) if(!do_after(user, 5, target = src)) return @@ -903,7 +1019,7 @@ //Anti Jeff mechanism if(!allowed(user)) var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg') - playsound(src, sound, 100, 1) + playsound(src, sound, 100, TRUE) visible_message("[icon2html(src, viewers(src.loc))] ACCESS DENIED.") return FALSE if(!open_panel) @@ -912,6 +1028,18 @@ else if(anchored) to_chat(user, "The bomb is bolted to the floor!") +//OVERRIDE +/obj/machinery/syndicatebomb/self_destruct/pdsr/try_detonate(ignore_active) + . = (payload in src) && (active || ignore_active) + if(.) + var/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/goodbye = locate() in (orange(10, get_turf(src))) + DISABLE_BITFIELD(goodbye.resistance_flags, INDESTRUCTIBLE) + payload.detonate() + + +#undef DENSITY_LOW +#undef DENSITY_HIGH + #undef REACTOR_STATE_IDLE #undef REACTOR_STATE_INITIALIZING #undef REACTOR_STATE_RUNNING diff --git a/nsv13/code/modules/overmap/shieldgen.dm b/nsv13/code/modules/overmap/shieldgen.dm index f3788c8eff8..d0cd127dd03 100644 --- a/nsv13/code/modules/overmap/shieldgen.dm +++ b/nsv13/code/modules/overmap/shieldgen.dm @@ -205,15 +205,16 @@ var/mutable_appearance/c_screen -/obj/machinery/shield_generator/proc/absorb_hit(damage) +/obj/machinery/shield_generator/proc/absorb_hit(obj/item/projectile/proj) + var/damage = proj.damage if(!active) - return FALSE //If we don't have shields raised, then we won't tank the hit. This allows you to micro the shields back to health. + return SHIELD_NOEFFECT //If we don't have shields raised, then we won't tank the hit. This allows you to micro the shields back to health. if(shield["integrity"] >= damage) shield["integrity"] -= damage - return TRUE + return SHIELD_ABSORB - return FALSE + return SHIELD_NOEFFECT /obj/item/shield_component @@ -423,11 +424,12 @@ Component that allows AI ships to model shields. Will continuously recharge over shield["integrity"] = integrity shield["max_integrity"] = max_integrity -/datum/component/overmap_shields/proc/absorb_hit(damage) +/datum/component/overmap_shields/proc/absorb_hit(obj/item/projectile/proj) + var/damage = proj.damage if(!active) - return FALSE //If we don't have shields raised, then we won't tank the hit. This allows you to micro the shields back to health. + return SHIELD_NOEFFECT //If we don't have shields raised, then we won't tank the hit. This allows you to micro the shields back to health. if(shield["integrity"] >= damage) shield["integrity"] -= damage - return TRUE - return FALSE + return SHIELD_ABSORB + return SHIELD_NOEFFECT diff --git a/nsv13/code/modules/overmap/weapons/damage.dm b/nsv13/code/modules/overmap/weapons/damage.dm index 1f93920e6ce..1bcd3fa46b1 100644 --- a/nsv13/code/modules/overmap/weapons/damage.dm +++ b/nsv13/code/modules/overmap/weapons/damage.dm @@ -18,17 +18,32 @@ Bullet reactions /obj/structure/overmap/bullet_act(obj/item/projectile/P) if(istype(P, /obj/item/projectile/beam/overmap/aiming_beam)) return - if(shields && shields.absorb_hit(P.damage)) - var/damage_sound = pick('nsv13/sound/effects/ship/damage/shield_hit.ogg', 'nsv13/sound/effects/ship/damage/shield_hit2.ogg') - if(!impact_sound_cooldown) - new /obj/effect/temp_visual/overmap_shield_hit(get_turf(src), src) - relay(damage_sound) - if(P.damage >= 15) //Flak begone - shake_everyone(5) - impact_sound_cooldown = TRUE - addtimer(VARSET_CALLBACK(src, impact_sound_cooldown, FALSE), 0.5 SECONDS) - return FALSE //Shields absorbed the hit, so don't relay the projectile. + if(shields) + var/shield_result = shields.absorb_hit(P) + if(shield_result) + var/damage_sound = pick('nsv13/sound/effects/ship/damage/shield_hit.ogg', 'nsv13/sound/effects/ship/damage/shield_hit2.ogg') + if(!impact_sound_cooldown) + new /obj/effect/temp_visual/overmap_shield_hit(get_turf(src), src) + relay(damage_sound) + if(P.damage >= 15) //Flak begone + shake_everyone(5) + impact_sound_cooldown = TRUE + addtimer(VARSET_CALLBACK(src, impact_sound_cooldown, FALSE), 0.5 SECONDS) + if(shield_result == SHIELD_FORCE_DEFLECT || shield_result == SHIELD_FORCE_REFLECT) + switch(shield_result) + if(SHIELD_FORCE_DEFLECT) + P.setAngle((P.Angle + rand(25, 50) + (prob(50) ? 0 : 285)) % 360) + if(SHIELD_FORCE_REFLECT) + P.setAngle((P.Angle + rand(160, 200)) % 360) + else + P.faction = null //We go off the rails. + P.homing_target = null + P.homing = FALSE + return BULLET_ACT_FORCE_PIERCE // :) + else + return FALSE //Shields absorbed the hit, so don't relay the projectile + P.spec_overmap_hit(src) var/relayed_type = P.relay_projectile_type ? P.relay_projectile_type : P.type relay_damage(relayed_type) if(!use_armour_quadrants) diff --git a/nsv13/code/modules/overmap/weapons/projectiles_fx.dm b/nsv13/code/modules/overmap/weapons/projectiles_fx.dm index b6fbc3221dc..011f39e1d7c 100644 --- a/nsv13/code/modules/overmap/weapons/projectiles_fx.dm +++ b/nsv13/code/modules/overmap/weapons/projectiles_fx.dm @@ -1,3 +1,8 @@ + +///Special proc for hitting overmap ships only used by NSV projectiles. +/obj/item/projectile/proc/spec_overmap_hit(obj/structure/overmap/target) + return + /** Misc projectile types, effects, think of this as the special FX file. @@ -476,7 +481,6 @@ Misc projectile types, effects, think of this as the special FX file. if(!check_faction(target)) return FALSE //Nsv13 - faction checking for overmaps. We're gonna just cut off real early and save some math if the IFF doesn't check out. if(isovermap(target)) //Were we to explode on an actual overmap, this would oneshot the ship as it's a powerful explosion. - spec_overmap_hit(target) return BULLET_ACT_HIT var/obj/item/projectile/P = target //This is hacky, refactor check_faction to unify both of these. I'm bodging it for now. if(isprojectile(target) && P.faction != faction && !P.nodamage) //Because we could be in the same faction and collide with another bullet. Let's not blow ourselves up ok? @@ -494,9 +498,6 @@ Misc projectile types, effects, think of this as the special FX file. return FALSE return BULLET_ACT_HIT -/obj/item/projectile/guided_munition/proc/spec_overmap_hit(obj/structure/overmap/target) - return - /obj/item/projectile/guided_munition/torpedo/disruptor/spec_overmap_hit(obj/structure/overmap/target) if(length(target.occupying_levels)) return //Detonate is gonna handle this for us. diff --git a/nsv13/icons/obj/shield_components.dmi b/nsv13/icons/obj/shield_components.dmi index e7d28d423cd403bca9fd2b36606be78c88311d01..4a9eea8108772f0a60a9cb9328cccbc05f98b911 100644 GIT binary patch literal 4340 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 diff --git a/tgui/packages/tgui/interfaces/FTLComputer.js b/tgui/packages/tgui/interfaces/FTLComputer.js index 6464f7cb455..fe9c3699f0e 100644 --- a/tgui/packages/tgui/interfaces/FTLComputer.js +++ b/tgui/packages/tgui/interfaces/FTLComputer.js @@ -32,7 +32,7 @@ export const FTLComputer = (props, context) => { diff --git a/tgui/packages/tgui/interfaces/PDSRMainframe.js b/tgui/packages/tgui/interfaces/PDSRMainframe.js index 2811748d054..ea6ac7aad09 100644 --- a/tgui/packages/tgui/interfaces/PDSRMainframe.js +++ b/tgui/packages/tgui/interfaces/PDSRMainframe.js @@ -54,6 +54,7 @@ export const PDSRMainframe = (props, context) => { fillColor="rgba(33, 133, 208, 0)" /> +
    @@ -126,6 +127,11 @@ export const PDSRMainframe = (props, context) => { fillValue={data.r_injection_rate} minValue={0} maxValue={25} + ranges={{ + default: [5, 20], + yellow: [2.5, Infinity], + bad: [-Infinity, 2.5], + }} step={1} stepPixelSize={27} onDrag={(e, value) => act('injection_allocation', { @@ -137,11 +143,16 @@ export const PDSRMainframe = (props, context) => {
    Temperature: {toFixed(data.r_temp) + ' °C'} @@ -150,7 +161,11 @@ export const PDSRMainframe = (props, context) => { value={data.r_reaction_rate} minValue={0} maxValue={25} - color="teal" > + color={data.r_temp === 0 ? "default" : null} + ranges={{ + bad: [-Infinity, 5], + teal: [5, Infinity], + }}> {data.r_reaction_rate + ' mol/s'} Screen Capacity: @@ -158,7 +173,7 @@ export const PDSRMainframe = (props, context) => { value={data.r_energy_output} minValue={0} maxValue={50} - color="yellow" > + color={(data.r_energy_output === 0 && data.r_temp > 0) ? "red" : "yellow"} > {data.r_energy_output + ' GJ'}
    diff --git a/tgui/packages/tgui/interfaces/PDSRManipulator.js b/tgui/packages/tgui/interfaces/PDSRManipulator.js index b403dc7d94b..6804b95fe13 100644 --- a/tgui/packages/tgui/interfaces/PDSRManipulator.js +++ b/tgui/packages/tgui/interfaces/PDSRManipulator.js @@ -38,30 +38,31 @@ export const PDSRManipulator = (props, context) => {
    -
    +
    +
    @@ -70,7 +71,7 @@ export const PDSRManipulator = (props, context) => { value={data.available_power} minValue={0} maxValue={data.r_max_power_input * 1.25} - color="yellow"> + color={(data.r_temp !== 0 && !data.r_has_enough_power) ? "bad" : "yellow"}> {data.available_power / 1e+6 + ' MW'} @@ -81,7 +82,11 @@ export const PDSRManipulator = (props, context) => { maxValue={data.r_max_power_input * 1.25} step={1} stepPixelSize={0.000004} - color="white" + ranges={{ + white: [data.r_min_power_input, data.r_max_power_input], + yellow: [data.r_max_power_input, data.r_max_power_input * 1.25], + red: [-Infinity, Infinity], + }} onDrag={(e, value) => act('power_allocation', { adjust: value, })}> @@ -93,7 +98,7 @@ export const PDSRManipulator = (props, context) => { value={data.r_max_power_input} minValue={0} maxValue={data.r_max_power_input} - color="teal"> + color={data.r_relay_count === 0 ? "bad" : "teal"}> {data.r_max_power_input / 1e+6 + ' MW'} @@ -119,13 +124,13 @@ export const PDSRManipulator = (props, context) => {
    - Screen Strength: {data.s_integrity} + Screen Strength: {data.s_integrity + ' | ' + data.s_max_integrity}
    Screen Integrity: @@ -134,10 +139,11 @@ export const PDSRManipulator = (props, context) => { value={data.s_stability} minValue={0} maxValue={100} - range={{ - good: [], - average: [0.33, 0.65], - bad: [-Infinity, 0.33], + color={(data.s_regen === 100 ? "blue" : null) || (data.r_temp === 0 ? "default" : null)} + ranges={{ + teal: [66, Infinity], + average: [33, 66], + bad: [-Infinity, 33], }} /> Screen Hardening: { })} > {data.s_regen + ' %'} + Screen Particle Density: +
    From 02acc76dc06167ff901bffd3a032495ab9d66cb7 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:53:15 -0700 Subject: [PATCH 13/36] Automatic changelog generation for PR #2579 [ci skip] --- html/changelogs/AutoChangeLog-pr-2579.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2579.yml diff --git a/html/changelogs/AutoChangeLog-pr-2579.yml b/html/changelogs/AutoChangeLog-pr-2579.yml new file mode 100644 index 00000000000..08fa308e0f2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2579.yml @@ -0,0 +1,5 @@ +author: DeltaFire15 +delete-after: true +changes: + - tweak: Some small Galactica changes. Don't worry about it. + - bugfix: Fixes a random weird thing with explosions I stumbled upon. From 8b4b991d3ec450af1baf979cc3616824b6e6ab36 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 1 Dec 2023 21:03:49 +0000 Subject: [PATCH 14/36] Automatic changelog compile [ci skip] --- html/changelog.html | 16 ++++++++++++++++ html/changelogs/.all_changelog.yml | 9 +++++++++ html/changelogs/AutoChangeLog-pr-2411.yml | 5 ----- html/changelogs/AutoChangeLog-pr-2574.yml | 4 ---- html/changelogs/AutoChangeLog-pr-2579.yml | 5 ----- 5 files changed, 25 insertions(+), 14 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-2411.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-2574.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-2579.yml diff --git a/html/changelog.html b/html/changelog.html index 04fc26131ac..f8fd28b6e1f 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,22 @@ -->
    +

    01 December 2023

    +

    Bokkiewokkie updated:

    +
      +
    • Added munitions shirts, tank tops and socks
    • +
    +

    DeltaFire15 updated:

    +
      +
    • Some small Galactica changes. Don't worry about it.
    • +
    • Fixes a random weird thing with explosions I stumbled upon.
    • +
    +

    covertcorvid updated:

    +
      +
    • Made repo config client version match the server
    • +
    • Increased max recommended client version to 515.1608
    • +
    +

    24 November 2023

    Bokkiewokkie updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 7d125e65150..20dcd9588c9 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -1819,3 +1819,12 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Bokkiewokkie: - rscadd: Added Syndicate light fighter plushie - rscadd: Added DIY fighter plush bomb kit traitor item +2023-12-01: + Bokkiewokkie: + - imageadd: Added munitions shirts, tank tops and socks + DeltaFire15: + - tweak: Some small Galactica changes. Don't worry about it. + - bugfix: Fixes a random weird thing with explosions I stumbled upon. + covertcorvid: + - config: Made repo config client version match the server + - server: Increased max recommended client version to 515.1608 diff --git a/html/changelogs/AutoChangeLog-pr-2411.yml b/html/changelogs/AutoChangeLog-pr-2411.yml deleted file mode 100644 index 248479918ee..00000000000 --- a/html/changelogs/AutoChangeLog-pr-2411.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: covertcorvid -delete-after: true -changes: - - config: Made repo config client version match the server - - server: Increased max recommended client version to 515.1608 diff --git a/html/changelogs/AutoChangeLog-pr-2574.yml b/html/changelogs/AutoChangeLog-pr-2574.yml deleted file mode 100644 index d91663779e1..00000000000 --- a/html/changelogs/AutoChangeLog-pr-2574.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Bokkiewokkie -delete-after: true -changes: - - imageadd: Added munitions shirts, tank tops and socks diff --git a/html/changelogs/AutoChangeLog-pr-2579.yml b/html/changelogs/AutoChangeLog-pr-2579.yml deleted file mode 100644 index 08fa308e0f2..00000000000 --- a/html/changelogs/AutoChangeLog-pr-2579.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: DeltaFire15 -delete-after: true -changes: - - tweak: Some small Galactica changes. Don't worry about it. - - bugfix: Fixes a random weird thing with explosions I stumbled upon. From 09fb633a0978999638bd0f55af14e014f60357ad Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 8 Dec 2023 00:13:59 +0000 Subject: [PATCH 15/36] Automatic changelog compile [ci skip] --- html/changelog.html | 8 -------- 1 file changed, 8 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index f8fd28b6e1f..8486fbfef3e 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -191,14 +191,6 @@

      someone543 updated:

    • fixed Kadesh cruisers from being boarded
    • borgs can now leftclick to enter transport small crafts
    - -

    06 October 2023

    -

    SerynEngi updated:

    -
      -
    • Added new nucleum preset engineering tile
    • -
    • Nucleum tank to the Gladius
    • -
    • Added nucleum gas to the nucleum tanks on the Gladius, Galactica, Tycoon and Aetherwhisp
    • -
    GoonStation 13 Development Team From 51eadeef806d5f842ce2ecfc770e4d8771870a9f Mon Sep 17 00:00:00 2001 From: Bokkiewokkie <43698041+Bokkiewokkie@users.noreply.github.com> Date: Sat, 9 Dec 2023 00:57:17 +0100 Subject: [PATCH 16/36] 'Ports' PR labeler fix from Beestation (#2591) --- .github/workflows/extra_pr_labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/extra_pr_labels.yml b/.github/workflows/extra_pr_labels.yml index f42a69933e3..08c629edce5 100644 --- a/.github/workflows/extra_pr_labels.yml +++ b/.github/workflows/extra_pr_labels.yml @@ -19,7 +19,7 @@ jobs: commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request." - name: Apply labels based on changed files if: github.event_name != 'push' - uses: actions/labeler@main + uses: actions/labeler@v4.3.0 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: true From b3f463ae8648cb103f957c9177e9e90aa5577065 Mon Sep 17 00:00:00 2001 From: Bokkiewokkie <43698041+Bokkiewokkie@users.noreply.github.com> Date: Fri, 15 Dec 2023 23:13:10 +0100 Subject: [PATCH 17/36] Adds special offers to overmap traders (#2573) --- nsv13/code/modules/overmap/traders.dm | 69 ++++++++++++++++++--- nsv13/code/modules/overmap/traders_items.dm | 42 +++++++++++-- 2 files changed, 98 insertions(+), 13 deletions(-) diff --git a/nsv13/code/modules/overmap/traders.dm b/nsv13/code/modules/overmap/traders.dm index cfb11d01649..4fd02bda040 100644 --- a/nsv13/code/modules/overmap/traders.dm +++ b/nsv13/code/modules/overmap/traders.dm @@ -4,6 +4,7 @@ var/shortname = "DM" //Used in Brazil. var/list/stonks = list() //The trader's inventory. var/list/sold_items = list() + var/list/special_offers = list() //Items locked behind points var/faction_type = null //What faction does the dude belong to. var/system_type = "unaligned" //In what systems do they spawn? //Fluff / voice stuff @@ -48,11 +49,20 @@ yellow_pages_dat += "" for(var/itemPath in sold_items) var/datum/trader_item/TI = new itemPath() - TI.price = rand(TI.price/2, TI.price*4) - TI.stock = rand(TI.stock/2, TI.stock*2) //How much we got in stock boys + TI.price = round(rand(TI.price/2, TI.price*4)) + TI.stock = round(rand(TI.stock/2, TI.stock*2)) //How much we got in stock boys stonks += TI TI.owner = src yellow_pages_dat += "[TI.stock]x [TI.name] ([TI.price] ea.) - [TI.desc]

    " + for(var/itemPath in special_offers) + var/datum/trader_item/TI = new itemPath() + if(system_type == SSstar_system.find_main_overmap().faction && F.tickets >= TI.special_requirement) //Right now we use faction tickets to unlock better items + TI.name = "SPECIAL OFFER! " + TI.name //Advertising is very important + TI.price = round(rand((2*TI.price)/3, TI.price*2)) //These will be more expensive by default already and have less chaotic prices + TI.stock = round(rand(TI.stock/2, TI.stock*2)) + stonks += TI + TI.owner = src + yellow_pages_dat += "SPECIAL OFFER! [TI.stock]x [TI.name] ([TI.price] ea.) - [TI.desc]

    " yellow_pages_dat += "
    " /datum/trader_item @@ -62,6 +72,7 @@ var/unlock_path = null var/stock = 1 //How many of these items are usually stocked, this is randomized var/owner = null + var/special_requirement //How many tickets do we need to unlock this item in the store? /datum/trader_item/proc/on_purchase(obj/structure/overmap/OM) return OM.send_supplypod(unlock_path) @@ -138,7 +149,15 @@ faction_type = FACTION_ID_NT system_type = "nanotrasen" image = "https://cdn.discordapp.com/attachments/701841640897380434/764557336684527637/unknown.png" - sold_items = list(/datum/trader_item/torpedo, /datum/trader_item/missile, /datum/trader_item/c45, /datum/trader_item/pdc, /datum/trader_item/deck_gun_autorepair, /datum/trader_item/yellow_pages) + sold_items = list(/datum/trader_item/torpedo, \ + /datum/trader_item/missile, \ + /datum/trader_item/c45, \ + /datum/trader_item/pdc, \ + /datum/trader_item/pdc_circuit, \ + /datum/trader_item/deck_gun_autorepair, \ + /datum/trader_item/yellow_pages) + special_offers = list(/datum/trader_item/firing_electronics, \ + /datum/trader_item/vls_circuit) /datum/trader/armsdealer/syndicate name = "DonkCo Warcrime Emporium" @@ -147,8 +166,20 @@ faction_type = FACTION_ID_SYNDICATE system_type = "syndicate" //Top tier trader with the best items available. - sold_items = list(/datum/trader_item/hellfire,/datum/trader_item/torpedo, /datum/trader_item/missile, /datum/trader_item/c20r, /datum/trader_item/c45, /datum/trader_item/stechkin, \ - /datum/trader_item/pdc, /datum/trader_item/fighter/syndicate, /datum/trader_item/overmap_shields, /datum/trader_item/deck_gun_autoelevator, /datum/trader_item/yellow_pages) + sold_items = list(/datum/trader_item/hellfire, \ + /datum/trader_item/torpedo, \ + /datum/trader_item/missile, \ + /datum/trader_item/c20r, \ + /datum/trader_item/c45, \ + /datum/trader_item/stechkin, \ + /datum/trader_item/pdc, \ + /datum/trader_item/pdc_circuit, \ + /datum/trader_item/fighter/syndicate, \ + /datum/trader_item/overmap_shields, \ + /datum/trader_item/deck_gun_autoelevator, \ + /datum/trader_item/yellow_pages) + special_offers = list(/datum/trader_item/firing_electronics, \ + /datum/trader_item/vls_circuit) station_type = /obj/structure/overmap/trader/syndicate image = "https://cdn.discordapp.com/attachments/728055734159540244/764570187357093928/unknown.png" greetings = list("You've made it pretty far in, huh? We won't tell if you're buying...", "Freedom isn't free, buy a gun to secure yours.", "Excercise your right to bear arms now!") @@ -180,7 +211,13 @@ "CzanekCorp here. We got a new shipment in, you down for talking turkey?",\ "CzanekCorp, we got repairs and goods on a budget, you in?") on_purchase = list("Yes, we know the tazers aren't the safest, but if you don't like 'em, stop buying 'em, eh?", "Good doing business with you. Good luck out there, killer.", "About time we got somebody who knows what they're doing. Here, free shipping!", "No refunds, no returns!") - sold_items = list(/datum/trader_item/ship_repair,/datum/trader_item/fighter/light,/datum/trader_item/fighter/heavy,/datum/trader_item/fighter/utility, /datum/trader_item/taser, /datum/trader_item/taser_ammo, /datum/trader_item/yellow_pages) + sold_items = list(/datum/trader_item/ship_repair, \ + /datum/trader_item/fighter/light, \ + /datum/trader_item/fighter/heavy, \ + /datum/trader_item/fighter/utility, \ + /datum/trader_item/taser, \ + /datum/trader_item/taser_ammo, \ + /datum/trader_item/yellow_pages) station_type = /obj/structure/overmap/trader/shipyard image = "https://cdn.discordapp.com/attachments/701841640897380434/764540586732421120/unknown.png" @@ -193,7 +230,14 @@ "Have you come to dig or pay?",\ "We got minerals for you, so long as you've got a deposit for us.") on_purchase = list("Maybe next time, dig it up yourself lazy gits!", "Credits have been withdrawn, Supplies inbound.", "Czanek would approve of this.", "If you're too afraid to get these yourself, I'm almost scared to give them to you. But money is money.") - sold_items = list(/datum/trader_item/mining_point_card, /datum/trader_item/gold, /datum/trader_item/diamond, /datum/trader_item/uranium, /datum/trader_item/silver, /datum/trader_item/bluespace_crystal, /datum/trader_item/titanium, /datum/trader_item/yellow_pages) + sold_items = list(/datum/trader_item/mining_point_card, \ + /datum/trader_item/gold, \ + /datum/trader_item/diamond, \ + /datum/trader_item/uranium, \ + /datum/trader_item/silver, \ + /datum/trader_item/bluespace_crystal, \ + /datum/trader_item/titanium, \ + /datum/trader_item/yellow_pages) station_type = /obj/structure/overmap/trader image = "https://cdn.discordapp.com/attachments/612668662977134592/859132739147792444/unknown.png" //I don't wanna do this but I'm also not going to break the mold as to make it hopefully easier in future to fix. @@ -210,7 +254,16 @@ desc = "Corporate approved aftermarket shipyard." shortname = "MHE" faction_type = FACTION_ID_NT - sold_items = list(/datum/trader_item/ship_repair/tier2, /datum/trader_item/flak,/datum/trader_item/fighter/light,/datum/trader_item/fighter/heavy,/datum/trader_item/fighter/utility, /datum/trader_item/fighter/judgement, /datum/trader_item/fighter/prototype, /datum/trader_item/railgun_disk, /datum/trader_item/yellow_pages) + sold_items = list(/datum/trader_item/ship_repair/tier2, \ + /datum/trader_item/flak, \ + /datum/trader_item/fighter/light, \ + /datum/trader_item/fighter/heavy, \ + /datum/trader_item/fighter/utility, \ + /datum/trader_item/fighter/judgement, \ + /datum/trader_item/fighter/prototype, \ + /datum/trader_item/railgun_disk, \ + /datum/trader_item/yellow_pages) + special_offers = list(/datum/trader_item/ship_repair/tier3) station_type = /obj/structure/overmap/trader/shipyard // HIM diff --git a/nsv13/code/modules/overmap/traders_items.dm b/nsv13/code/modules/overmap/traders_items.dm index c61d148914d..4533db36d44 100644 --- a/nsv13/code/modules/overmap/traders_items.dm +++ b/nsv13/code/modules/overmap/traders_items.dm @@ -77,6 +77,15 @@ repair_amount = 50 stock = 2 +/datum/trader_item/ship_repair/tier3 + name = "Deluxe ship repair" + desc = "A complete repair job of all of your ship's hull and armour, because you've earned it!" + price = 2000 + failure_chance = 0 + repair_amount = 100 + stock = 1 + special_requirement = 400 //Prove those scars are worth the cost + /datum/trader_item/ship_repair/on_purchase(obj/structure/overmap/OM) OM.repair_all_quadrants(repair_amount, failure_chance) @@ -166,17 +175,40 @@ /obj/item/ship_weapon/parts/loading_tray,\ ) +/datum/trader_item/vls_circuit + name = "VLS tube circuit board" + desc = "The critical component for expanding your missile complement!" + price = 5000 + stock = 10 + unlock_path = /obj/item/circuitboard/machine/vls + special_requirement = 100 //At least put some effort into it + +/datum/trader_item/firing_electronics + name = "Firing Electronics" + desc = "Essential electronics for building most modern naval weapons." + price = 20000 + stock = 2 + unlock_path = /obj/item/ship_weapon/parts/firing_electronics + special_requirement = 500 //Kick their ass!!! + +/datum/trader_item/pdc_circuit + name = "PDC mount circuit board" + desc = "Not enough point defense? Just build more!" + price = 2500 + stock = 4 + unlock_path = /obj/item/circuitboard/machine/pdc_mount + /datum/trader_item/torpedo name = "Standard Torpedo" desc = "A standard torpedo for ship to ship combat." - price = 1000 - stock = 10 + price = 900 //Price is 1000 per in cargo + stock = 15 unlock_path = /obj/item/ship_weapon/ammunition/torpedo /datum/trader_item/missile name = "Standard Missile" desc = "A standard missile for ship to ship combat." - price = 500 + price = 500 //Price in cargo is 833 per, this is a real steal stock = 20 unlock_path = /obj/item/ship_weapon/ammunition/missile @@ -211,8 +243,8 @@ /datum/trader_item/pdc name = "PDC Ammo Box" desc = "PDC rounds for use in ship to ship guns." - price = 800 - stock = 10 + price = 175 //cost of buying in cargo is 200 per box + stock = 20 unlock_path = /obj/item/ammo_box/magazine/nsv/pdc /datum/trader_item/anti_air From 78189cb7fde6eb55cfe0ca48859fc5b13be1695c Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:14:31 -0700 Subject: [PATCH 18/36] Automatic changelog generation for PR #2573 [ci skip] --- html/changelogs/AutoChangeLog-pr-2573.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2573.yml diff --git a/html/changelogs/AutoChangeLog-pr-2573.yml b/html/changelogs/AutoChangeLog-pr-2573.yml new file mode 100644 index 00000000000..e02a644fd5a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2573.yml @@ -0,0 +1,6 @@ +author: Bokkiewokkie +delete-after: true +changes: + - rscadd: Added special offers to traders, which have to be unlocked with faction + ticket points + - balance: Made gun parts available for purchase at Whiterapids' Munitions From 0a5089be3977f345120fa0118ad4c9899952c2b8 Mon Sep 17 00:00:00 2001 From: Bokkiewokkie <43698041+Bokkiewokkie@users.noreply.github.com> Date: Fri, 15 Dec 2023 23:47:53 +0100 Subject: [PATCH 19/36] Update TGS DMAPI (#2593) Co-authored-by: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> --- code/__DEFINES/tgs.dm | 2 +- code/modules/tgs/v5/__interop_version.dm | 2 +- code/modules/tgs/v5/_defines.dm | 2 ++ code/modules/tgs/v5/topic.dm | 20 ++++++++++++++++---- code/modules/tgs/v5/undefs.dm | 4 ++++ 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index 0cc106ec9cf..b0e97e05e9b 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -1,6 +1,6 @@ // tgstation-server DMAPI -#define TGS_DMAPI_VERSION "6.6.2" +#define TGS_DMAPI_VERSION "6.7.0" // All functions and datums outside this document are subject to change with any version and should not be relied on. diff --git a/code/modules/tgs/v5/__interop_version.dm b/code/modules/tgs/v5/__interop_version.dm index 1b52b31d6a7..83420d130a7 100644 --- a/code/modules/tgs/v5/__interop_version.dm +++ b/code/modules/tgs/v5/__interop_version.dm @@ -1 +1 @@ -"5.6.2" +"5.7.0" diff --git a/code/modules/tgs/v5/_defines.dm b/code/modules/tgs/v5/_defines.dm index bdcd4e4dd58..48969c0c7d5 100644 --- a/code/modules/tgs/v5/_defines.dm +++ b/code/modules/tgs/v5/_defines.dm @@ -80,6 +80,7 @@ #define DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH 8 #define DMAPI5_TOPIC_COMMAND_SEND_CHUNK 9 #define DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK 10 +#define DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST 11 #define DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE "commandType" #define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand" @@ -89,6 +90,7 @@ #define DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME "newInstanceName" #define DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE "chatUpdate" #define DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION "newServerVersion" +#define DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE "broadcastMessage" #define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE "commandResponse" #define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE "commandResponseMessage" diff --git a/code/modules/tgs/v5/topic.dm b/code/modules/tgs/v5/topic.dm index d7d47121381..2ef0c70a97f 100644 --- a/code/modules/tgs/v5/topic.dm +++ b/code/modules/tgs/v5/topic.dm @@ -94,7 +94,7 @@ if(DMAPI5_TOPIC_COMMAND_CHANGE_PORT) var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT] if (!isnum(new_port) || !(new_port > 0)) - return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]") + return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]") if(event_handler != null) event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port) @@ -141,7 +141,7 @@ if(DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE) var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT] if (!isnum(new_port) || !(new_port > 0)) - return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]") + return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]") server_port = new_port return TopicResponse() @@ -157,7 +157,7 @@ var/error_message = null if (new_port != null) if (!isnum(new_port) || !(new_port > 0)) - error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]" + error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]" else server_port = new_port @@ -165,7 +165,7 @@ if (!istext(new_version_string)) if(error_message != null) error_message += ", " - error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]]" + error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]" else var/datum/tgs_version/new_version = new(new_version_string) if (event_handler) @@ -267,4 +267,16 @@ return chunk_to_send + if(DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST) + var/message = topic_parameters[DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE] + if (!istext(message)) + return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE]") + + TGS_WORLD_ANNOUNCE(message) + return TopicResponse() + return TopicResponse("Unknown command: [command]") + +/datum/tgs_api/v5/proc/WorldBroadcast(message) + set waitfor = FALSE + TGS_WORLD_ANNOUNCE(message) diff --git a/code/modules/tgs/v5/undefs.dm b/code/modules/tgs/v5/undefs.dm index f163adaaafe..fd1ed7e4cf5 100644 --- a/code/modules/tgs/v5/undefs.dm +++ b/code/modules/tgs/v5/undefs.dm @@ -78,6 +78,9 @@ #undef DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE #undef DMAPI5_TOPIC_COMMAND_HEALTHCHECK #undef DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH +#undef DMAPI5_TOPIC_COMMAND_SEND_CHUNK +#undef DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK +#undef DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST #undef DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE #undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND @@ -87,6 +90,7 @@ #undef DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME #undef DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE #undef DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION +#undef DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE #undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE #undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE From 240eb3dabe8ac6dac5d5f515b0bec7ce98bbf23b Mon Sep 17 00:00:00 2001 From: DeltaFire <46569814+DeltaFire15@users.noreply.github.com> Date: Fri, 15 Dec 2023 23:50:11 +0100 Subject: [PATCH 20/36] Species names are colored when examining / health analyzing (#2582) --- code/game/objects/items/devices/scanners.dm | 2 +- .../mob/living/carbon/human/examine.dm | 2 +- .../carbon/human/species_types/ethereal.dm | 1 + nsv13.dme | 1 + .../living/carbon/human/nsv_human_helpers.dm | 25 +++++++++++++++++++ .../tgui-panel/styles/goon/chat-dark.scss | 9 +++++++ .../tgui-panel/styles/goon/chat-light.scss | 9 +++++++ 7 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 nsv13/code/modules/mob/living/carbon/human/nsv_human_helpers.dm diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index fae0e73b471..6dddb203bc4 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -359,7 +359,7 @@ GENE SCANNER else if(S.mutantstomach != initial(S.mutantstomach)) mutant = TRUE - to_chat(user, "Species: [S.name][mutant ? "-derived mutant" : ""]") + to_chat(user, "Species: [S.name][mutant ? "-derived mutant" : ""]") //NSV13 - species name is colored depending on special conditions. to_chat(user, "Body temperature: [round(M.bodytemperature-T0C,0.1)] °C ([round(M.bodytemperature*1.8-459.67,0.1)] °F)") // Time of death diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 6d8e78bf809..061b5bf68d5 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -24,7 +24,7 @@ display_name += "[compose_rank(src)]" display_name += name if(dna?.species && !skipface) - apparent_species = ", \an [dna.species.name]" + apparent_species = ", \an [dna.species.name]" //NSV13 - species name is colored depending on special conditions. . = list("*---------*\nThis is [!obscure_name ? display_name : "Unknown"][apparent_species]!") //uniform diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index 3ff1c2a28a7..dea066c1f34 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -175,6 +175,7 @@ H.throw_alert("nutrition", /atom/movable/screen/alert/etherealcharge, 3) if(H.health > 10.5) apply_damage(0.65, TOX, null, null, H) + brutemod = 1.9 else H.throw_alert("nutrition", /atom/movable/screen/alert/etherealcharge, 4) if(H.health > 10.5) diff --git a/nsv13.dme b/nsv13.dme index ebba4f603ad..08d24ee8f07 100644 --- a/nsv13.dme +++ b/nsv13.dme @@ -3915,6 +3915,7 @@ #include "nsv13\code\modules\mob\living\carbon\carbon.dm" #include "nsv13\code\modules\mob\living\carbon\examine_tgui.dm" #include "nsv13\code\modules\mob\living\carbon\human\nsv_emotes.dm" +#include "nsv13\code\modules\mob\living\carbon\human\nsv_human_helpers.dm" #include "nsv13\code\modules\mob\living\carbon\human\species_types\catgirl.dm" #include "nsv13\code\modules\mob\living\carbon\human\species_types\nanotrasen_knpc.dm" #include "nsv13\code\modules\mob\living\carbon\human\species_types\other_knpc.dm" diff --git a/nsv13/code/modules/mob/living/carbon/human/nsv_human_helpers.dm b/nsv13/code/modules/mob/living/carbon/human/nsv_human_helpers.dm new file mode 100644 index 00000000000..c54880f6993 --- /dev/null +++ b/nsv13/code/modules/mob/living/carbon/human/nsv_human_helpers.dm @@ -0,0 +1,25 @@ +/** + * # `species_examine_font()` + * + * This gets a humanoid's special examine font, which is used to color their species name during examine / health analyzing. + * The first of these that applies is returned. + * Returns: + * * Metallic font if robotic + * * Cyan if a toxinlover + * * Yellow-ish if an Ethereal + * * Purple if plasmaperson + * * Rock / Brownish if a golem + * * Green if none of the others apply (aka, generic organic) +*/ +/mob/living/carbon/human/proc/species_examine_font() + if((MOB_ROBOTIC in mob_biotypes)) + return "sc_robotic" + if(HAS_TRAIT(src, TRAIT_TOXINLOVER)) + return "sc_toxlover" + if(isethereal(src)) + return "sc_ethereal" + if(isplasmaman(src)) + return "sc_plasmaman" + if(isgolem(src)) + return "sc_golem" + return "sc_normal" diff --git a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss index 41b2b9aad83..7c3ff319713 100644 --- a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss @@ -582,3 +582,12 @@ em {font-style: normal; font-weight: bold;} .stat_infomation {color: #e6a648;} .stat_br {color: #2ace53;} + +//NSV13 - species examine colors +.sc_robotic {color: #aaa9ad;} +.sc_toxlover {color: #00ffff;} +.sc_ethereal {color: #e0d31d;} +.sc_plasmaman {color: #c400c4} +.sc_golem {color: #b34a00} +.sc_normal {color: #18d855} +//NSV13 end diff --git a/tgui/packages/tgui-panel/styles/goon/chat-light.scss b/tgui/packages/tgui-panel/styles/goon/chat-light.scss index e543f40fb2d..e324d49ad74 100644 --- a/tgui/packages/tgui-panel/styles/goon/chat-light.scss +++ b/tgui/packages/tgui-panel/styles/goon/chat-light.scss @@ -581,3 +581,12 @@ h1.alert, h2.alert {color: #000000;} .stat_infomation {color: #be8530;} .stat_br {color: #2ace53;} + +//NSV13 - species examine colors +.sc_robotic {color: #8a898d;} +.sc_toxlover {color: #1e89d1;} +.sc_ethereal {color: #dda91a;} +.sc_plasmaman {color: #aa03aa} +.sc_golem {color: #8f3e05} +.sc_normal {color: #029731} +//NSV13 end From 0ddeca381dae20230bbdbf84958989f5544df375 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:50:48 -0700 Subject: [PATCH 21/36] Automatic changelog generation for PR #2582 [ci skip] --- html/changelogs/AutoChangeLog-pr-2582.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2582.yml diff --git a/html/changelogs/AutoChangeLog-pr-2582.yml b/html/changelogs/AutoChangeLog-pr-2582.yml new file mode 100644 index 00000000000..f2fc69757ba --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2582.yml @@ -0,0 +1,5 @@ +author: DeltaFire15 +delete-after: true +changes: + - bugfix: Ethereal starvation no longer has a nutrition window where increased brute + vulnerability was not handled. From 2a7d3c77d22276c2213dcecd4a542137d0a05f2c Mon Sep 17 00:00:00 2001 From: Bobbanz1 <59128051+Bobbanz1@users.noreply.github.com> Date: Fri, 15 Dec 2023 23:56:13 +0100 Subject: [PATCH 22/36] [EXPERIMENTAL] [UPSTREAM] Antag Preferences and Bans Rewrite (#2488) --- SQL/beestation_schema.sql | 1 + code/__DEFINES/antagonists.dm | 5 + code/__DEFINES/preferences.dm | 6 +- code/__DEFINES/role_preferences.dm | 242 ++++++++++++++---- code/__HELPERS/game.dm | 43 ++-- code/_globalvars/lists/poll_ignore.dm | 61 ++--- code/controllers/subsystem/job.dm | 7 +- code/controllers/subsystem/pai.dm | 6 +- code/datums/brain_damage/imaginary_friend.dm | 4 +- code/datums/brain_damage/split_personality.dm | 4 +- code/datums/diseases/transformation.dm | 5 +- code/game/gamemodes/brother/traitor_bro.dm | 4 +- code/game/gamemodes/changeling/changeling.dm | 19 +- .../game/gamemodes/changeling/traitor_chan.dm | 21 +- code/game/gamemodes/clock_cult/clockcult.dm | 6 +- code/game/gamemodes/cult/cult.dm | 6 +- code/game/gamemodes/devil/devil_game_mode.dm | 6 +- code/game/gamemodes/dynamic/dynamic.dm | 9 - .../gamemodes/dynamic/dynamic_rulesets.dm | 26 +- .../dynamic/dynamic_rulesets_latejoin.dm | 31 +-- .../dynamic/dynamic_rulesets_midround.dm | 51 ++-- .../dynamic/dynamic_rulesets_roundstart.dm | 41 ++- .../gamemodes/dynamic/dynamic_simulations.dm | 5 - code/game/gamemodes/dynamic/readme.md | 1 - .../gamemodes/eldritch_cult/eldritch_cult.dm | 6 +- code/game/gamemodes/game_mode.dm | 92 ++++--- code/game/gamemodes/gangs/gangs.dm | 4 +- code/game/gamemodes/hivemind/hivemind.dm | 6 +- code/game/gamemodes/incursion/incursion.dm | 9 +- code/game/gamemodes/nuclear/nuclear.dm | 8 +- code/game/gamemodes/overthrow/overthrow.dm | 5 +- code/game/gamemodes/revolution/revolution.dm | 6 +- code/game/gamemodes/traitor/double_agents.dm | 4 +- code/game/gamemodes/traitor/traitor.dm | 19 +- code/game/gamemodes/wizard/wizard.dm | 6 +- code/game/machinery/cloning.dm | 2 +- .../machinery/porta_turret/portable_turret.dm | 4 +- code/game/objects/effects/anomalies.dm | 2 +- code/game/objects/items/holy_weapons.dm | 2 +- .../objects/structures/ghost_role_spawners.dm | 118 +-------- code/game/objects/structures/spawner.dm | 2 +- code/modules/admin/antag_panel.dm | 16 +- code/modules/admin/fun_balloon.dm | 2 +- code/modules/admin/secrets.dm | 4 +- code/modules/admin/sql_ban_system.dm | 40 ++- code/modules/admin/verbs/one_click_antag.dm | 41 ++- code/modules/admin/verbs/randomverbs.dm | 4 +- .../antagonists/_common/antag_datum.dm | 18 +- .../antagonists/_common/antag_spawner.dm | 8 +- code/modules/antagonists/abductor/abductor.dm | 3 +- .../antagonists/ashwalker/ashwalker.dm | 2 +- code/modules/antagonists/blob/blob.dm | 2 +- code/modules/antagonists/blob/blob_mobs.dm | 4 +- .../blob/blobstrains/explosive_lattice.dm | 2 +- code/modules/antagonists/blob/overmind.dm | 4 +- code/modules/antagonists/blob/powers.dm | 8 +- .../antagonists/blob/structures/_blob.dm | 2 +- .../blood_contract/blood_contract.dm | 2 + .../antagonists/brainwashing/brainwashing.dm | 2 +- code/modules/antagonists/brother/brother.dm | 10 +- .../antagonists/changeling/changeling.dm | 3 +- .../antagonists/changeling/powers/teratoma.dm | 2 +- .../antagonists/changeling/teratoma.dm | 3 +- .../antagonists/clock_cult/mobs/cogscarab.dm | 5 - .../scriptures/sigil_of_vitality.dm | 2 +- .../clock_cult/scriptures/summon_marauder.dm | 2 +- .../clock_cult/servant_of_ratvar.dm | 3 +- .../clock_cult/structure/eminence_beacon.dm | 2 +- code/modules/antagonists/creep/creep.dm | 2 +- code/modules/antagonists/cult/cult.dm | 3 +- code/modules/antagonists/cult/cult_comms.dm | 2 +- code/modules/antagonists/cult/runes.dm | 2 +- code/modules/antagonists/devil/devil.dm | 2 +- code/modules/antagonists/devil/imp/imp.dm | 1 + .../devil/sintouched/sintouched.dm | 1 + .../eldritch_cult/eldritch_antag.dm | 3 +- .../eldritch_cult/eldritch_monster_antag.dm | 2 +- code/modules/antagonists/ert/ert.dm | 1 + code/modules/antagonists/fugitive/fugitive.dm | 1 + code/modules/antagonists/fugitive/hunter.dm | 1 + code/modules/antagonists/gang/gang.dm | 2 +- .../antagonists/greentext/greentext.dm | 1 + code/modules/antagonists/guardian/guardian.dm | 1 + .../antagonists/highlander/highlander.dm | 1 + code/modules/antagonists/hivemind/hivemind.dm | 5 +- code/modules/antagonists/hivemind/vessel.dm | 4 +- .../antagonists/incursion/incursion.dm | 12 +- .../antagonists/magic_servant/servant.dm | 1 + code/modules/antagonists/morph/morph.dm | 2 +- code/modules/antagonists/morph/morph_antag.dm | 1 + .../antagonists/nightmare/nightmare.dm | 1 + code/modules/antagonists/ninja/ninja.dm | 2 +- code/modules/antagonists/nukeop/nukeop.dm | 5 +- code/modules/antagonists/official/official.dm | 1 + .../antagonists/overthrow/overthrow.dm | 2 +- code/modules/antagonists/pirate/pirate.dm | 2 +- code/modules/antagonists/revenant/revenant.dm | 2 +- .../antagonists/revenant/revenant_antag.dm | 1 + .../revenant/revenant_spawn_event.dm | 2 +- .../antagonists/revolution/revolution.dm | 7 +- .../role_preference/_role_preference.dm | 27 ++ .../role_preference/role_antagonists.dm | 43 ++++ .../role_preference/role_changeling.dm | 3 + .../role_preference/role_midrounds.dm | 69 +++++ .../role_preference/role_operative.dm | 7 + .../role_preference/role_traitor.dm | 7 + .../role_preference/role_wizard.dm | 7 + .../roundstart_special/special_antagonist.dm | 11 +- code/modules/antagonists/santa/santa.dm | 1 + .../antagonists/separatist/separatist.dm | 1 + .../antagonists/slaughter/slaughter_antag.dm | 2 +- .../antagonists/slaughter/slaughterevent.dm | 2 +- .../antagonists/space_dragon/space_dragon.dm | 1 + .../antagonists/survivalist/survivalist.dm | 1 + .../antagonists/traitor/datum_traitor.dm | 8 +- .../traitor/equipment/contractor.dm | 2 +- .../antagonists/traitor/traitor_spawner.dm | 6 +- .../antagonists/valentines/heartbreaker.dm | 1 + .../antagonists/valentines/valentine.dm | 1 + .../antagonists/wishgranter/wishgranter.dm | 1 + .../antagonists/wizard/equipment/soulstone.dm | 2 +- code/modules/antagonists/wizard/wizard.dm | 7 +- code/modules/antagonists/xeno/xeno.dm | 2 +- code/modules/awaymissions/corpse.dm | 80 ++---- .../awaymissions/mission_code/Academy.dm | 7 +- .../awaymissions/mission_code/snowdin.dm | 2 +- code/modules/client/preferences.dm | 169 +++++++++--- .../client/preferences2/character_save.dm | 23 +- .../client/preferences2/preferences2.dm | 16 +- code/modules/client/preferences_toggles.dm | 9 - code/modules/clothing/outfits/vr.dm | 2 +- code/modules/events/abductor.dm | 2 +- code/modules/events/alien_infestation.dm | 2 +- code/modules/events/blob.dm | 2 +- code/modules/events/creep_awakening.dm | 2 +- code/modules/events/devil.dm | 2 +- code/modules/events/fugitive_spawning.dm | 2 +- code/modules/events/ghost_role.dm | 4 +- code/modules/events/holiday/xmas.dm | 2 +- code/modules/events/nightmare.dm | 2 +- code/modules/events/operative.dm | 2 +- code/modules/events/pirates.dm | 2 +- code/modules/events/sentience.dm | 2 +- code/modules/events/space_dragon.dm | 2 +- code/modules/events/special_antag_event.dm | 15 +- code/modules/events/wizard/imposter.dm | 2 +- .../food_and_drinks/food/snacks_meat.dm | 2 +- code/modules/guardian/guardian.dm | 4 +- code/modules/guardian/guardianbuilder.dm | 2 +- code/modules/guardian/standarrow.dm | 2 +- code/modules/jobs/jobs.dm | 2 +- .../modules/mob/dead/new_player/new_player.dm | 4 +- .../mob/dead/observer/notificationprefs.dm | 53 ---- code/modules/mob/living/carbon/alien/alien.dm | 2 +- .../modules/mob/living/carbon/alien/organs.dm | 4 +- .../carbon/alien/special/alien_embryo.dm | 2 +- code/modules/mob/living/living_sentience.dm | 17 +- .../modules/mob/living/silicon/robot/robot.dm | 2 +- .../living/simple_animal/bot/SuperBeepsky.dm | 2 +- .../friendly/drone/drones_as_items.dm | 6 - .../friendly/drone/extra_drone_types.dm | 2 +- .../living/simple_animal/guardian/guardian.dm | 4 +- .../mob/living/simple_animal/hostile/alien.dm | 2 +- .../mob/living/simple_animal/hostile/carp.dm | 2 +- .../hostile/mining_mobs/elites/elite.dm | 2 +- .../living/simple_animal/hostile/syndicate.dm | 4 +- .../living/simple_animal/hostile/wizard.dm | 2 +- .../mob/living/simple_animal/slime/slime.dm | 2 +- code/modules/mob/mob_helpers.dm | 6 +- code/modules/ninja/ninja_event.dm | 2 +- code/modules/projectiles/projectile/magic.dm | 6 +- code/modules/religion/rites.dm | 6 +- .../xenobiology/crossbreeding/warping.dm | 2 +- .../research/xenobiology/xenobiology.dm | 2 +- code/modules/ruins/lavaland_ruin_code.dm | 3 +- .../ruins/spaceruin_code/hilbertshotel.dm | 1 + .../objective_types/assassination.dm | 2 +- .../objective_types/vip_extraction.dm | 2 +- code/modules/shuttle/syndicate.dm | 2 +- code/modules/unit_tests/_unit_tests.dm | 2 + code/modules/unit_tests/antag_datums.dm | 15 ++ .../unit_tests/dynamic_ruleset_sanity.dm | 11 + code/modules/unit_tests/gamemode_sanity.dm | 20 ++ .../traits/xenoartifact_minors.dm | 3 +- config/dbconfig.txt | 4 +- config/dynamic.json | 19 +- html/admin/banpanel.css | 18 +- nsv13.dme | 11 +- .../controllers/subsystem/overmap_mode.dm | 2 +- nsv13/code/game/gamemodes/bloodling.dm | 31 ++- nsv13/code/game/gamemodes/pvp/pvp.dm | 4 +- nsv13/code/game/gamemodes/pvp/roles.dm | 6 +- .../spawners/custom_ghost_role_spawners.dm | 2 +- nsv13/code/modules/antagonists/bloodling.dm | 3 +- .../antagonists/ghostship/ghost_ship.dm | 8 + .../role_preference/role_antagonists.dm | 7 + .../role_preference/role_midrounds.dm | 7 + nsv13/code/modules/cargo/objective_cargo.dm | 2 +- nsv13/code/modules/overmap/ai-skynet.dm | 2 +- .../code/modules/overmap/boarding/boarding.dm | 2 +- .../overmap/boarding/ghost_role_spawners.dm | 22 ++ nsv13/code/modules/overmap/overmap_ghosts.dm | 6 +- .../interfaces/NotificationPreferences.js | 41 --- 203 files changed, 1230 insertions(+), 918 deletions(-) create mode 100644 code/modules/antagonists/role_preference/_role_preference.dm create mode 100644 code/modules/antagonists/role_preference/role_antagonists.dm create mode 100644 code/modules/antagonists/role_preference/role_changeling.dm create mode 100644 code/modules/antagonists/role_preference/role_midrounds.dm create mode 100644 code/modules/antagonists/role_preference/role_operative.dm create mode 100644 code/modules/antagonists/role_preference/role_traitor.dm create mode 100644 code/modules/antagonists/role_preference/role_wizard.dm delete mode 100644 code/modules/mob/dead/observer/notificationprefs.dm create mode 100644 code/modules/unit_tests/antag_datums.dm create mode 100644 code/modules/unit_tests/gamemode_sanity.dm create mode 100644 nsv13/code/modules/antagonists/ghostship/ghost_ship.dm create mode 100644 nsv13/code/modules/antagonists/role_preference/role_antagonists.dm create mode 100644 nsv13/code/modules/antagonists/role_preference/role_midrounds.dm delete mode 100644 tgui/packages/tgui/interfaces/NotificationPreferences.js diff --git a/SQL/beestation_schema.sql b/SQL/beestation_schema.sql index 632d928b17a..ea6af3a19df 100644 --- a/SQL/beestation_schema.sql +++ b/SQL/beestation_schema.sql @@ -122,6 +122,7 @@ CREATE TABLE IF NOT EXISTS `SS13_characters` ( `general_record` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci', `security_record` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci', `medical_record` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci', + `role_preferences` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci', PRIMARY KEY (`slot`, `ckey`) USING BTREE ) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB; diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 6620df43f48..93f62654284 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -76,6 +76,11 @@ #define IS_HERETIC(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic)) #define IS_HERETIC_MONSTER(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic_monster)) +#define FACTION_SYNDICATE "Syndicate" +#define FACTION_BLOB "Blob" +#define FACTION_ALIEN "Xenomorph" +#define FACTION_WIZARD "Wizard" + #define PATH_SIDE "Side" #define PATH_ASH "Ash" diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 003ccf3e3db..7fc49bbf376 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -6,7 +6,7 @@ #define PREFTOGGLE_SOUND_LOBBY (1<<3) #define PREFTOGGLE_MEMBER_PUBLIC (1<<4) #define PREFTOGGLE_INTENT_STYLE (1<<5) -#define PREFTOGGLE_MIDROUND_ANTAG (1<<6) +//#define PREFTOGGLE_MIDROUND_ANTAG (1<<6) #define PREFTOGGLE_SOUND_INSTRUMENTS (1<<7) #define PREFTOGGLE_SOUND_SHIP_AMBIENCE (1<<8) #define PREFTOGGLE_SOUND_PRAYERS (1<<9) @@ -27,7 +27,7 @@ #define PREFTOGGLE_RUNECHAT_NONMOBS (1<<22) #define PREFTOGGLE_RUNECHAT_EMOTES (1<<23) -#define TOGGLES_DEFAULT (PREFTOGGLE_SOUND_ADMINHELP|PREFTOGGLE_SOUND_MIDI|PREFTOGGLE_SOUND_AMBIENCE|PREFTOGGLE_SOUND_LOBBY|PREFTOGGLE_MEMBER_PUBLIC|PREFTOGGLE_INTENT_STYLE|PREFTOGGLE_MIDROUND_ANTAG|PREFTOGGLE_SOUND_INSTRUMENTS|PREFTOGGLE_SOUND_SHIP_AMBIENCE|PREFTOGGLE_SOUND_PRAYERS|PREFTOGGLE_SOUND_ANNOUNCEMENTS|PREFTOGGLE_OUTLINE_ENABLED|PREFTOGGLE_RUNECHAT_GLOBAL|PREFTOGGLE_RUNECHAT_NONMOBS|PREFTOGGLE_RUNECHAT_EMOTES) +#define TOGGLES_DEFAULT (PREFTOGGLE_SOUND_ADMINHELP|PREFTOGGLE_SOUND_MIDI|PREFTOGGLE_SOUND_AMBIENCE|PREFTOGGLE_SOUND_LOBBY|PREFTOGGLE_MEMBER_PUBLIC|PREFTOGGLE_INTENT_STYLE|PREFTOGGLE_SOUND_INSTRUMENTS|PREFTOGGLE_SOUND_SHIP_AMBIENCE|PREFTOGGLE_SOUND_PRAYERS|PREFTOGGLE_SOUND_ANNOUNCEMENTS|PREFTOGGLE_OUTLINE_ENABLED|PREFTOGGLE_RUNECHAT_GLOBAL|PREFTOGGLE_RUNECHAT_NONMOBS|PREFTOGGLE_RUNECHAT_EMOTES) // You CANNOT go above 1<<23 in BYOND due to integer limits // Please add subsequent ones as PREFTOGGLE_2_[name] @@ -183,7 +183,7 @@ #define PREFERENCE_TAG_PDA_COLOUR "23" #define PREFERENCE_TAG_KEYBINDS "24" #define PREFERENCE_TAG_PURCHASED_GEAR "25" -#define PREFERENCE_TAG_BE_SPECIAL "26" +#define PREFERENCE_TAG_ROLE_PREFERENCES "26" #define PREFERENCE_TAG_PREFERRED_SYNDIE_ROLE "27" //NSV13 - syndicate crew role // True value of max save slots (3 is default, 8 is byond member, +1 to either if you have the extra slot loadout entry). Potential max is 9 diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index c5ba51036ad..c1da00441f8 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -4,21 +4,21 @@ +// Banning snowflake - global antag ban. Does not include ghost roles that aren't antagonists or forced antagonists +#define BAN_ROLE_ALL_ANTAGONISTS "All Antagonists" + //These are synced with the Database, if you change the values of the defines //then you MUST update the database! -#define ROLE_SYNDICATE "Syndicate" #define ROLE_TRAITOR "Traitor" -#define ROLE_OPERATIVE "Operative" +#define ROLE_OPERATIVE "Nuclear Operative" #define ROLE_CHANGELING "Changeling" #define ROLE_WIZARD "Wizard" -#define ROLE_MALF "Malf AI" +//#define ROLE_MALF "Malf AI" // Currently under traitor datum, so we can't have this separate. #define ROLE_INCURSION "Incursion Team" #define ROLE_EXCOMM "Excommunicated Syndicate Agent" #define ROLE_REV "Revolutionary" #define ROLE_REV_HEAD "Head Revolutionary" -#define ROLE_REV_SUCCESSFUL "Victorious Revolutionary" #define ROLE_ALIEN "Xenomorph" -#define ROLE_PAI "pAI" #define ROLE_CULTIST "Cultist" #define ROLE_SERVANT_OF_RATVAR "Servant of Ratvar" #define ROLE_HERETIC "Heretic" @@ -28,66 +28,222 @@ #define ROLE_REVENANT "Revenant" #define ROLE_DEVIL "Devil" #define ROLE_BROTHER "Blood Brother" -#define ROLE_BRAINWASHED "Brainwashed Victim" #define ROLE_OVERTHROW "Syndicate Mutineer" #define ROLE_HIVE "Hivemind Host" -#define ROLE_HIVE_VESSEL "Awakened Vessel" #define ROLE_OBSESSED "Obsessed" -#define ROLE_SENTIENCE "Sentience Potion Spawn" -#define ROLE_MIND_TRANSFER "Mind Transfer Potion" -#define ROLE_POSIBRAIN "Posibrain" -#define ROLE_DRONE "Drone" -#define ROLE_DEATHSQUAD "Deathsquad" -#define ROLE_LAVALAND "Lavaland" +#define ROLE_SPACE_DRAGON "Space Dragon" #define ROLE_INTERNAL_AFFAIRS "Internal Affairs Agent" #define ROLE_GANG "Gangster" #define ROLE_HOLOPARASITE "Holoparasite" #define ROLE_TERATOMA "Teratoma" - +#define ROLE_MORPH "Morph" +#define ROLE_NIGHTMARE "Nightmare" +#define ROLE_SPACE_PIRATE "Space Pirate" +#define ROLE_FUGITIVE "Fugitive" +#define ROLE_FUGITIVE_HUNTER "Fugitive Hunter" #define ROLE_SYNDI_CREW "Syndicate crew" //Nsv13 - added pvp role #define ROLE_BLOODLING "Bloodling" //Nsv13 - Bloodling #define ROLE_GHOSTSHIP "Ghost Ship" //NSV13 - Playable "NPC" ships -#define ROLE_EXPERIMENTAL_CLONE "Experimental Clone" +#define ROLE_SLAUGHTER_DEMON "Slaughter Demon" +#define ROLE_CONTRACTOR_SUPPORT_UNIT "Contractor Support Unit" +#define ROLE_PYRO_SLIME "Pyroclastic Anomaly Slime" -//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. -//The gamemode specific ones are just so the gamemodes can query whether a player is old enough -//(in game days played) to play that role -GLOBAL_LIST_INIT(special_roles, list( - ROLE_TRAITOR = /datum/game_mode/traitor, - ROLE_BROTHER = /datum/game_mode/traitor/bros, - ROLE_INCURSION = /datum/game_mode/incursion, - ROLE_EXCOMM = /datum/game_mode/incursion, - ROLE_OPERATIVE = /datum/game_mode/nuclear, - ROLE_CHANGELING = /datum/game_mode/changeling, - ROLE_WIZARD = /datum/game_mode/wizard, - ROLE_MALF, - ROLE_REV = /datum/game_mode/revolution, +/// Roles that are antagonists, roundstart or not, and have passes to do.. antagonistry +GLOBAL_LIST_INIT(antagonist_bannable_roles, list( + ROLE_TRAITOR, + ROLE_OPERATIVE, + ROLE_CHANGELING, + ROLE_WIZARD, +// ROLE_MALF, + ROLE_INCURSION, + ROLE_EXCOMM, + ROLE_REV, + ROLE_REV_HEAD, ROLE_ALIEN, - ROLE_PAI, - ROLE_CULTIST = /datum/game_mode/cult, - ROLE_SERVANT_OF_RATVAR = /datum/game_mode/clockcult, + ROLE_CULTIST, + ROLE_SERVANT_OF_RATVAR, + ROLE_HERETIC, ROLE_BLOB, ROLE_NINJA, - ROLE_OBSESSED, - ROLE_REVENANT, ROLE_ABDUCTOR, - ROLE_DEVIL = /datum/game_mode/devil, - ROLE_OVERTHROW = /datum/game_mode/overthrow, - ROLE_HIVE = /datum/game_mode/hivemind, - ROLE_INTERNAL_AFFAIRS = /datum/game_mode/traitor/internal_affairs, - ROLE_SENTIENCE, - ROLE_GANG = /datum/game_mode/gang, + ROLE_REVENANT, + ROLE_DEVIL, + ROLE_BROTHER, + ROLE_OVERTHROW, + ROLE_HIVE, + ROLE_OBSESSED, + ROLE_SPACE_DRAGON, + ROLE_INTERNAL_AFFAIRS, + ROLE_GANG, ROLE_HOLOPARASITE, - ROLE_HERETIC = /datum/game_mode/heretics, - ROLE_SYNDI_CREW = /datum/game_mode/pvp, //NSV13 - ROLE_BLOODLING = /datum/game_mode/bloodling, //NSV13 + ROLE_MORPH, + ROLE_NIGHTMARE, + ROLE_SPACE_PIRATE, + ROLE_SYNDI_CREW, //NSV13 + ROLE_BLOODLING, //NSV13 ROLE_GHOSTSHIP, //NSV13 - ROLE_TERATOMA + ROLE_TERATOMA, + ROLE_FUGITIVE, + ROLE_FUGITIVE_HUNTER, + ROLE_SLAUGHTER_DEMON, + ROLE_CONTRACTOR_SUPPORT_UNIT, )) //nsv13 - pvp + bloodling modes added here +#define BAN_ROLE_FORCED_ANTAGONISTS "Forced Antagonists" + +#define ROLE_BRAINWASHED "Brainwashed Victim" +#define ROLE_HYPNOTIZED "Hypnotized Victim" +#define ROLE_HIVE_VESSEL "Awakened Vessel" + +/// Forced antagonist roles +GLOBAL_LIST_INIT(forced_bannable_roles, list( + ROLE_BRAINWASHED, + ROLE_HYPNOTIZED, + ROLE_HIVE_VESSEL, +)) + +#define BAN_ROLE_ALL_GHOST "Non-Antagonist Ghost Roles" + +#define ROLE_PAI "pAI" +#define ROLE_POSIBRAIN "Posibrain" +#define ROLE_DRONE "Drone" +#define ROLE_SENTIENCE "Sentience Potion Spawn" +#define ROLE_EXPERIMENTAL_CLONE "Experimental Clone" +#define ROLE_LAVALAND_ELITE "Lavaland Elite" +#define ROLE_SPECTRAL_BLADE "Spectral Blade" +#define ROLE_ASHWALKER "Ashwalker" +#define ROLE_LIFEBRINGER "Lifebringer" +#define ROLE_FREE_GOLEM "Free Golem" +#define ROLE_HERMIT "Hermit" +#define ROLE_TRANSLOCATED_VET "Translocated Vet" +#define ROLE_LAVALAND_ESCAPED_PRISONER "Lavaland Escaped Prisoner" +#define ROLE_BEACH_BUM "Beach Bum" +#define ROLE_HOTEL_STAFF "Hotel Staff" +#define ROLE_LAVALAND_SYNDICATE "Lavaland Syndicate" +#define ROLE_DEMONIC_FRIEND "Demonic Friend" +#define ROLE_ANCIENT_CREW "Ancient Crew" +#define ROLE_SKELETAL_REMAINS "Skeletal Remains" +#define ROLE_SENTIENT_ANIMAL "Sentient Animal" +#define ROLE_HOLY_SUMMONED "Holy Summoned" +#define ROLE_SURVIVALIST "Exploration Survivalist" +#define ROLE_EXPLORATION_VIP "Exploration VIP" +#define ROLE_SENTIENT_XENOARTIFACT "Sentient Xenoartifiact" + +/// Any ghost role that is not really an antagonist or doesn't antagonize (lavaland, sentience potion, etc) +GLOBAL_LIST_INIT(ghost_role_bannable_roles, list( + ROLE_PAI, + ROLE_POSIBRAIN, + ROLE_DRONE, + ROLE_SENTIENCE, + ROLE_EXPERIMENTAL_CLONE, + ROLE_LAVALAND_ELITE, + ROLE_SPECTRAL_BLADE, + ROLE_ASHWALKER, + ROLE_LIFEBRINGER, + ROLE_FREE_GOLEM, + ROLE_HERMIT, + ROLE_TRANSLOCATED_VET, + ROLE_LAVALAND_ESCAPED_PRISONER, + ROLE_BEACH_BUM, + ROLE_HOTEL_STAFF, + ROLE_LAVALAND_SYNDICATE, + ROLE_DEMONIC_FRIEND, + ROLE_ANCIENT_CREW, + ROLE_SKELETAL_REMAINS, + ROLE_SENTIENT_ANIMAL, + ROLE_HOLY_SUMMONED, +)) + +#define ROLE_IMAGINARY_FRIEND "Imaginary Friend" +#define ROLE_SPLIT_PERSONALITY "Split Personality" +#define ROLE_MIND_TRANSFER "Mind Transfer Potion" +#define ROLE_ERT "Emergency Response Team" + +/// Other roles that don't really fit any of the above, and probably shouldn't be banned with the others as a group +/// Little to no impact on anything +GLOBAL_LIST_INIT(other_bannable_roles, list( + ROLE_IMAGINARY_FRIEND, + ROLE_SPLIT_PERSONALITY, + ROLE_MIND_TRANSFER, + ROLE_ERT, +)) + +/// Do not ban this role. Oh my god. Please. +#define UNBANNABLE_ANTAGONIST "Unbannable" + +/client/proc/role_preference_enabled(role_preference_key) + if(!ispath(role_preference_key, /datum/role_preference)) + CRASH("Invalid role_preference_key [role_preference_key] passed to role_preference_enabled!") + if(!src.prefs) + return FALSE + var/list/source = src.prefs.role_preferences + var/datum/role_preference/pref = role_preference_key + if(initial(pref.per_character)) + source = src.prefs.active_character.role_preferences_character + var/role_preference_value = source["[role_preference_key]"] + if(isnum(role_preference_value) && !role_preference_value) // explicitly disabled and not null + return FALSE + return TRUE + +/// If the client given is fit for a given role based on the arguments passed +/// banning_key: ROLE_X used for this role - to check if the player is banned. +/// role_preference_key: The /datum/role_preference typepath to check if the player has the role enabled and would like to receive the poll. +/// poll_ignore_key: The POLL_IGNORE_X define for this role, used for temporarily disabling ghost polls for high volume roles. +/// req_hours: The amount of living hours required to receive this role. +/// feedback: if we should send a to_chat +/client/proc/should_include_for_role(banning_key = BAN_ROLE_ALL_ANTAGONISTS, role_preference_key = null, poll_ignore_key = null, req_hours = 0, feedback = FALSE) + if(QDELETED(src) || (poll_ignore_key && GLOB.poll_ignore[poll_ignore_key] && (src.ckey in GLOB.poll_ignore[poll_ignore_key]))) + return FALSE + if(role_preference_key) + if(!ispath(role_preference_key, /datum/role_preference)) + CRASH("Invalid role_preference_key [role_preference_key] passed to should_include_for_role!") + if(!src.role_preference_enabled(role_preference_key)) + return FALSE + if(banning_key) + if(is_banned_from(src.ckey, banning_key)) + if(feedback) + to_chat(src, "You are banned from this role!") + return FALSE + if(req_hours) //minimum living hour count + if((src.get_exp_living(TRUE)/60) < req_hours) + if(feedback) + to_chat(src, "You do not have enough living hours to take this role ([req_hours]hrs required)!") + return FALSE + return TRUE + +/client/proc/can_take_ghost_spawner(banning_key = BAN_ROLE_ALL_ANTAGONISTS, use_cooldown = TRUE, is_ghost_role = FALSE, is_admin_spawned = FALSE) + if(!istype(src)) + return FALSE + if(is_ghost_role && !(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER) && !is_admin_spawned) + to_chat(src, "An admin has temporarily disabled non-admin ghost roles!") + return FALSE + if(!src.should_include_for_role( + banning_key = banning_key, + feedback = TRUE + )) + return FALSE + if(use_cooldown && src.next_ghost_role_tick > world.time) + to_chat(src, "You have died recently, you must wait [(src.next_ghost_role_tick - world.time)/10] seconds until you can use a ghost spawner.") + return FALSE + return TRUE + //Job defines for what happens when you fail to qualify for any job during job selection #define BEOVERFLOW 1 #define BERANDOMJOB 2 #define RETURNTOLOBBY 3 + +#define ROLE_PREFERENCE_CATEGORY_ANAGONIST "Antagonists" +#define ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING "Midrounds (Living)" +#define ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST "Midrounds (Ghost Poll)" + +GLOBAL_LIST_INIT(role_preference_entries, init_role_preference_entries()) + +/proc/init_role_preference_entries() + var/list/output = list() + for (var/datum/role_preference/preference_type as anything in subtypesof(/datum/role_preference)) + if (initial(preference_type.abstract_type) == preference_type) + continue + output[preference_type] = new preference_type + return output diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index dae5cf88e73..89c70e94cc6 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -425,7 +425,7 @@ else candidates -= M -/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, req_hours = 0) +/proc/pollGhostCandidates(Question, jobbanType, role_preference_key, poll_time = 300, ignore_category = null, flashwindow = TRUE, req_hours = 0) var/list/candidates = list() if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE)) return candidates @@ -433,31 +433,30 @@ for(var/mob/dead/observer/G in GLOB.player_list) candidates += G - return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates, req_hours) + return pollCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category, flashwindow, candidates, req_hours) -/proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null, req_hours = 0) +/proc/pollCandidates(Question, banning_key, role_preference_key = null, poll_time = 300, poll_ignore_key = null, flashwindow = TRUE, list/group = null, req_hours = 0) var/time_passed = world.time if (!Question) Question = "Would you like to be a special role?" + if(isnull(poll_ignore_key)) // FALSE will not put one, no matter what + if(role_preference_key) + poll_ignore_key = "role_[role_preference_key]" + else if(banning_key) + poll_ignore_key = "ban_[role_preference_key]" var/list/result = list() for(var/m in group) var/mob/M = m - if(!M.key || !M.client || (ignore_category && GLOB.poll_ignore[ignore_category] && (M.ckey in GLOB.poll_ignore[ignore_category]))) + if(QDELETED(M) || !M.key || !M.client) continue - if(be_special_flag) - if(!(M.client.prefs) || !(be_special_flag in M.client.prefs.be_special)) - continue - if(gametypeCheck) - if(!gametypeCheck.age_check(M.client)) - continue - if(jobbanType) - if(QDELETED(M) || is_banned_from(M.ckey, list(jobbanType, ROLE_SYNDICATE))) - continue - if(req_hours) //minimum living hour count - if((M.client.get_exp_living(TRUE)/60) < req_hours) - continue - - showCandidatePollWindow(M, poll_time, Question, result, ignore_category, time_passed, flashwindow) + if(!M.client.should_include_for_role( + banning_key = banning_key, + role_preference_key = role_preference_key, + poll_ignore_key = poll_ignore_key, + req_hours = req_hours + )) + continue + showCandidatePollWindow(M, poll_time, Question, result, poll_ignore_key, time_passed, flashwindow) sleep(poll_time) //Check all our candidates, to make sure they didn't log off or get deleted during the wait period. @@ -469,14 +468,14 @@ return result -/proc/pollCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null) - var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) +/proc/pollCandidatesForMob(Question, jobbanType, role_preference_key, poll_time = 300, mob/M, ignore_category = null) + var/list/L = pollGhostCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category) if(QDELETED(M) || !M.loc) return list() return L -/proc/pollCandidatesForMobs(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null) - var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) +/proc/pollCandidatesForMobs(Question, jobbanType, role_preference_key, poll_time = 300, list/mobs, ignore_category = null) + var/list/L = pollGhostCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category) var/i=1 for(var/v in mobs) var/atom/A = v diff --git a/code/_globalvars/lists/poll_ignore.dm b/code/_globalvars/lists/poll_ignore.dm index 6f15e22d270..1cca65374b0 100644 --- a/code/_globalvars/lists/poll_ignore.dm +++ b/code/_globalvars/lists/poll_ignore.dm @@ -1,57 +1,42 @@ //Each lists stores ckeys for "Never for this round" option category -#define POLL_IGNORE_SENTIENCE_POTION "sentience_potion" -#define POLL_IGNORE_POSSESSED_BLADE "possessed_blade" #define POLL_IGNORE_ALIEN_LARVA "alien_larva" -#define POLL_IGNORE_SYNDICATE "syndicate" -#define POLL_IGNORE_HOLOPARASITE "holoparasite" #define POLL_IGNORE_POSIBRAIN "posibrain" -#define POLL_IGNORE_SPECTRAL_BLADE "spectral_blade" -#define POLL_IGNORE_CONSTRUCT "construct" #define POLL_IGNORE_SPIDER "spider" #define POLL_IGNORE_ASHWALKER "ashwalker" +#define POLL_IGNORE_BLOB_HELPER "blob_helper" +#define POLL_IGNORE_CLOCKWORK_HELPER "clockwork_helper" +#define POLL_IGNORE_CULT_SHADE "cult_shade" #define POLL_IGNORE_GOLEM "golem" -#define POLL_IGNORE_SWARMER "swarmer" #define POLL_IGNORE_DRONE "drone" -#define POLL_IGNORE_FUGITIVE "fugitive" -#define POLL_IGNORE_DEFECTIVECLONE "defective_clone" -#define POLL_IGNORE_PYROSLIME "slime" +#define POLL_IGNORE_SWARMER "swarmer" +#define POLL_IGNORE_SPECTRAL_BLADE "spectral_blade" #define POLL_IGNORE_SHADE "shade" -#define POLL_IGNORE_IMAGINARYFRIEND "imaginary_friend" +#define POLL_IGNORE_FUGITIVE "fugitive" #define POLL_IGNORE_SPLITPERSONALITY "split_personality" -#define POLL_IGNORE_CONTRACTOR_SUPPORT "contractor_support" -#define POLL_IGNORE_CLOCKWORK "clockwork" #define POLL_IGNORE_GHOSTSHIP "ghost ships" //NSV13 -#define POLL_IGNORE_EXPERIMENTAL_CLONE "experimental_clone" +#define POLL_IGNORE_WIZARD_HELPER "wizard_helper" -GLOBAL_LIST_INIT(poll_ignore_desc, list( - POLL_IGNORE_SENTIENCE_POTION = "Sentience potion", - POLL_IGNORE_POSSESSED_BLADE = "Possessed blade", - POLL_IGNORE_ALIEN_LARVA = "Xenomorph larva", - POLL_IGNORE_SYNDICATE = "Syndicate", - POLL_IGNORE_HOLOPARASITE = "Holoparasite", - POLL_IGNORE_POSIBRAIN = "Positronic brain", - POLL_IGNORE_SPECTRAL_BLADE = "Spectral blade", - POLL_IGNORE_CONSTRUCT = "Construct", - POLL_IGNORE_SPIDER = "Spiders", - POLL_IGNORE_ASHWALKER = "Ashwalker eggs", - POLL_IGNORE_GOLEM = "Golems", - POLL_IGNORE_SWARMER = "Swarmer shells", - POLL_IGNORE_DRONE = "Drone shells", - POLL_IGNORE_FUGITIVE = "Fugitive Hunter", - POLL_IGNORE_DEFECTIVECLONE = "Defective clone", - POLL_IGNORE_PYROSLIME = "Slime", - POLL_IGNORE_SHADE = "Shade", - POLL_IGNORE_IMAGINARYFRIEND = "Imaginary Friend", - POLL_IGNORE_SPLITPERSONALITY = "Split Personality", - POLL_IGNORE_CONTRACTOR_SUPPORT = "Contractor Support Unit", - POLL_IGNORE_GHOSTSHIP = "Ghost Ship", //NSV13 - POLL_IGNORE_EXPERIMENTAL_CLONE = "Experimental clone" +GLOBAL_LIST_INIT(poll_ignore_list, list( + POLL_IGNORE_ALIEN_LARVA, + POLL_IGNORE_ASHWALKER, + POLL_IGNORE_BLOB_HELPER, + POLL_IGNORE_CLOCKWORK_HELPER, + POLL_IGNORE_CULT_SHADE, + POLL_IGNORE_GOLEM, + POLL_IGNORE_DRONE, + POLL_IGNORE_POSIBRAIN, + POLL_IGNORE_SPECTRAL_BLADE, + POLL_IGNORE_SHADE, + POLL_IGNORE_SPIDER, + POLL_IGNORE_GHOSTSHIP, //NSV13 + POLL_IGNORE_WIZARD_HELPER, )) + GLOBAL_LIST_INIT(poll_ignore, init_poll_ignore()) /proc/init_poll_ignore() . = list() - for (var/k in GLOB.poll_ignore_desc) + for (var/k in GLOB.poll_ignore_list) .[k] = list() diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index bd62bf9d98e..41a29447708 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -137,8 +137,8 @@ SUBSYSTEM_DEF(job) return FALSE job.current_positions = max(0, job.current_positions - 1) -/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level, flag) - JobDebug("Running FOC, Job: [job], Level: [level], Flag: [flag]") +/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level) + JobDebug("Running FOC, Job: [job], Level: [level]") var/list/candidates = list() for(var/mob/dead/new_player/player in unassigned) if(QDELETED(player) || is_banned_from(player.ckey, job.title)) @@ -150,9 +150,6 @@ SUBSYSTEM_DEF(job) if(job.required_playtime_remaining(player.client)) JobDebug("FOC player not enough xp, Player: [player]") continue - if(flag && (!(flag in player.client.prefs.be_special))) - JobDebug("FOC flag failed, Player: [player], Flag: [flag], ") - continue if(player.mind && (job.title in player.mind.restricted_roles)) JobDebug("FOC incompatible with antagonist role, Player: [player]") continue diff --git a/code/controllers/subsystem/pai.dm b/code/controllers/subsystem/pai.dm index d942dda58bd..4fc3090979d 100644 --- a/code/controllers/subsystem/pai.dm +++ b/code/controllers/subsystem/pai.dm @@ -149,8 +149,10 @@ SUBSYSTEM_DEF(pai) for(var/mob/dead/observer/G in GLOB.player_list) if(!G.key || !G.client) continue - if(!(ROLE_PAI in G.client.prefs.be_special)) - continue + //NSV13 - Disabled until Beemerge, I don't know what to put here without porting from another PR so WEH + //if(!(ROLE_PAI in G.client.prefs.be_special)) + // continue + //NSV13 - Stop to_chat(G, "[user] is requesting a pAI personality! Use the pAI button to submit yourself as one.") addtimer(CALLBACK(src, PROC_REF(spam_again)), spam_delay) var/list/available = list() diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index b77bd799465..ad30ac75041 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -46,7 +46,7 @@ if(owner.stat == DEAD || !owner.mind) qdel(src) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_PAI, null, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_IMAGINARY_FRIEND, null, 7.5 SECONDS, friend) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) friend.key = C.key @@ -193,7 +193,7 @@ hearers += client if(owner_chat_map) hearers += owner.client - + var/rendered = "[name] [say_quote(message)]" var/dead_rendered = "[name] (Imaginary friend of [owner]) [say_quote(message)]" diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index b2ebc6c1899..c5a8a5fd4fd 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -31,7 +31,7 @@ if(owner.stat == DEAD || !owner.mind) qdel(src) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_PAI, null, null, 75, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_SPLIT_PERSONALITY, null, 7.5 SECONDS, stranger_backseat) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) stranger_backseat.key = C.key @@ -196,7 +196,7 @@ /datum/brain_trauma/severe/split_personality/brainwashing/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s brainwashed mind?", null, null, null, 75, stranger_backseat) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s brainwashed mind?", ROLE_TRAITOR, null, 7.5 SECONDS, stranger_backseat, ignore_category = FALSE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) stranger_backseat.key = C.key diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index b60bedb1a3e..218d0255411 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -80,10 +80,11 @@ /datum/disease/transformation/proc/replace_banned_player(var/mob/living/new_mob) // This can run well after the mob has been transferred, so need a handle on the new mob to kill it if needed. set waitfor = FALSE + affected_mob.playable_bantype = bantype affected_mob.ghostize(TRUE,SENTIENCE_FORCE) to_chat(affected_mob, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.name]?", bantype, null, bantype, 50, affected_mob) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.name]?", bantype, null, 7.5 SECONDS, affected_mob, ignore_category = FALSE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbanned player.") @@ -108,7 +109,7 @@ stage5 = list("Your skin feels as if it's about to burst off!") new_form = /mob/living/silicon/robot infectable_biotypes = list(MOB_ORGANIC, MOB_UNDEAD, MOB_ROBOTIC) - bantype = "Cyborg" + bantype = JOB_NAME_CYBORG /datum/disease/transformation/robot/stage_act() ..() diff --git a/code/game/gamemodes/brother/traitor_bro.dm b/code/game/gamemodes/brother/traitor_bro.dm index 6059dba2a14..2ba9c586359 100644 --- a/code/game/gamemodes/brother/traitor_bro.dm +++ b/code/game/gamemodes/brother/traitor_bro.dm @@ -27,7 +27,7 @@ if(CONFIG_GET(flag/protect_heads_from_antagonist)) restricted_jobs += GLOB.command_positions - var/list/datum/mind/possible_brothers = get_players_for_role(ROLE_BROTHER) + var/list/datum/mind/possible_brothers = get_players_for_role(/datum/antagonist/brother, /datum/role_preference/antagonist/blood_brother) var/num_teams = team_amount var/bsc = CONFIG_GET(number/brother_scaling_coeff) @@ -40,7 +40,7 @@ var/datum/team/brother_team/team = new var/team_size = prob(10) ? min(3, possible_brothers.len) : 2 for(var/k = 1 to team_size) - var/datum/mind/bro = antag_pick(possible_brothers, ROLE_BROTHER) + var/datum/mind/bro = antag_pick(possible_brothers, /datum/role_preference/antagonist/blood_brother) possible_brothers -= bro antag_candidates -= bro team.add_member(bro) diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 884e9025c74..49b9cdcf0c9 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -10,7 +10,8 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w name = "changeling" config_tag = "changeling" report_type = "changeling" - antag_flag = ROLE_CHANGELING + role_preference = /datum/role_preference/antagonist/changeling + antag_datum = /datum/antagonist/changeling false_report_weight = 10 restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG) protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) @@ -52,7 +53,7 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w for(var/i = 0, i < num_changelings, i++) if(!antag_candidates.len) break - var/datum/mind/changeling = antag_pick(antag_candidates, ROLE_CHANGELING) + var/datum/mind/changeling = antag_pick(antag_candidates, /datum/role_preference/antagonist/changeling) antag_candidates -= changeling changelings += changeling changeling.special_role = ROLE_CHANGELING @@ -75,12 +76,14 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w if(changelings.len >= changelingcap) //Caps number of latejoin antagonists return if(changelings.len <= (changelingcap - 2) || prob(100 - (csc * 2))) - if(ROLE_CHANGELING in character.client.prefs.be_special) - if(!is_banned_from(character.ckey, list(ROLE_CHANGELING, ROLE_SYNDICATE)) && !QDELETED(character)) - if(age_check(character.client)) - if(!(character.job in restricted_jobs)) - character.mind.make_Changeling() - changelings += character.mind + if(!QDELETED(character) && character.client?.should_include_for_role( + banning_key = initial(antag_datum.banning_key), + role_preference_key = role_preference, + req_hours = initial(antag_datum.required_living_playtime) + )) + if(!(character.job in restricted_jobs)) + character.mind.make_Changeling() + changelings += character.mind /datum/game_mode/changeling/generate_report() return "The Gorlex Marauders have announced the successful raid and destruction of Central Command containment ship #S-[rand(1111, 9999)]. This ship housed only a single prisoner - \ diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm index 95057675495..6912af56cbf 100644 --- a/code/game/gamemodes/changeling/traitor_chan.dm +++ b/code/game/gamemodes/changeling/traitor_chan.dm @@ -22,7 +22,7 @@ /datum/game_mode/traitor/changeling/can_start() if(!..()) return 0 - possible_changelings = get_players_for_role(ROLE_CHANGELING) + possible_changelings = get_players_for_role(/datum/antagonist/changeling, /datum/role_preference/antagonist/changeling) if(possible_changelings.len < required_enemies) return 0 return 1 @@ -37,7 +37,7 @@ if(CONFIG_GET(flag/protect_heads_from_antagonist)) restricted_jobs += GLOB.command_positions - var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING) + var/list/datum/mind/possible_changelings = get_players_for_role(/datum/antagonist/changeling, /datum/role_preference/antagonist/changeling) var/num_changelings = 1 @@ -51,7 +51,7 @@ for(var/j = 0, j < num_changelings, j++) if(!possible_changelings.len) break - var/datum/mind/changeling = antag_pick(possible_changelings, ROLE_CHANGELING) + var/datum/mind/changeling = antag_pick(possible_changelings, /datum/role_preference/antagonist/changeling) antag_candidates -= changeling possible_changelings -= changeling changeling.special_role = ROLE_CHANGELING @@ -72,13 +72,16 @@ if(changelings.len >= changelingcap) //Caps number of latejoin antagonists ..() return + var/datum/antagonist/aux_antag_datum = /datum/antagonist/changeling if(changelings.len <= (changelingcap - 2) || prob(100 / (csc * 4))) - if(ROLE_CHANGELING in character.client.prefs.be_special) - if(!is_banned_from(character.ckey, list(ROLE_CHANGELING, ROLE_SYNDICATE)) && !QDELETED(character)) - if(age_check(character.client)) - if(!(character.job in restricted_jobs)) - character.mind.make_Changeling() - changelings += character.mind + if(!QDELETED(character) && character.client.should_include_for_role( + banning_key = initial(aux_antag_datum.banning_key), + role_preference_key = /datum/role_preference/antagonist/changeling, + req_hours = initial(aux_antag_datum.required_living_playtime) + )) + if(!(character.job in restricted_jobs)) + character.mind.make_Changeling() + changelings += character.mind if(QDELETED(character)) return ..() diff --git a/code/game/gamemodes/clock_cult/clockcult.dm b/code/game/gamemodes/clock_cult/clockcult.dm index b31dd9e379c..af4f68b3bc8 100644 --- a/code/game/gamemodes/clock_cult/clockcult.dm +++ b/code/game/gamemodes/clock_cult/clockcult.dm @@ -32,8 +32,8 @@ GLOBAL_VAR(clockcult_eminence) required_players = 24 required_enemies = 4 recommended_enemies = 4 - antag_flag = ROLE_SERVANT_OF_RATVAR - enemy_minimum_age = 14 + role_preference = /datum/role_preference/antagonist/clock_cultist + antag_datum = /datum/antagonist/servant_of_ratvar title_icon = "clockcult" announce_span = "danger" @@ -58,7 +58,7 @@ GLOBAL_VAR(clockcult_eminence) for(var/i in 1 to clock_cultists) if(!antag_candidates.len) break - var/datum/mind/clockie = antag_pick(antag_candidates, ROLE_SERVANT_OF_RATVAR) + var/datum/mind/clockie = antag_pick(antag_candidates, /datum/role_preference/antagonist/clock_cultist) //In case antag_pick breaks if(!clockie) continue diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index 7b2498f9263..1280a629674 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -36,14 +36,14 @@ name = "cult" config_tag = "cult" report_type = "cult" - antag_flag = ROLE_CULTIST + role_preference = /datum/role_preference/antagonist/blood_cultist + antag_datum = /datum/antagonist/cult false_report_weight = 1 restricted_jobs = list(JOB_NAME_CHAPLAIN,JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL) protected_jobs = list() required_players = 29 required_enemies = 4 recommended_enemies = 4 - enemy_minimum_age = 14 announce_span = "cult" announce_text = "Some crew members are trying to start a cult to Nar'Sie!\n\ @@ -84,7 +84,7 @@ for(var/cultists_number = 1 to recommended_enemies) if(!antag_candidates.len) break - var/datum/mind/cultist = antag_pick(antag_candidates, ROLE_CULTIST) + var/datum/mind/cultist = antag_pick(antag_candidates, /datum/role_preference/antagonist/blood_cultist) antag_candidates -= cultist if(!cultist) cultists_number-- diff --git a/code/game/gamemodes/devil/devil_game_mode.dm b/code/game/gamemodes/devil/devil_game_mode.dm index b7d0bbe7766..95b7ab5db67 100644 --- a/code/game/gamemodes/devil/devil_game_mode.dm +++ b/code/game/gamemodes/devil/devil_game_mode.dm @@ -2,14 +2,14 @@ name = "devil" config_tag = "devil" report_type = "devil" - antag_flag = ROLE_DEVIL + role_preference = /datum/role_preference/antagonist/devil + antag_datum = /datum/antagonist/devil false_report_weight = 1 protected_jobs = list(JOB_NAME_LAWYER, JOB_NAME_CURATOR, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE) required_players = 0 required_enemies = 1 recommended_enemies = 4 reroll_friendly = 1 - enemy_minimum_age = 0 title_icon = "devil" allowed_special = list(/datum/special_role/traitor) @@ -42,7 +42,7 @@ for(var/j = 0, j < num_devils, j++) if (!antag_candidates.len) break - var/datum/mind/devil = antag_pick(antag_candidates, ROLE_DEVIL) + var/datum/mind/devil = antag_pick(antag_candidates, /datum/role_preference/antagonist/devil) devils += devil devil.special_role = traitor_name devil.restricted_roles = restricted_jobs diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 8e07f35c244..de004e48a54 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -699,15 +699,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) log_game("DYNAMIC: [src] passed lowpop_lowimpact requirement: ([living_antags_count] antags of [living_players_count] players - [antag_percent]%)") return FALSE -/// Checks if client age is age or older. -/datum/game_mode/dynamic/proc/check_age(client/C, age) - enemy_minimum_age = age - if(get_remaining_days(C) == 0) - enemy_minimum_age = initial(enemy_minimum_age) - return TRUE // Available in 0 days = available right now = player is old enough to play. - enemy_minimum_age = initial(enemy_minimum_age) - return FALSE - /datum/game_mode/dynamic/make_antag_chance(mob/living/carbon/human/newPlayer) if (GLOB.dynamic_forced_extended) return diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm index 3179f32c974..54d038e1447 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm @@ -18,12 +18,10 @@ var/list/mob/candidates = list() /// List of players that were selected for this rule var/list/datum/mind/assigned = list() - /// Preferences flag such as ROLE_WIZARD that need to be turned on for players to be antag - var/antag_flag = null + /// The /datum/role_preference typepath used for this ruleset. + var/role_preference = null /// The antagonist datum that is assigned to the mobs mind on ruleset execution. var/datum/antagonist/antag_datum = null - /// The required minimum account age for this ruleset. - var/minimum_required_age = 7 /// If set, and config flag protect_roles_from_antagonist is false, then the rule will not pick players from these roles. var/list/protected_roles = list() /// If set, rule will deny candidates from those roles always. @@ -55,8 +53,6 @@ var/list/requirements = list(40,30,20,10,10,10,10,10,10,10) /// Reference to the mode, use this instead of SSticker.mode. var/datum/game_mode/dynamic/mode = null - /// If a role is to be considered another for the purpose of banning. - var/antag_flag_override = null /// If a ruleset type which is in this list has been executed, then the ruleset will not be executed. var/list/blocking_rules = list() /// The minimum amount of players required for the rule to be considered. @@ -213,19 +209,19 @@ var/client/client = GET_CLIENT(P) if (!client || !P.mind) // Are they connected? candidates.Remove(P) - else if(!mode.check_age(client, minimum_required_age)) + continue + + if(!client.should_include_for_role( + banning_key = initial(antag_datum.banning_key), + role_preference_key = role_preference, + req_hours = initial(antag_datum.required_living_playtime) + )) candidates.Remove(P) continue + if(P.mind.special_role) // We really don't want to give antag to an antag. candidates.Remove(P) - else if(antag_flag_override) - if(!(antag_flag_override in client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))) - candidates.Remove(P) - continue - else - if(!(antag_flag in client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE))) - candidates.Remove(P) - continue + continue /// Do your checks if the ruleset is ready to be executed here. /// Should ignore certain checks if forced is TRUE diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index 375d1163e56..74cf8577ef0 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -9,21 +9,15 @@ if (!P.client || !P.mind || !P.mind.assigned_role) // Are they connected? candidates.Remove(P) continue - if(!mode.check_age(P.client, minimum_required_age)) - candidates.Remove(P) - continue - if(antag_flag_override) - if(!(antag_flag_override in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))) - candidates.Remove(P) - continue - else - if(!(antag_flag in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE))) - candidates.Remove(P) - continue if (P.mind.assigned_role in restricted_roles) // Does their job allow for it? candidates.Remove(P) - continue - if ((exclusive_roles.len > 0) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job? + else if(length(exclusive_roles) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job? + candidates.Remove(P) + else if(!P.client.should_include_for_role( + banning_key = initial(antag_datum.banning_key), + role_preference_key = role_preference, + req_hours = initial(antag_datum.required_living_playtime) + )) candidates.Remove(P) continue @@ -53,7 +47,7 @@ /datum/dynamic_ruleset/latejoin/execute() var/mob/M = pick(candidates) assigned += M.mind - M.mind.special_role = antag_flag + M.mind.special_role = initial(antag_datum.banning_key) M.mind.add_antag_datum(antag_datum) return TRUE @@ -66,7 +60,7 @@ /datum/dynamic_ruleset/latejoin/infiltrator name = "Syndicate Infiltrator" antag_datum = /datum/antagonist/traitor - antag_flag = ROLE_TRAITOR + role_preference = /datum/role_preference/antagonist/traitor protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL) restricted_roles = list(JOB_NAME_AI,JOB_NAME_CYBORG) required_candidates = 1 @@ -92,8 +86,7 @@ name = "Provocateur" persistent = TRUE antag_datum = /datum/antagonist/rev/head - antag_flag = ROLE_REV_HEAD - antag_flag_override = ROLE_REV + role_preference = /datum/role_preference/antagonist/revolutionary restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFENGINEER, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_MASTERATARMS) //NSV13 - added MAA enemy_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER,JOB_NAME_DETECTIVE,JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_WARDEN) required_enemies = list(2,2,1,1,1,1,1,0,0,0) @@ -125,7 +118,7 @@ var/mob/M = pick(candidates) // This should contain a single player, but in case. if(check_eligible(M.mind)) // Didnt die/run off z-level/get implanted since leaving shuttle. assigned += M.mind - M.mind.special_role = antag_flag + M.mind.special_role = ROLE_REV_HEAD revolution = new() var/datum/antagonist/rev/head/new_head = new() new_head.give_flash = TRUE @@ -168,7 +161,7 @@ /datum/dynamic_ruleset/latejoin/heretic_smuggler name = "Heretic Smuggler" antag_datum = /datum/antagonist/heretic - antag_flag = ROLE_HERETIC + role_preference = /datum/role_preference/antagonist/heretic protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_AI,JOB_NAME_CYBORG) required_candidates = 1 diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 9aa65b5b481..0c6c3579dd2 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -51,17 +51,14 @@ if (!M.client) // Are they connected? trimmed_list.Remove(M) continue - if(!mode.check_age(M.client, minimum_required_age)) + if(!M.client.should_include_for_role( + banning_key = initial(antag_datum.banning_key), + role_preference_key = role_preference, + poll_ignore_key = role_preference, + req_hours = initial(antag_datum.required_living_playtime) + )) trimmed_list.Remove(M) continue - if(antag_flag_override) - if(!(antag_flag_override in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag_override, ROLE_SYNDICATE))) - trimmed_list.Remove(M) - continue - else - if(!(antag_flag in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag, ROLE_SYNDICATE))) - trimmed_list.Remove(M) - continue if (M.mind) if (restrict_ghost_roles && (M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role? trimmed_list.Remove(M) @@ -114,7 +111,7 @@ message_admins("Polling [possible_volunteers.len] players to apply for the [name] ruleset.") log_game("DYNAMIC: Polling [possible_volunteers.len] players to apply for the [name] ruleset.") - candidates = pollGhostCandidates("The mode is looking for volunteers to become [antag_flag] for [name]", antag_flag, SSticker.mode, antag_flag_override ? antag_flag_override : antag_flag, poll_time = 300) + candidates = pollGhostCandidates("The mode is looking for volunteers to become [initial(antag_datum.name)] for [name]", initial(antag_datum.banning_key), role_preference, poll_time = 300) if(!length(candidates)) message_admins("The ruleset [name] received no applications.") @@ -170,7 +167,7 @@ var/datum/antagonist/new_role = new antag_datum() setup_role(new_role) new_character.mind.add_antag_datum(new_role) - new_character.mind.special_role = antag_flag + new_character.mind.special_role = new_role.banning_key /datum/dynamic_ruleset/midround/from_ghosts/proc/setup_role(datum/antagonist/new_role) return @@ -185,7 +182,7 @@ name = "Syndicate Sleeper Agent" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT antag_datum = /datum/antagonist/traitor - antag_flag = ROLE_TRAITOR + role_preference = /datum/role_preference/midround_living/traitor protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_CYBORG, JOB_NAME_AI, "Positronic Brain") required_candidates = 1 @@ -239,7 +236,7 @@ name = "Malfunctioning AI" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/traitor - antag_flag = ROLE_MALF + role_preference = /datum/role_preference/midround_living/malfunctioning_ai enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_SCIENTIST, JOB_NAME_CHEMIST, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFENGINEER) exclusive_roles = list(JOB_NAME_AI) required_enemies = list(4,4,4,4,4,4,2,2,2,0) @@ -274,7 +271,7 @@ var/mob/living/silicon/ai/M = pick_n_take(candidates) assigned += M.mind var/datum/antagonist/traitor/AI = new - M.mind.special_role = antag_flag + M.mind.special_role = "Malf AI" M.mind.add_antag_datum(AI) if(prob(ion_announce)) priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", ANNOUNCER_IONSTORM) @@ -294,7 +291,7 @@ name = "Wizard" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/wizard - antag_flag = ROLE_WIZARD + role_preference = /datum/role_preference/midround_ghost/wizard enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_RESEARCHDIRECTOR) //RD doesn't believe in magic required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 @@ -325,7 +322,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/nuclear name = "Nuclear Assault" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY - antag_flag = ROLE_OPERATIVE + role_preference = /datum/role_preference/midround_ghost/nuclear_operative antag_datum = /datum/antagonist/nukeop enemy_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(3,3,3,3,3,2,1,1,0,0) @@ -351,8 +348,8 @@ return ..() /datum/dynamic_ruleset/midround/from_ghosts/nuclear/finish_setup(mob/new_character, index) - new_character.mind.special_role = "Nuclear Operative" - new_character.mind.assigned_role = "Nuclear Operative" + new_character.mind.special_role = ROLE_OPERATIVE + new_character.mind.assigned_role = ROLE_OPERATIVE if (index == 1) // Our first guy is the leader var/datum/antagonist/nukeop/leader/new_role = new nuke_team = new_role.nuke_team @@ -370,7 +367,7 @@ name = "Blob" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/blob - antag_flag = ROLE_BLOB + role_preference = /datum/role_preference/midround_ghost/blob enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 @@ -394,7 +391,7 @@ name = "Alien Infestation" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY antag_datum = /datum/antagonist/xeno - antag_flag = ROLE_ALIEN + role_preference = /datum/role_preference/midround_ghost/xenomorph enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,2,1,1,1,1,0,0,0) required_candidates = 1 @@ -442,8 +439,7 @@ name = "Nightmare" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT antag_datum = /datum/antagonist/nightmare - antag_flag = "Nightmare" - antag_flag_override = ROLE_ALIEN + role_preference = /datum/role_preference/midround_ghost/space_dragon enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 @@ -490,8 +486,7 @@ /datum/dynamic_ruleset/midround/from_ghosts/abductors name = "Abductors" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY - antag_flag = "Abductor" - antag_flag_override = ROLE_ABDUCTOR + role_preference = /datum/role_preference/midround_ghost/abductor enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 2 @@ -529,8 +524,7 @@ name = "Revenant" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT antag_datum = /datum/antagonist/revenant - antag_flag = "Revenant" - antag_flag_override = ROLE_REVENANT + role_preference = /datum/role_preference/midround_ghost/revenant enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 @@ -581,7 +575,7 @@ /datum/dynamic_ruleset/midround/pirates name = "Space Pirates" midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY - antag_flag = "Space Pirates" + role_preference = /datum/role_preference/midround_ghost/space_pirate required_type = /mob/dead/observer enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,1,1,1,1,1,0,0,0) @@ -608,7 +602,7 @@ name = "Obsessed" midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT antag_datum = /datum/antagonist/obsessed - antag_flag = ROLE_OBSESSED + role_preference = /datum/role_preference/midround_living/obsessed restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, "Positronic Brain") enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) required_enemies = list(2,2,1,1,1,1,1,0,0,0) @@ -625,7 +619,6 @@ !candidate.getorgan(/obj/item/organ/brain) \ || candidate.mind.has_antag_datum(/datum/antagonist/obsessed) \ || candidate.stat == DEAD \ - || !(ROLE_OBSESSED in candidate.client?.prefs?.be_special) \ || !SSjob.GetJob(candidate.mind.assigned_role) \ || (candidate.mind.assigned_role in GLOB.nonhuman_positions) \ ) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 569319b8cf8..fe384067216 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -7,9 +7,8 @@ /datum/dynamic_ruleset/roundstart/traitor name = "Traitors" - antag_flag = ROLE_TRAITOR + role_preference = /datum/role_preference/antagonist/traitor antag_datum = /datum/antagonist/traitor - minimum_required_age = 0 protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_CYBORG) required_candidates = 1 @@ -41,8 +40,8 @@ /datum/dynamic_ruleset/roundstart/traitorbro name = "Blood Brothers" - antag_flag = ROLE_BROTHER - antag_datum = /datum/antagonist/brother/ + role_preference = /datum/role_preference/antagonist/blood_brother + antag_datum = /datum/antagonist/brother protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG) required_candidates = 2 @@ -66,7 +65,7 @@ var/mob/bro = pick_n_take(candidates) assigned += bro.mind team.add_member(bro.mind) - bro.mind.special_role = "brother" + bro.mind.special_role = ROLE_BROTHER bro.mind.restricted_roles = restricted_roles pre_brother_teams += team return TRUE @@ -89,7 +88,7 @@ /datum/dynamic_ruleset/roundstart/changeling name = "Changelings" - antag_flag = ROLE_CHANGELING + role_preference = /datum/role_preference/antagonist/changeling antag_datum = /datum/antagonist/changeling protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -126,7 +125,7 @@ /datum/dynamic_ruleset/roundstart/heretics name = "Heretics" - antag_flag = ROLE_HERETIC + role_preference = /datum/role_preference/antagonist/heretic antag_datum = /datum/antagonist/heretic protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -170,10 +169,9 @@ // Dynamic is a wonderful thing that adds wizards to every round and then adds even more wizards during the round. /datum/dynamic_ruleset/roundstart/wizard name = "Wizard" - antag_flag = ROLE_WIZARD + role_preference = /datum/role_preference/antagonist/wizard antag_datum = /datum/antagonist/wizard flags = HIGH_IMPACT_RULESET | NO_OTHER_ROUNDSTARTS_RULESET - minimum_required_age = 14 restricted_roles = list(JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted required_candidates = 1 weight = 2 @@ -214,9 +212,8 @@ /datum/dynamic_ruleset/roundstart/bloodcult name = "Blood Cult" - antag_flag = ROLE_CULTIST + role_preference = /datum/role_preference/antagonist/blood_cultist antag_datum = /datum/antagonist/cult - minimum_required_age = 14 restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFPERSONNEL) required_candidates = 2 weight = 3 @@ -269,10 +266,9 @@ /datum/dynamic_ruleset/roundstart/nuclear name = "Nuclear Emergency" - antag_flag = ROLE_OPERATIVE + role_preference = /datum/role_preference/antagonist/nuclear_operative antag_datum = /datum/antagonist/nukeop var/datum/antagonist/antag_leader_datum = /datum/antagonist/nukeop/leader - minimum_required_age = 14 restricted_roles = list(JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) // Just to be sure that a nukie getting picked won't ever imply a Captain or HoS not getting drafted required_candidates = 5 weight = 3 @@ -295,8 +291,8 @@ break var/mob/M = pick_n_take(candidates) assigned += M.mind - M.mind.assigned_role = "Nuclear Operative" - M.mind.special_role = "Nuclear Operative" + M.mind.assigned_role = ROLE_OPERATIVE + M.mind.special_role = ROLE_OPERATIVE return TRUE /datum/dynamic_ruleset/roundstart/nuclear/execute() @@ -354,10 +350,8 @@ /datum/dynamic_ruleset/roundstart/revs name = "Revolution" persistent = TRUE - antag_flag = ROLE_REV_HEAD - antag_flag_override = ROLE_REV + role_preference = /datum/role_preference/antagonist/revolutionary antag_datum = /datum/antagonist/rev/head - minimum_required_age = 14 restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFENGINEER, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_MASTERATARMS) //NSV13 - added MAA required_candidates = 3 weight = 3 @@ -383,7 +377,7 @@ var/mob/M = pick_n_take(candidates) assigned += M.mind M.mind.restricted_roles = restricted_roles - M.mind.special_role = antag_flag + M.mind.special_role = ROLE_REV_HEAD return TRUE /datum/dynamic_ruleset/roundstart/revs/execute() @@ -437,7 +431,6 @@ /datum/dynamic_ruleset/roundstart/extended name = "Extended" - antag_flag = null antag_datum = null restricted_roles = list() required_candidates = 0 @@ -486,7 +479,7 @@ /datum/dynamic_ruleset/roundstart/devil name = "Devil" - antag_flag = ROLE_DEVIL + role_preference = /datum/role_preference/antagonist/devil antag_datum = /datum/antagonist/devil restricted_roles = list(JOB_NAME_LAWYER, JOB_NAME_CURATOR, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE) required_candidates = 1 @@ -574,7 +567,7 @@ /datum/dynamic_ruleset/roundstart/clockcult name = "Clockwork Cult" - antag_flag = ROLE_SERVANT_OF_RATVAR + role_preference = /datum/role_preference/antagonist/clock_cultist antag_datum = /datum/antagonist/servant_of_ratvar restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFPERSONNEL) required_candidates = 4 @@ -636,7 +629,7 @@ /datum/dynamic_ruleset/roundstart/incursion name = "Incursion" - antag_flag = ROLE_INCURSION + role_preference = /datum/role_preference/antagonist/incursionist antag_datum = /datum/antagonist/incursion protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER) restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -688,7 +681,7 @@ /datum/dynamic_ruleset/roundstart/hivemind name = "Assimilation" - antag_flag = ROLE_HIVE + role_preference = /datum/role_preference/antagonist/hivemind_host antag_datum = /datum/antagonist/hivemind protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG) diff --git a/code/game/gamemodes/dynamic/dynamic_simulations.dm b/code/game/gamemodes/dynamic/dynamic_simulations.dm index 4b48955ce75..acba5d1cc42 100644 --- a/code/game/gamemodes/dynamic/dynamic_simulations.dm +++ b/code/game/gamemodes/dynamic/dynamic_simulations.dm @@ -29,11 +29,6 @@ var/datum/client_interface/mock_client = new var/datum/preferences/prefs = new - var/list/be_special = list() - for (var/special_role in GLOB.special_roles) - be_special += special_role - - prefs.be_special = be_special mock_client.prefs = prefs mock_new_player.mock_client = mock_client diff --git a/code/game/gamemodes/dynamic/readme.md b/code/game/gamemodes/dynamic/readme.md index 7faa5c2d870..9af317301d8 100644 --- a/code/game/gamemodes/dynamic/readme.md +++ b/code/game/gamemodes/dynamic/readme.md @@ -144,7 +144,6 @@ Rulesets have the following variables notable to developers and those interested - Traitor: `antag_cap = list("denominator" = 24)`. This means that for every 24 players, 1 traitor will be added (assuming no scaling). - Nuclear Emergency: `antag_cap = list("denominator" = 18, "offset" = 1)`. For every 18 players, 1 nuke op will be added. Starts at 1, meaning at 30 players, 3 nuke ops will be created, rather than 2. - Revolution: `antag_cap = 3`. There will always be 3 rev-heads, no matter what. -- `minimum_required_age` - The minimum age in order to apply for the ruleset. - `weight` - How likely this ruleset is to be picked. A higher weight results in a higher chance of drafting. - `cost` - The initial cost of the ruleset. This cost is taken from either the roundstart or midround budget, depending on the ruleset. - `scaling_cost` - Cost for every *additional* application of this ruleset. diff --git a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm index 307b1be0893..c169e11ae73 100644 --- a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm +++ b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm @@ -2,7 +2,8 @@ name = "heresy" config_tag = "heresy" report_type = "heresy" - antag_flag = ROLE_HERETIC + role_preference = /datum/role_preference/antagonist/heretic + antag_datum = /datum/antagonist/heretic false_report_weight = 5 protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -10,7 +11,6 @@ required_enemies = 1 recommended_enemies = 4 reroll_friendly = 1 - enemy_minimum_age = 0 allowed_special = list(/datum/special_role/traitor/higher_chance) @@ -44,7 +44,7 @@ for(var/i in 1 to num_ecult) if(!antag_candidates.len) break - var/datum/mind/cultie = antag_pick(antag_candidates, ROLE_HERETIC) + var/datum/mind/cultie = antag_pick(antag_candidates, /datum/role_preference/antagonist/heretic) antag_candidates -= cultie cultie.special_role = ROLE_HERETIC cultie.restricted_roles = restricted_jobs diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 03300874083..80576b5813e 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -28,13 +28,15 @@ var/maximum_players = -1 // -1 is no maximum, positive numbers limit the selection of a mode on overstaffed stations var/required_enemies = 0 var/recommended_enemies = 0 - var/antag_flag = null //preferences flag such as BE_WIZARD that need to be turned on for players to be antag + /// The role preference used to poll players. + var/role_preference + /// The antag datum typepath primarily spawned by this gamemode. Used for checking the banning key and required playtime. + var/datum/antagonist/antag_datum var/mob/living/living_antag_player = null var/datum/game_mode/replacementmode = null var/round_converted = 0 //0: round not converted, 1: round going to convert, 2: round converted var/reroll_friendly //During mode conversion only these are in the running var/continuous_sanity_checked //Catches some cases where config options could be used to suggest that modes without antagonists should end when all antagonists die - var/enemy_minimum_age = 7 //How many days must players have been playing before they can play this antagonist var/list/allowed_special = list() //Special roles that can spawn (Add things like /datum/antagonist/special/undercover for them to be able to spawn during this gamemode) var/list/active_specials = list() //Special roles that have spawned, and can now spawn late @@ -87,7 +89,7 @@ return 1 /datum/game_mode/proc/setup_antag_candidates() - antag_candidates = get_players_for_role(antag_flag) + antag_candidates = get_players_for_role(antag_datum, role_preference) ///Attempts to select players for special roles the mode might have. /datum/game_mode/proc/pre_setup() @@ -121,11 +123,11 @@ if(candidates.len == 0) return //No more candidates, end the selection process, and active specials at this time will be handled by latejoins or not included var/mob/person - if(special.special_role_flag) - person = antag_pick(candidates, special.special_role_flag) + if(special.use_antag_rep) + person = antag_pick(candidates, special.preference_type) else person = pick_n_take(candidates) - if(is_banned_from(person.ckey, special.preference_type)) + if(is_banned_from(person.ckey, special.banning_key)) continue if(!person) continue @@ -205,7 +207,7 @@ continue if(!is_special_type(M, subantag.attached_antag_datum)) continue - if(is_banned_from(M.ckey, list(subantag.preference_type))) + if(is_banned_from(M.ckey, subantag.banning_key)) continue count++ if(count >= subantag.max_amount) @@ -256,7 +258,7 @@ var/list/antag_candidates = list() for(var/mob/living/carbon/human/H in living_crew) - if(H.client && (H.client.prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG) && !is_centcom_level(H.z)) + if(H.client && !is_centcom_level(H.z)) antag_candidates += H if(!antag_candidates) @@ -429,7 +431,9 @@ // The odds become: // Player A: 150 / 250 = 0.6 = 60% // Player B: 100 / 250 = 0.4 = 40% -/datum/game_mode/proc/antag_pick(list/datum/candidates, role) +/// The role_preference argument is optional, but candidates will not use their PERSONAL antag rep if the preference is disabled, rather only using the "base" antag rep. +/// This is mainly used in the situation where someone is drafted for a ruleset despite having the preference disabled (a feature of gamemodes) - we don't want to spend their rep. +/datum/game_mode/proc/antag_pick(list/datum/candidates, role_preference = null) if(!CONFIG_GET(flag/use_antag_rep)) // || candidates.len <= 1) return pick(candidates) @@ -452,7 +456,10 @@ if(!player) candidates -= mind continue - total_tickets += min(((role in player.client.prefs.be_special) ? SSpersistence.antag_rep[p_ckey] : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL) + var/role_enabled = TRUE + if(role_preference && player.client) + role_enabled = player.client.role_preference_enabled(role_preference) + total_tickets += min((role_enabled ? SSpersistence.antag_rep[p_ckey] : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL) var/antag_select = rand(1,total_tickets) var/current = 1 @@ -461,9 +468,11 @@ p_ckey = ckey(mind.key) var/mob/dead/new_player/player = get_mob_by_ckey(p_ckey) p_rep = SSpersistence.antag_rep[p_ckey] - + var/role_enabled = TRUE + if(role_preference && player.client) + role_enabled = player.client.role_preference_enabled(role_preference) var/previous = current - var/spend = min(((role in player.client.prefs.be_special) ? p_rep : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL) + var/spend = min((role_enabled ? p_rep : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL) current += spend if(antag_select >= previous && antag_select <= (current-1)) @@ -474,7 +483,9 @@ WARNING("Something has gone terribly wrong. /datum/game_mode/proc/antag_pick failed to select a candidate. Falling back to pick()") return pick(candidates) -/datum/game_mode/proc/get_players_for_role(role) +/datum/game_mode/proc/get_players_for_role(datum/antagonist/antag_datum, role_preference = null) + var/banning_key = ispath(antag_datum, /datum/antagonist) ? initial(antag_datum.banning_key) : null + var/req_hours = ispath(antag_datum, /datum/antagonist) ? initial(antag_datum.required_living_playtime) : 0 var/list/players = list() var/list/candidates = list() var/list/drafted = list() @@ -490,11 +501,13 @@ players = shuffle(players) for(var/mob/dead/new_player/player in players) - if(player.client && player.ready == PLAYER_READY_TO_PLAY) - if(role in player.client.prefs.be_special) - if(!is_banned_from(player.ckey, list(role, ROLE_SYNDICATE)) && !QDELETED(player)) - if(age_check(player.client)) //Must be older than the minimum age - candidates += player.mind // Get a list of all the people who want to be the antagonist for this round + if(!QDELETED(player) && player.client && player.ready == PLAYER_READY_TO_PLAY) + if(player.client.should_include_for_role( + banning_key = banning_key, + role_preference_key = role_preference, + req_hours = req_hours + )) + candidates += player.mind // Get a list of all the people who want to be the antagonist for this round if(restricted_jobs) for(var/datum/mind/player in candidates) @@ -504,10 +517,14 @@ if(candidates.len < recommended_enemies) for(var/mob/dead/new_player/player in players) - if(player.client && player.ready == PLAYER_READY_TO_PLAY) - if(!(role in player.client.prefs.be_special)) // We don't have enough people who want to be antagonist, make a separate list of people who don't want to be one - if(!is_banned_from(player.ckey, list(role, ROLE_SYNDICATE)) && !QDELETED(player)) - drafted += player.mind + if(!QDELETED(player) && player.client && player.ready == PLAYER_READY_TO_PLAY) + // We don't have enough people who want to be antagonist, make a separate list of people who don't want to be one but are otherwise eligible + if(player.client.should_include_for_role( + banning_key = banning_key, + role_preference_key = null, + req_hours = req_hours + ) && !player.client.role_preference_enabled(role_preference)) + drafted += player.mind if(restricted_jobs) for(var/datum/mind/player in drafted) // Remove people who can't be an antagonist @@ -550,15 +567,15 @@ // Less if there are not enough valid players in the game entirely to make recommended_enemies. -/datum/game_mode/proc/get_alive_non_antagonsist_players_for_role(role, list/restricted_roles) +/datum/game_mode/proc/get_alive_non_antagonsist_players_for_role(datum/antagonist/antag_datum, role_preference, list/restricted_roles) + var/banning_key = ispath(antag_datum, /datum/antagonist) ? initial(antag_datum.banning_key) : null + var/req_hours = ispath(antag_datum, /datum/antagonist) ? initial(antag_datum.required_living_playtime) : 0 var/list/candidates = list() for(var/mob/living/carbon/human/player in GLOB.player_list) - if(player.client && is_station_level(player.z)) - if(role in player.client.prefs.be_special) - if(!is_banned_from(player.ckey, list(role, ROLE_SYNDICATE)) && !QDELETED(player)) - if(age_check(player.client) && !player.mind.special_role) //Must be older than the minimum age - candidates += player.mind // Get a list of all the people who want to be the antagonist for this round + if(!QDELETED(player) && player.client && is_station_level(player.z) && !player.mind.special_role) + if(player.client.should_include_for_role(banning_key = banning_key, role_preference_key = role_preference, req_hours = req_hours)) + candidates += player.mind // Get a list of all the people who want to be the antagonist for this round // Get a list of all the people who want to be the antagonist for this round var/restricted_list = length(restricted_roles) ? restricted_roles : restricted_jobs if(restricted_list) @@ -728,25 +745,6 @@ for (var/C in GLOB.admins) to_chat(C, msg.Join()) -//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 -/datum/game_mode/proc/age_check(client/C) - if(get_remaining_days(C) == 0) - return 1 //Available in 0 days = available right now = player is old enough to play. - return 0 - - -/datum/game_mode/proc/get_remaining_days(client/C) - if(!C) - return 0 - if(!CONFIG_GET(flag/use_age_restriction_for_jobs)) - return 0 - if(!isnum_safe(C.player_age)) - return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced - if(!isnum_safe(enemy_minimum_age)) - return 0 - - return max(0, enemy_minimum_age - C.player_age) - /datum/game_mode/proc/remove_antag_for_borging(datum/mind/newborgie) SSticker.mode.remove_cultist(newborgie, 0, 0) remove_servant_of_ratvar(newborgie) diff --git a/code/game/gamemodes/gangs/gangs.dm b/code/game/gamemodes/gangs/gangs.dm index 7d1187ef1d9..f0e9536cdae 100644 --- a/code/game/gamemodes/gangs/gangs.dm +++ b/code/game/gamemodes/gangs/gangs.dm @@ -5,12 +5,12 @@ GLOBAL_LIST_EMPTY(gangs) /datum/game_mode/gang name = "gang war" config_tag = "gang" - antag_flag = ROLE_GANG + role_preference = /datum/role_preference/antagonist/gangster + antag_datum = /datum/antagonist/gang restricted_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_AI, JOB_NAME_CYBORG,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY) required_players = 15 //NSV13 - down from 30 required_enemies = 1 //NSV13 - down from 2 recommended_enemies = 2 //NSV13 - down from 3 - enemy_minimum_age = 14 announce_span = "danger" announce_text = "A violent turf war has erupted on the station!\n\ diff --git a/code/game/gamemodes/hivemind/hivemind.dm b/code/game/gamemodes/hivemind/hivemind.dm index 4fa0b6095c0..90bf74f93cc 100644 --- a/code/game/gamemodes/hivemind/hivemind.dm +++ b/code/game/gamemodes/hivemind/hivemind.dm @@ -4,7 +4,8 @@ GLOBAL_LIST_EMPTY(hivehosts) name = "assimilation" config_tag = "hivemind" report_type = "hivemind" - antag_flag = ROLE_HIVE + role_preference = /datum/role_preference/antagonist/hivemind_host + antag_datum = /datum/antagonist/hivemind false_report_weight = 5 protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG) @@ -12,7 +13,6 @@ GLOBAL_LIST_EMPTY(hivehosts) required_enemies = 3 recommended_enemies = 3 reroll_friendly = 1 - enemy_minimum_age = 0 announce_span = "danger" announce_text = "The hosts of several psionic hiveminds have infiltrated the station and are looking to assimilate the crew!\n\ @@ -62,7 +62,7 @@ GLOBAL_LIST_EMPTY(hivehosts) for(var/j = 0, j < num_hosts, j++) if (!antag_candidates.len) break - var/datum/mind/host = antag_pick(antag_candidates, ROLE_HIVE) + var/datum/mind/host = antag_pick(antag_candidates, /datum/role_preference/antagonist/hivemind_host) hosts += host host.special_role = ROLE_HIVE host.restricted_roles = restricted_jobs diff --git a/code/game/gamemodes/incursion/incursion.dm b/code/game/gamemodes/incursion/incursion.dm index adf8979f516..c5324e52111 100644 --- a/code/game/gamemodes/incursion/incursion.dm +++ b/code/game/gamemodes/incursion/incursion.dm @@ -7,9 +7,9 @@ config_tag = "incursion" restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG) protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_MASTERATARMS) //NSV13 - added MAA - antag_flag = ROLE_INCURSION + role_preference = /datum/role_preference/antagonist/incursionist + antag_datum = /datum/antagonist/incursion false_report_weight = 10 - enemy_minimum_age = 0 announce_span = "danger" announce_text = "A large force of syndicate operatives have infiltrated the ranks of the station and wish to take it by force!\n\ @@ -30,8 +30,6 @@ if(CONFIG_GET(flag/protect_assistant_from_antagonist)) restricted_jobs += JOB_NAME_ASSISTANT - var/list/datum/mind/possible_traitors = get_players_for_role(ROLE_INCURSION) - var/datum/team/incursion/team = new var/cost_base = CONFIG_GET(number/incursion_cost_base) var/cost_increment = CONFIG_GET(number/incursion_cost_increment) @@ -41,11 +39,10 @@ team_size = CLAMP(team_size, CONFIG_GET(number/incursion_count_min), CONFIG_GET(number/incursion_count_max)) for(var/k = 1 to team_size) - var/datum/mind/incursion = antag_pick(possible_traitors, ROLE_INCURSION) + var/datum/mind/incursion = antag_pick(antag_candidates, /datum/role_preference/antagonist/incursionist) if(!incursion) message_admins("Ran out of people to put in an incursion team, wanted [team_size] but only got [k-1]") break - possible_traitors -= incursion antag_candidates -= incursion team.add_member(incursion) incursion.special_role = ROLE_INCURSION diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 281f3507fe3..9d2880be163 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -8,8 +8,8 @@ required_players = 30 // 30 players - 3 players to be the nuke ops = 27 players remaining required_enemies = 2 recommended_enemies = 5 - antag_flag = ROLE_OPERATIVE - enemy_minimum_age = 14 + role_preference = /datum/role_preference/antagonist/nuclear_operative + antag_datum = /datum/antagonist/nukeop announce_span = "danger" announce_text = "Syndicate forces are approaching the station in an attempt to destroy it!\n\ @@ -31,7 +31,7 @@ var/n_agents = min(round(num_players() / 10), antag_candidates.len, agents_possible) if(n_agents >= required_enemies) for(var/i = 0, i < n_agents, ++i) - var/datum/mind/new_op = antag_pick(antag_candidates, ROLE_OPERATIVE) + var/datum/mind/new_op = antag_pick(antag_candidates, /datum/role_preference/antagonist/nuclear_operative) pre_nukeops += new_op new_op.assigned_role = "Nuclear Operative" new_op.special_role = "Nuclear Operative" @@ -160,7 +160,7 @@ E.implant(H) var/obj/item/implant/weapons_auth/W = new/obj/item/implant/weapons_auth(H) W.implant(H) - H.faction |= ROLE_SYNDICATE + H.faction |= FACTION_SYNDICATE H.update_icons() /datum/outfit/syndicate/full diff --git a/code/game/gamemodes/overthrow/overthrow.dm b/code/game/gamemodes/overthrow/overthrow.dm index 0783c862f5e..9fd0cd3863e 100644 --- a/code/game/gamemodes/overthrow/overthrow.dm +++ b/code/game/gamemodes/overthrow/overthrow.dm @@ -3,7 +3,8 @@ name = "overthrow" config_tag = "overthrow" report_type = "overthrow" - antag_flag = ROLE_OVERTHROW + role_preference = /datum/role_preference/antagonist/traitor // use traitor role pref + antag_datum = /datum/antagonist/overthrow restricted_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_AI, JOB_NAME_CYBORG,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_MASTERATARMS) //NSV13 - added MAA required_players = 20 // the core idea is of a swift, bloodless coup, so it shouldn't be as chaotic as revs. required_enemies = 2 // minimum two teams, otherwise it's just nerfed revs. @@ -28,7 +29,7 @@ for (var/i in 1 to sleeping_agents) if (!antag_candidates.len) break - var/datum/mind/sleeping_agent = antag_pick(antag_candidates, ROLE_OVERTHROW) + var/datum/mind/sleeping_agent = antag_pick(antag_candidates, /datum/role_preference/antagonist/traitor) antag_candidates -= sleeping_agent initial_agents += sleeping_agent sleeping_agent.restricted_roles = restricted_jobs diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 0ff58d3c82b..a635784064a 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -11,14 +11,14 @@ name = "revolution" config_tag = "revolution" report_type = "revolution" - antag_flag = ROLE_REV + role_preference = /datum/role_preference/antagonist/revolutionary + antag_datum = /datum/antagonist/rev/head false_report_weight = 10 restricted_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_AI, JOB_NAME_CYBORG,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_MASTERATARMS) //NSV13 - added MAA required_jobs = list(list(JOB_NAME_CAPTAIN=1),list(JOB_NAME_HEADOFPERSONNEL=1),list(JOB_NAME_HEADOFSECURITY=1),list(JOB_NAME_CHIEFENGINEER=1),list(JOB_NAME_RESEARCHDIRECTOR=1),list(JOB_NAME_CHIEFMEDICALOFFICER=1),list(JOB_NAME_MASTERATARMS=1)) //Any head present //NSV13 - added MAA required_players = 30 required_enemies = 2 recommended_enemies = 3 - enemy_minimum_age = 14 title_icon = "revolution" announce_span = "danger" @@ -55,7 +55,7 @@ for (var/i=1 to max_headrevs) if (antag_candidates.len==0) break - var/datum/mind/lenin = antag_pick(antag_candidates, ROLE_REV_HEAD) + var/datum/mind/lenin = antag_pick(antag_candidates, /datum/role_preference/antagonist/revolutionary) antag_candidates -= lenin headrev_candidates += lenin lenin.restricted_roles = restricted_jobs diff --git a/code/game/gamemodes/traitor/double_agents.dm b/code/game/gamemodes/traitor/double_agents.dm index 7e3db6e86c4..b5b6f9bbecc 100644 --- a/code/game/gamemodes/traitor/double_agents.dm +++ b/code/game/gamemodes/traitor/double_agents.dm @@ -12,11 +12,11 @@ recommended_enemies = 8 reroll_friendly = 0 traitor_name = "Nanotrasen Internal Affairs Agent" - antag_flag = ROLE_INTERNAL_AFFAIRS + antag_datum = /datum/antagonist/traitor/internal_affairs + role_preference = /datum/role_preference/antagonist/internal_affairs traitors_possible = 10 //hard limit on traitors if scaling is turned off num_modifier = 4 // Four additional traitors - antag_datum = /datum/antagonist/traitor/internal_affairs announce_text = "There are Nanotrasen Internal Affairs Agents trying to kill each other!\n\ IAA: Eliminate your targets and protect yourself!\n\ diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index b87f81f8570..30a6cea5124 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -9,7 +9,8 @@ name = "traitor" config_tag = "traitor" report_type = "traitor" - antag_flag = ROLE_TRAITOR + role_preference = /datum/role_preference/antagonist/traitor + antag_datum = /datum/antagonist/traitor false_report_weight = 20 //Reports of traitors are pretty common. restricted_jobs = list(JOB_NAME_CYBORG)//They are part of the AI if he is traitor so are they, they use to get double chances protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_PILOT, JOB_NAME_MASTERATARMS) //NSV13 added pilots and master at arms @@ -17,7 +18,6 @@ required_enemies = 1 recommended_enemies = 4 reroll_friendly = 1 - enemy_minimum_age = 0 announce_span = "danger" announce_text = "There are Syndicate agents on the station!\n\ @@ -29,7 +29,6 @@ var/list/datum/mind/pre_traitors = list() var/traitors_possible = 4 //hard limit on traitors if scaling is turned off var/num_modifier = 0 // Used for gamemodes, that are a child of traitor, that need more than the usual. - var/antag_datum = /datum/antagonist/traitor //what type of antag to create var/traitors_required = TRUE //Will allow no traitors @@ -55,7 +54,7 @@ for(var/j = 0, j < num_traitors, j++) if (!antag_candidates.len) break - var/datum/mind/traitor = antag_pick(antag_candidates, ROLE_TRAITOR) + var/datum/mind/traitor = antag_pick(antag_candidates, /datum/role_preference/antagonist/traitor) pre_traitors += traitor traitor.special_role = traitor_name traitor.restricted_roles = restricted_jobs @@ -90,11 +89,13 @@ if((SSticker.mode.traitors.len + pre_traitors.len) >= traitorcap) //Upper cap for number of latejoin antagonists return if((SSticker.mode.traitors.len + pre_traitors.len) <= (traitorcap - 2) || prob(100 / (tsc * 2))) - if(antag_flag in character.client.prefs.be_special) - if(!is_banned_from(character.ckey, list(ROLE_TRAITOR, ROLE_SYNDICATE)) && !QDELETED(character)) - if(age_check(character.client)) - if(!(character.job in restricted_jobs)) - add_latejoin_traitor(character.mind) + if(!QDELETED(character) && character.client?.should_include_for_role( + banning_key = initial(antag_datum.banning_key), + role_preference_key = role_preference, + req_hours = initial(antag_datum.required_living_playtime), + )) + if(!(character.job in restricted_jobs)) + add_latejoin_traitor(character.mind) /datum/game_mode/traitor/proc/add_latejoin_traitor(datum/mind/character) var/datum/antagonist/traitor/new_antag = new antag_datum() diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index f72b5041322..f58b8b6c09d 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -6,12 +6,12 @@ name = "wizard" config_tag = "wizard" report_type = "wizard" - antag_flag = ROLE_WIZARD + role_preference = /datum/role_preference/antagonist/wizard + antag_datum = /datum/antagonist/wizard false_report_weight = 10 required_players = 20 required_enemies = 1 recommended_enemies = 1 - enemy_minimum_age = 14 round_ends_with_antag_death = 1 announce_span = "danger" announce_text = "There is a space wizard attacking the station!\n\ @@ -22,7 +22,7 @@ title_icon = "wizard" /datum/game_mode/wizard/pre_setup() - var/datum/mind/wizard = antag_pick(antag_candidates, ROLE_WIZARD) + var/datum/mind/wizard = antag_pick(antag_candidates, /datum/role_preference/antagonist/wizard) wizards += wizard wizard.assigned_role = ROLE_WIZARD wizard.special_role = ROLE_WIZARD diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index f3e600f570d..f3b490f4993 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -297,7 +297,7 @@ /obj/machinery/clonepod/proc/offer_to_ghost(mob/living/carbon/H) set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [H.real_name]'s experimental clone?", ROLE_EXPERIMENTAL_CLONE, null, null, 300, H, POLL_IGNORE_EXPERIMENTAL_CLONE) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [H.real_name]'s experimental clone?", ROLE_EXPERIMENTAL_CLONE, null, 30 SECONDS, H) if(length(candidates)) var/mob/dead/observer/C = pick(candidates) H.key = C.key diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index 9830d6e5454..e60327d47be 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -424,7 +424,7 @@ if(iscyborg(sillycone)) var/mob/living/silicon/robot/sillyconerobot = A - if((ROLE_SYNDICATE in faction) && sillyconerobot.emagged == TRUE) + if((FACTION_SYNDICATE in faction) && sillyconerobot.emagged == TRUE) continue else if(iscarbon(A)) @@ -686,7 +686,7 @@ stun_projectile_sound = 'sound/weapons/gunshot.ogg' icon_state = "syndie_off" base_icon_state = "syndie" - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) desc = "A ballistic machine gun auto-turret." /obj/machinery/porta_turret/syndicate/ComponentInitialize() diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index 68dcf131774..0a932d2a8b0 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -329,7 +329,7 @@ S.amount_grown = SLIME_EVOLUTION_THRESHOLD S.Evolve() S.flavor_text = FLAVOR_TEXT_EVIL - S.set_playable() + S.set_playable(ROLE_PYRO_SLIME) ///////////////////// diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 36845c8446d..3779a8f4450 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -518,7 +518,7 @@ possessed = TRUE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_SPECTRAL_BLADE, null, 10 SECONDS, ignore_category = POLL_IGNORE_SPECTRAL_BLADE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index d3156ca1370..244549a5f29 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -18,6 +18,7 @@ Estimated time of last contact: Deployment, 5000 millennia ago." assignedrole = "Lifebringer" use_cooldown = TRUE + banType = ROLE_LIFEBRINGER /obj/effect/mob_spawn/human/seed_vault/special(mob/living/new_spawn) var/plant_name = pick("Tomato", "Potato", "Broccoli", "Carrot", "Ambrosia", "Pumpkin", "Ivy", "Kudzu", "Banana", "Moss", "Flower", "Bloom", "Root", "Bark", "Glowshroom", "Petal", "Leaf", \ @@ -54,6 +55,7 @@ assignedrole = "Ash Walker" var/datum/team/ashwalkers/team use_cooldown = TRUE + banType = ROLE_ASHWALKER /obj/effect/mob_spawn/human/ash_walker/special(mob/living/new_spawn) to_chat(new_spawn, "Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Don't leave your nest undefended, protect it with your life. Glory to the Necropolis!") @@ -78,40 +80,6 @@ head = /obj/item/clothing/head/helmet/gladiator uniform = /obj/item/clothing/under/costume/gladiator/ash_walker - -//Timeless prisons: Spawns in Wish Granter prisons in lavaland. Ghosts become age-old users of the Wish Granter and are advised to seek repentance for their past. -/obj/effect/mob_spawn/human/exile - name = "timeless prison" - desc = "Although this stasis pod looks medicinal, it seems as though it's meant to preserve something for a very long time." - mob_name = "a penitent exile" - icon = 'icons/obj/machines/sleeper.dmi' - icon_state = "sleeper" - roundstart = FALSE - death = FALSE - mob_species = /datum/species/shadow - short_desc = "You are cursed." - flavour_text = "Years ago, you sacrificed the lives of your trusted friends and the humanity of yourself to reach the Wish Granter. Though you \ - did so, it has come at a cost: your very body rejects the light, dooming you to wander endlessly in this horrible wasteland." - assignedrole = "Exile" - use_cooldown = TRUE - -/obj/effect/mob_spawn/human/exile/Destroy() - new/obj/structure/fluff/empty_sleeper(get_turf(src)) - return ..() - -/obj/effect/mob_spawn/human/exile/special(mob/living/new_spawn) - new_spawn.fully_replace_character_name(null,"Wish Granter's Victim ([rand(1,999)])") - var/wish = rand(1,4) - switch(wish) - if(1) - to_chat(new_spawn, "You wished to kill, and kill you did. You've lost track of how many, but the spark of excitement that murder once held has winked out. You feel only regret.") - if(2) - to_chat(new_spawn, "You wished for unending wealth, but no amount of money was worth this existence. Maybe charity might redeem your soul?") - if(3) - to_chat(new_spawn, "You wished for power. Little good it did you, cast out of the light. You are the [gender == MALE ? "king" : "queen"] of a hell that holds no subjects. You feel only remorse.") - if(4) - to_chat(new_spawn, "You wished for immortality, even as your friends lay dying behind you. No matter how many times you cast yourself into the lava, you awaken in this room again within a few days. There is no escape.") - //Golem shells: Spawns in Free Golem ships in lavaland. Ghosts become mineral golems and are advised to spread personal freedom. /obj/effect/mob_spawn/human/golem name = "inert free golem shell" @@ -132,6 +100,7 @@ flavour_text = "In his infinite and divine wisdom, he set your clan free to \ travel the stars with a single declaration: \"Yeah go do whatever.\" Though you are bound to the one who created you, it is customary in your society to repeat those same words to newborn \ golems, so that no golem may ever be forced to serve again." + banType = ROLE_FREE_GOLEM /obj/effect/mob_spawn/human/golem/Initialize(mapload, datum/species/golem/species = null, mob/creator = null) if(species) //spawners list uses object name to register so this goes before ..() @@ -206,6 +175,7 @@ can_transfer = FALSE mob_species = /datum/species/golem/adamantine use_cooldown = TRUE //Only the roundstart free golems are + banType = ROLE_FREE_GOLEM //Malfunctioning cryostasis sleepers: Spawns in makeshift shelters in lavaland. Ghosts become hermits with knowledge of how they got to where they are now. /obj/effect/mob_spawn/human/hermit @@ -224,6 +194,7 @@ the fresh air of Earth. These thoughts are dispelled by yet another recollection of how you got here... " assignedrole = "Hermit" use_cooldown = TRUE + banType = ROLE_HERMIT /obj/effect/mob_spawn/human/hermit/Initialize(mapload) . = ..() @@ -266,6 +237,7 @@ all you did was apply bruise packs. Why is this place full of advanced medical equipment? And what are those screams you hear? The world outside is desolate - tormented with fire and brimstone. But you took an oath. \ You have to save these people! You might not have a fancy cloning machine like a real hospital, but surely there must be some way to save these people with the tools you have. Right?" assignedrole = "Translocated Vet" + banType = ROLE_TRANSLOCATED_VET /obj/effect/mob_spawn/human/doctor/alive/lavaland/Destroy() var/obj/structure/fluff/empty_sleeper/S = new(drop_location()) @@ -287,6 +259,7 @@ flavour_text = "Good. It seems as though your ship crashed. You remember that you were convicted of " assignedrole = "Escaped Prisoner" use_cooldown = TRUE + banType = ROLE_LAVALAND_ESCAPED_PRISONER /obj/effect/mob_spawn/human/prisoner_transport/special(mob/living/L) L.fully_replace_character_name(null,"NTP #LL-0[rand(111,999)]") //Nanotrasen Prisoner #Lavaland-(numbers) @@ -327,6 +300,7 @@ important_info = "DON'T leave the hotel" assignedrole = "Hotel Staff" use_cooldown = TRUE + banType = ROLE_HOTEL_STAFF /datum/outfit/hotelstaff name = "Hotel Staff" @@ -374,6 +348,7 @@ var/obj/effect/proc_holder/spell/targeted/summon_friend/spell var/datum/mind/owner assignedrole = "SuperFriend" + banType = ROLE_DEMONIC_FRIEND /obj/effect/mob_spawn/human/demonic_friend/Initialize(mapload, datum/mind/owner_mind, obj/effect/proc_holder/spell/targeted/summon_friend/summoning_spell) . = ..() @@ -414,77 +389,6 @@ implants = list(/obj/item/implant/mindshield) //No revolutionaries, he's MY friend. id = /obj/item/card/id -/obj/effect/mob_spawn/human/syndicate - name = "Syndicate Operative" - roundstart = FALSE - death = FALSE - icon = 'icons/obj/machines/sleeper.dmi' - icon_state = "sleeper_s" - outfit = /datum/outfit/syndicate_empty - assignedrole = "Space Syndicate" //I know this is really dumb, but Syndicate operative is nuke ops - -/datum/outfit/syndicate_empty - name = "Syndicate Operative Empty" - uniform = /obj/item/clothing/under/syndicate - shoes = /obj/item/clothing/shoes/combat - gloves = /obj/item/clothing/gloves/combat - ears = /obj/item/radio/headset/syndicate/alt - back = /obj/item/storage/backpack - implants = list(/obj/item/implant/weapons_auth) - id = /obj/item/card/id/syndicate - -/datum/outfit/syndicate_empty/post_equip(mob/living/carbon/human/H) - H.faction |= ROLE_SYNDICATE - -/obj/effect/mob_spawn/human/syndicate/battlecruiser - name = "Syndicate Battlecruiser Ship Operative" - short_desc = "You are a crewmember aboard the syndicate flagship: the SBC Starfury." - flavour_text = "Your job is to follow your captain's orders, maintain the ship, and keep the engine running. If you are not familiar with how the supermatter engine functions: do not attempt to start it." - important_info = "The armory is not a candy store, and your role is not to assault the station directly, leave that work to the assault operatives." - outfit = /datum/outfit/syndicate_empty/SBC - -/datum/outfit/syndicate_empty/SBC - name = "Syndicate Battlecruiser Ship Operative" - l_pocket = /obj/item/gun/ballistic/automatic/pistol - r_pocket = /obj/item/kitchen/knife/combat/survival - belt = /obj/item/storage/belt/military/assault - -/obj/effect/mob_spawn/human/syndicate/battlecruiser/assault - name = "Syndicate Battlecruiser Assault Operative" - short_desc = "You are an assault operative aboard the syndicate flagship: the SBC Starfury." - flavour_text = "Your job is to follow your captain's orders, keep intruders out of the ship, and assault Space Station 13. There is an armory, multiple assault ships, and beam cannons to attack the station with." - important_info = "Work as a team with your fellow operatives and work out a plan of attack. If you are overwhelmed, escape back to your ship!" - outfit = /datum/outfit/syndicate_empty/SBC/assault - -/datum/outfit/syndicate_empty/SBC/assault - name = "Syndicate Battlecruiser Assault Operative" - uniform = /obj/item/clothing/under/syndicate/combat - l_pocket = /obj/item/ammo_box/magazine/m10mm - r_pocket = /obj/item/kitchen/knife/combat/survival - belt = /obj/item/storage/belt/military - suit = /obj/item/clothing/suit/armor/vest - suit_store = /obj/item/gun/ballistic/automatic/pistol - back = /obj/item/storage/backpack/security - mask = /obj/item/clothing/mask/gas/syndicate - -/obj/effect/mob_spawn/human/syndicate/battlecruiser/captain - name = "Syndicate Battlecruiser Captain" - short_desc = "You are the captain aboard the syndicate flagship: the SBC Starfury." - flavour_text = "Your job is to oversee your crew, defend the ship, and destroy Space Station 13. The ship has an armory, multiple ships, beam cannons, and multiple crewmembers to accomplish this goal." - important_info = "As the captain, this whole operation falls on your shoulders. You do not need to nuke the station, causing sufficient damage and preventing your ship from being destroyed will be enough." - outfit = /datum/outfit/syndicate_empty/SBC/assault/captain - id_access_list = list(150,151) - -/datum/outfit/syndicate_empty/SBC/assault/captain - name = "Syndicate Battlecruiser Captain" - l_pocket = /obj/item/melee/transforming/energy/sword/saber/red - r_pocket = /obj/item/melee/classic_baton/police/telescopic - suit = /obj/item/clothing/suit/armor/vest/capcarapace/syndicate - suit_store = /obj/item/gun/ballistic/revolver/mateba - back = /obj/item/storage/backpack/satchel/leather - head = /obj/item/clothing/head/HoS/syndicate - mask = /obj/item/clothing/mask/cigarette/cigar/havana - glasses = /obj/item/clothing/glasses/thermal/eyepatch //Ancient cryogenic sleepers. Players become NT crewmen from a hundred year old space station, now on the verge of collapse. /obj/effect/mob_spawn/human/oldsec @@ -509,6 +413,7 @@ l_pocket = /obj/item/assembly/flash/handheld assignedrole = "Ancient Crew" use_cooldown = TRUE + banType = ROLE_ANCIENT_CREW /obj/effect/mob_spawn/human/oldsec/Destroy() new/obj/structure/showcase/machinery/oldpod/used(drop_location()) @@ -536,6 +441,7 @@ l_pocket = /obj/item/tank/internals/emergency_oxygen assignedrole = "Ancient Crew" use_cooldown = TRUE + banType = ROLE_ANCIENT_CREW /obj/effect/mob_spawn/human/oldeng/Destroy() new/obj/structure/showcase/machinery/oldpod/used(drop_location()) @@ -562,6 +468,7 @@ l_pocket = /obj/item/stack/medical/bruise_pack assignedrole = "Ancient Crew" use_cooldown = TRUE + banType = ROLE_ANCIENT_CREW /obj/effect/mob_spawn/human/oldsci/Destroy() new/obj/structure/showcase/machinery/oldpod/used(drop_location()) @@ -584,6 +491,7 @@ short_desc = "You are a space pirate." flavour_text = "The station refused to pay for your protection, protect the ship, siphon the credits from the station and raid it for even more loot." assignedrole = "Space Pirate" + is_antagonist = TRUE var/rank = "Mate" /obj/effect/mob_spawn/human/pirate/special(mob/living/new_spawn) diff --git a/code/game/objects/structures/spawner.dm b/code/game/objects/structures/spawner.dm index 0b41fcf8d49..8f7870e7b30 100644 --- a/code/game/objects/structures/spawner.dm +++ b/code/game/objects/structures/spawner.dm @@ -31,7 +31,7 @@ icon_state = "syndbeacon" spawn_text = "warps in from" mob_types = list(/mob/living/simple_animal/hostile/syndicate/ranged) - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) /obj/structure/spawner/skeleton name = "bone pit" diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm index 3dc29f2d1cf..5b4f5087d99 100644 --- a/code/modules/admin/antag_panel.dm +++ b/code/modules/admin/antag_panel.dm @@ -165,11 +165,19 @@ GLOBAL_VAR(antag_prototypes) continue pref_source = prototype break - if(pref_source.job_rank) - if(!is_banned_from(src.key, pref_source.job_rank)) - antag_header_parts += pref_source.enabled_in_preferences(src) ? "Enabled in Prefs" : "Disabled in Prefs" - else + if(pref_source.banning_key) + if(is_banned_from(src.key, pref_source.banning_key)) antag_header_parts += "\[BANNED\]" + else if(current.client) + var/list/related_preferences = list() + for(var/datum/role_preference/role_pref_type as anything in GLOB.role_preference_entries) + if(initial(role_pref_type.antag_datum) == pref_source.type) + related_preferences += role_pref_type + if(length(related_preferences) == 1) + antag_header_parts += current.client.role_preference_enabled(related_preferences[1]) ? "Enabled in Prefs" : "Disabled in Prefs" + else if(length(related_preferences) >= 1) + for(var/datum/role_preference/preftype as anything in related_preferences) + antag_header_parts += "[initial(preftype.name)]: [current.client.role_preference_enabled(preftype) ? "Enabled Pref" : "Disabled Pref"]" //Traitor : None | Traitor | IAA // Command1 | Command2 | Command3 diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm index ca395efabda..33308c17d44 100644 --- a/code/modules/admin/fun_balloon.dm +++ b/code/modules/admin/fun_balloon.dm @@ -53,7 +53,7 @@ bodies += M var/question = "Would you like to be [group_name]?" - var/list/candidates = pollCandidatesForMobs(question, ROLE_PAI, null, FALSE, 100, bodies) + var/list/candidates = pollCandidatesForMobs(question, ROLE_SENTIENCE, null, 10 SECONDS, bodies) while(LAZYLEN(candidates) && LAZYLEN(bodies)) var/mob/dead/observer/C = pick_n_take(candidates) var/mob/living/body = pick_n_take(bodies) diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index 56759be507a..f05165e3a7a 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -690,7 +690,7 @@ GLOBAL_DATUM_INIT(admin_secrets, /datum/admin_secrets, new) continue if((L in GLOB.player_list) || L.mind || (L.flags_1 & HOLOGRAM_1)) continue - L.set_playable() + L.set_playable(ROLE_SENTIENT_ANIMAL) if("flipmovement") if(!check_rights(R_FUN)) @@ -789,7 +789,7 @@ GLOBAL_DATUM_INIT(admin_secrets, /datum/admin_secrets, new) var/list/candidates = list() if (prefs["offerghosts"]["value"] == "Yes") - candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), ROLE_TRAITOR) + candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), BAN_ROLE_ALL_ANTAGONISTS, ignore_category = FALSE) if (prefs["playersonly"]["value"] == "Yes" && length(candidates) < prefs["minplayers"]["value"]) message_admins("Not enough players signed up to create a portal storm, the minimum was [prefs["minplayers"]["value"]] and the number of signups [length(candidates)]") diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index 9b0efda4aec..255360d94de 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -1,20 +1,36 @@ #define MAX_ADMINBANS_PER_ADMIN 1 #define MAX_ADMINBANS_PER_HEADMIN 3 +/// Process global ban types +/proc/check_role_ban(ban_cache, role) + if(role in GLOB.antagonist_bannable_roles) + if((BAN_ROLE_ALL_ANTAGONISTS in ban_cache) || ("Syndicate" in ban_cache)) // Legacy "Syndicate" ban + return TRUE + if(role in GLOB.forced_bannable_roles) + if(BAN_ROLE_FORCED_ANTAGONISTS in ban_cache) + return TRUE + if(role in GLOB.ghost_role_bannable_roles) + if((BAN_ROLE_ALL_GHOST in ban_cache) || ("Lavaland" in ban_cache)) // Legacy "Lavaland" ban + return TRUE + return role in ban_cache + //checks client ban cache or DB ban table if ckey is banned from one or more roles //doesn't return any details, use only for if statements /proc/is_banned_from(player_ckey, list/roles) - if(!player_ckey) + if(!player_ckey || isnull(roles) || (islist(roles) && !length(roles))) return + player_ckey = ckey(player_ckey) var/client/C = GLOB.directory[player_ckey] if(C) if(!C.ban_cache) build_ban_cache(C) if(islist(roles)) for(var/R in roles) - if(R in C.ban_cache) + if(!R) + continue + if(check_role_ban(C.ban_cache, R)) return TRUE //they're banned from at least one role, no need to keep checking - else if(roles in C.ban_cache) + else if(check_role_ban(C.ban_cache, roles)) return TRUE else var/values = list( @@ -325,16 +341,14 @@ "} break_counter++ output += "" - var/list/long_job_lists = list(("Civilian" = GLOB.civilian_positions | JOB_NAME_GIMMICK), - "Ghost and Other Roles" = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE), - "Antagonist Positions" = list(ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_BLOB, - ROLE_BROTHER, ROLE_CHANGELING, ROLE_CULTIST, ROLE_HERETIC, - ROLE_DEVIL, ROLE_INTERNAL_AFFAIRS, ROLE_MALF, - ROLE_NINJA, ROLE_OPERATIVE, - ROLE_SERVANT_OF_RATVAR, - ROLE_OVERTHROW, ROLE_REV, ROLE_REVENANT, - ROLE_REV_HEAD, ROLE_SYNDICATE, - ROLE_TRAITOR, ROLE_WIZARD, ROLE_HIVE, ROLE_GANG, ROLE_TERATOMA)) //ROLE_REV_HEAD is excluded from this because rev jobbans are handled by ROLE_REV + var/list/long_job_lists = list( + "Civilian" = GLOB.civilian_positions | JOB_NAME_GIMMICK, + "Antagonist Positions" = list(BAN_ROLE_ALL_ANTAGONISTS) + GLOB.antagonist_bannable_roles, + "Forced Antagonist Positions" = list(BAN_ROLE_FORCED_ANTAGONISTS) + GLOB.forced_bannable_roles, + "Ghost Roles" = list(BAN_ROLE_ALL_GHOST) + GLOB.ghost_role_bannable_roles, + "Other" = GLOB.other_bannable_roles, + ) + for(var/department in long_job_lists) output += "
    " break_counter = 0 diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index bcf50f3238b..b91367cca0b 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -27,10 +27,10 @@ popup.set_content(dat) popup.open() -/datum/admins/proc/isReadytoRumble(mob/living/carbon/human/applicant, targetrole, onstation = TRUE, conscious = TRUE) +/datum/admins/proc/isReadytoRumble(mob/living/carbon/human/applicant, targetrole, preference, onstation = TRUE, conscious = TRUE) if(applicant.mind.special_role) return FALSE - if(!(targetrole in applicant.client.prefs.be_special)) + if(!applicant.client?.should_include_for_role(targetrole, preference)) return FALSE if(onstation) var/turf/T = get_turf(applicant) @@ -40,7 +40,7 @@ return FALSE if(!considered_alive(applicant.mind) || considered_afk(applicant.mind)) //makes sure the player isn't a zombie, brain, or just afk all together return FALSE - return !is_banned_from(applicant.ckey, list(targetrole, ROLE_SYNDICATE)) + return TRUE /datum/admins/proc/makeTraitors(maxCount = 3) @@ -59,10 +59,9 @@ var/mob/living/carbon/human/H = null for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(isReadytoRumble(applicant, ROLE_TRAITOR)) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant + if(isReadytoRumble(applicant, ROLE_TRAITOR, /datum/role_preference/midround_living/traitor)) + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant if(candidates.len) var/numTraitors = min(candidates.len, maxCount) @@ -94,10 +93,9 @@ var/mob/living/carbon/human/H = null for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(isReadytoRumble(applicant, ROLE_CHANGELING)) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant + if(isReadytoRumble(applicant, ROLE_CHANGELING, /datum/role_preference/antagonist/changeling)) + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant if(candidates.len) var/numChangelings = min(candidates.len, maxCount) @@ -124,10 +122,9 @@ var/mob/living/carbon/human/H = null for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(isReadytoRumble(applicant, ROLE_REV)) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant + if(isReadytoRumble(applicant, ROLE_REV_HEAD, /datum/role_preference/antagonist/revolutionary)) + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant if(candidates.len) var/numRevs = min(candidates.len, maxCount) @@ -142,7 +139,7 @@ /datum/admins/proc/makeWizard() - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Federation 'diplomat'?", ROLE_WIZARD, null) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Federation 'diplomat'?", ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, ignore_category = POLL_IGNORE_WIZARD_HELPER) var/mob/dead/observer/selected = pick_n_take(candidates) @@ -166,10 +163,9 @@ var/mob/living/carbon/human/H = null for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(isReadytoRumble(applicant, ROLE_CULTIST)) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant + if(isReadytoRumble(applicant, ROLE_CULTIST, /datum/role_preference/antagonist/blood_cultist)) + if(!(applicant.job in temp.restricted_jobs)) + candidates += applicant if(candidates.len) var/numCultists = min(candidates.len, maxCount) @@ -186,8 +182,7 @@ /datum/admins/proc/makeNukeTeam(maxCount = 5) - var/datum/game_mode/nuclear/temp = new - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", ROLE_OPERATIVE, temp) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative) var/list/mob/dead/observer/chosen = list() var/mob/dead/observer/theghost = null @@ -351,7 +346,7 @@ ertemplate.enforce_human = prefs["enforce_human"]["value"] == "Yes" ? TRUE : FALSE ertemplate.opendoors = prefs["open_armory"]["value"] == "Yes" ? TRUE : FALSE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for [ertemplate.polldesc] ?", "deathsquad", null, req_hours = 50) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for [ertemplate.polldesc] ?", ROLE_ERT, req_hours = 50) var/teamSpawned = FALSE if(candidates.len > 0) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 6c949a6a165..6cdb4f7c102 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -300,7 +300,7 @@ for(var/mob/M in GLOB.player_list) if(M.stat != DEAD) continue //we are not dead! - if(!(ROLE_ALIEN in M.client.prefs.be_special)) + if(!M.client?.should_include_for_role(ROLE_ALIEN, /datum/role_preference/midround_ghost/xenomorph)) continue //we don't want to be an alium if(M.client.is_afk()) continue //we are afk @@ -474,7 +474,7 @@ Traitors and the like can also be revived with the previous role mostly intact. new_character.forceMove(pick(GLOB.wizardstart)) var/datum/antagonist/wizard/A = new_character.mind.has_antag_datum(/datum/antagonist/wizard,TRUE) A.equip_wizard() - if(ROLE_SYNDICATE) + if(ROLE_OPERATIVE) new_character.forceMove(pick(GLOB.nukeop_start)) var/datum/antagonist/nukeop/N = new_character.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE) N.equip_op() diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index a7cc7e8c0d6..4da2f23239f 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -12,7 +12,10 @@ GLOBAL_LIST(admin_antag_list) var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with var/delete_on_mind_deletion = TRUE - var/job_rank + /// The ROLE_X key used for this antagonist. + var/banning_key + /// Required living playtime to be included in the rolling for this antagonist + var/required_living_playtime = 0 var/give_objectives = TRUE //Should the default objectives be generated? var/replace_banned = TRUE //Should replace jobbanned player with ghosts if granted. var/list/objectives = list() @@ -114,12 +117,12 @@ GLOBAL_LIST(admin_antag_list) /datum/antagonist/proc/is_banned(mob/M) if(!M) return FALSE - . = (is_banned_from(M.ckey, list(ROLE_SYNDICATE, job_rank)) || QDELETED(M)) + . = (is_banned_from(M.ckey, banning_key) || QDELETED(M)) /datum/antagonist/proc/replace_banned_player() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", job_rank, null, job_rank, 50, owner.current) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", banning_key, null, 7.5 SECONDS, owner.current, ignore_category = FALSE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") @@ -127,6 +130,7 @@ GLOBAL_LIST(admin_antag_list) owner.current.ghostize(FALSE) owner.current.key = C.key else + owner.current.playable_bantype = banning_key owner.current.ghostize(FALSE,SENTIENCE_FORCE) ///Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion. @@ -221,14 +225,6 @@ GLOBAL_LIST(admin_antag_list) /datum/antagonist/proc/antag_panel_data() return "" -/datum/antagonist/proc/enabled_in_preferences(datum/mind/M) - if(job_rank) - if(M.current && M.current.client && (job_rank in M.current.client.prefs.be_special)) - return TRUE - else - return FALSE - return TRUE - // List if ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution /datum/antagonist/proc/get_admin_commands() . = list() diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index d17f6c8e2e9..eff42e0d677 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -58,7 +58,7 @@ if(used) to_chat(H, "You already used this contract!") return - var/list/candidates = pollCandidatesForMob("Do you want to play as a wizard's [href_list["school"]] apprentice?", ROLE_WIZARD, null, ROLE_WIZARD, 150, src) + var/list/candidates = pollGhostCandidates("Do you want to play as a wizard's [href_list["school"]] apprentice?", ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 15 SECONDS, ignore_category = POLL_IGNORE_WIZARD_HELPER) if(LAZYLEN(candidates)) if(QDELETED(src)) return @@ -123,7 +123,7 @@ return to_chat(user, "You activate [src] and wait for confirmation.") - var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, POLL_IGNORE_SYNDICATE) + var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative, 15 SECONDS) if(LAZYLEN(nuke_candidates)) if(QDELETED(src) || !check_usability(user)) return @@ -243,7 +243,7 @@ return if(used) return - var/list/candidates = pollCandidatesForMob("Do you want to play as a [initial(demon_type.name)]?", ROLE_ALIEN, null, ROLE_ALIEN, 50, src) + var/list/candidates = pollGhostCandidates("Do you want to play as a [initial(demon_type.name)]?", ROLE_SLAUGHTER_DEMON, null, 10 SECONDS, ignore_category = FALSE) if(LAZYLEN(candidates)) if(used || QDELETED(src)) return @@ -295,7 +295,7 @@ return to_chat(user, "You activate [src] and wait for confirmation.") - var/list/candidates = pollGhostCandidates("Do you want to play as a gangster reinforcements?", ROLE_GANG, null, ROLE_GANG, 150) + var/list/candidates = pollGhostCandidates("Do you want to play as a gangster reinforcements?", ROLE_GANG, /datum/role_preference/antagonist/gangster, 15 SECONDS) if(LAZYLEN(candidates)) if(QDELETED(src) || !check_usability(user)) return diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm index 4973e420256..054c9d137bf 100644 --- a/code/modules/antagonists/abductor/abductor.dm +++ b/code/modules/antagonists/abductor/abductor.dm @@ -4,7 +4,7 @@ name = "Abductor" roundend_category = "abductors" antagpanel_category = "Abductor" - job_rank = ROLE_ABDUCTOR + banning_key = ROLE_ABDUCTOR show_in_antagpanel = FALSE //should only show subtypes show_to_ghosts = TRUE var/datum/team/abductor_team/team @@ -179,6 +179,7 @@ name = "Abductee" roundend_category = "abductees" antagpanel_category = "Abductee" + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/abductee/on_gain() give_objective() diff --git a/code/modules/antagonists/ashwalker/ashwalker.dm b/code/modules/antagonists/ashwalker/ashwalker.dm index f7a35eb0a31..5f767a84800 100644 --- a/code/modules/antagonists/ashwalker/ashwalker.dm +++ b/code/modules/antagonists/ashwalker/ashwalker.dm @@ -4,7 +4,7 @@ /datum/antagonist/ashwalker name = "Ash Walker" - job_rank = ROLE_LAVALAND + banning_key = ROLE_ASHWALKER show_in_antagpanel = FALSE show_to_ghosts = TRUE prevent_roundtype_conversion = FALSE diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob.dm index 9c58e95630b..802b38735b6 100644 --- a/code/modules/antagonists/blob/blob.dm +++ b/code/modules/antagonists/blob/blob.dm @@ -3,7 +3,7 @@ roundend_category = "blobs" antagpanel_category = "Blob" show_to_ghosts = TRUE - job_rank = ROLE_BLOB + banning_key = ROLE_BLOB var/datum/action/innate/blobpop/pop_action var/starting_points_human_blob = 60 diff --git a/code/modules/antagonists/blob/blob_mobs.dm b/code/modules/antagonists/blob/blob_mobs.dm index 33bc9acd69a..f67e4d2aeca 100644 --- a/code/modules/antagonists/blob/blob_mobs.dm +++ b/code/modules/antagonists/blob/blob_mobs.dm @@ -7,7 +7,7 @@ /mob/living/simple_animal/hostile/blob icon = 'icons/mob/blob.dmi' pass_flags = PASSBLOB - faction = list(ROLE_BLOB) + faction = list(FACTION_BLOB) bubble_icon = "blob" speak_emote = null //so we use verb_yell/verb_say/etc atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) @@ -166,7 +166,7 @@ update_icons() visible_message("The corpse of [H.name] suddenly rises!") if(!key) - set_playable() + set_playable(ROLE_BLOB) /mob/living/simple_animal/hostile/blob/blobspore/death(gibbed) // On death, create a small smoke of harmful gas (s-Acid) diff --git a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm index 82528d95828..7714ab6944c 100644 --- a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm +++ b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm @@ -31,7 +31,7 @@ var/obj/effect/temp_visual/explosion/fast/E = new /obj/effect/temp_visual/explosion/fast(get_turf(M)) E.alpha = 150 for(var/mob/living/L in ohearers(1, get_turf(M))) - if(ROLE_BLOB in L.faction) //no friendly fire + if(FACTION_BLOB in L.faction) //no friendly fire continue var/aoe_volume = ..(L, TOUCH, initial_volume, 0, L.get_permeability_protection(), O) L.apply_damage(0.4*aoe_volume, BRUTE) diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index 2c57a7da6dd..b2139d3cbac 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -18,7 +18,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) layer = FLY_LAYER pass_flags = PASSBLOB - faction = list(ROLE_BLOB) + faction = list(FACTION_BLOB) lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE hud_type = /datum/hud/blob_overmind var/obj/structure/blob/core/blob_core = null // The blob overmind's core @@ -145,7 +145,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) if(!(Ablob.area_flags & BLOBS_ALLOWED)) continue - if(!(ROLE_BLOB in L.faction)) + if(!(FACTION_BLOB in L.faction)) playsound(L, 'sound/effects/splat.ogg', 50, 1) L.death() new/mob/living/simple_animal/hostile/blob/blobspore(T) diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm index 706abeef12a..1040673e8b8 100644 --- a/code/modules/antagonists/blob/powers.dm +++ b/code/modules/antagonists/blob/powers.dm @@ -16,13 +16,13 @@ if(!placement_override) if(!pop_override) for(var/mob/living/M in range(7, src)) - if(ROLE_BLOB in M.faction) + if(FACTION_BLOB in M.faction) continue if(M.client) to_chat(src, "There is someone too close to place your blob core!") return 0 for(var/mob/living/M in hearers(13, src)) - if(ROLE_BLOB in M.faction) + if(FACTION_BLOB in M.faction) continue if(M.client) to_chat(src, "Someone could see your blob core from here!") @@ -172,7 +172,7 @@ B.naut = TRUE //temporary placeholder to prevent creation of more than one per factory. to_chat(src, "You attempt to produce a blobbernaut.") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", ROLE_BLOB, /datum/role_preference/midround_ghost/blob, 7.5 SECONDS, ignore_category = POLL_IGNORE_BLOB_HELPER) //players must answer rapidly if(LAZYLEN(candidates)) //if we got at least one candidate, they're a blobbernaut now. B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health B.obj_integrity = min(B.obj_integrity, B.max_integrity) @@ -268,7 +268,7 @@ if(can_buy(BLOB_SPREAD_COST)) var/attacksuccess = FALSE for(var/mob/living/L in T) - if(ROLE_BLOB in L.faction) //no friendly/dead fire + if(FACTION_BLOB in L.faction) //no friendly/dead fire continue if(L.stat != DEAD) attacksuccess = TRUE diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm index dad188eaaaf..20dfcbb3aef 100644 --- a/code/modules/antagonists/blob/structures/_blob.dm +++ b/code/modules/antagonists/blob/structures/_blob.dm @@ -253,7 +253,7 @@ /obj/structure/blob/attack_animal(mob/living/simple_animal/M) - if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut + if(FACTION_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut return ..() diff --git a/code/modules/antagonists/blood_contract/blood_contract.dm b/code/modules/antagonists/blood_contract/blood_contract.dm index f2faecdd458..c1b79bc13b1 100644 --- a/code/modules/antagonists/blood_contract/blood_contract.dm +++ b/code/modules/antagonists/blood_contract/blood_contract.dm @@ -4,6 +4,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE var/duration = 2 MINUTES + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/blood_contract/on_gain() . = ..() @@ -37,6 +38,7 @@ for(var/mob/living/carbon/human/P in GLOB.player_list) if(P == H) continue + log_game("[key_name(P)] was selected to kill [key_name(H)] by blood contract") // holy shit why is there no antag datum. I'm doing a huge refactor so I don't have time for one but I had to add this log here to_chat(P, "You have an overwhelming desire to kill [H]. [H.p_theyve(TRUE)] been marked red! Whoever [H.p_they()] [H.p_were()], friend or foe, go kill [H.p_them()]!") var/obj/item/I = new /obj/item/kitchen/knife/butcher(get_turf(P)) diff --git a/code/modules/antagonists/brainwashing/brainwashing.dm b/code/modules/antagonists/brainwashing/brainwashing.dm index 57d082ec819..269e40ee32b 100644 --- a/code/modules/antagonists/brainwashing/brainwashing.dm +++ b/code/modules/antagonists/brainwashing/brainwashing.dm @@ -28,7 +28,7 @@ /datum/antagonist/brainwashed name = "Brainwashed Victim" - job_rank = ROLE_BRAINWASHED + banning_key = ROLE_BRAINWASHED roundend_category = "brainwashed victims" show_in_antagpanel = TRUE antagpanel_category = "Other" diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm index efdea659704..b48cf60aa7f 100644 --- a/code/modules/antagonists/brother/brother.dm +++ b/code/modules/antagonists/brother/brother.dm @@ -1,8 +1,8 @@ /datum/antagonist/brother name = "Brother" antagpanel_category = "Brother" - job_rank = ROLE_BROTHER - var/special_role = ROLE_BROTHER + banning_key = ROLE_BROTHER + required_living_playtime = 4 hijack_speed = 0.5 var/datum/team/brother_team/team antag_moodlet = /datum/mood_event/focused @@ -22,14 +22,14 @@ objectives += team.objectives for(var/datum/objective/O in team.objectives) log_objective(owner, O.explanation_text) - owner.special_role = special_role + owner.special_role = ROLE_BROTHER finalize_brother() return ..() /datum/antagonist/brother/on_removal() SSticker.mode.brothers -= owner if(owner.current) - to_chat(owner.current,"You are no longer the [special_role]!") + to_chat(owner.current,"You are no longer the Blood Brother!") owner.special_role = null return ..() @@ -56,7 +56,7 @@ /datum/antagonist/brother/greet() var/brother_text = get_brother_names() - to_chat(owner.current, "You are the [owner.special_role] of [brother_text].") + to_chat(owner.current, "You are the Blood Brother of [brother_text].") to_chat(owner.current, "The Syndicate only accepts those that have proven themselves. Prove yourself and prove your [team.member_name]s by completing your objectives together! You and your team are outfitted with communication implants allowing for direct, encrypted communication.") owner.announce_objectives() give_meeting_area() diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 957db20573c..53c3a42b637 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -6,7 +6,8 @@ name = "Changeling" roundend_category = "changelings" antagpanel_category = "Changeling" - job_rank = ROLE_CHANGELING + banning_key = ROLE_CHANGELING + required_living_playtime = 4 antag_moodlet = /datum/mood_event/focused hijack_speed = 0.5 var/you_are_greet = TRUE diff --git a/code/modules/antagonists/changeling/powers/teratoma.dm b/code/modules/antagonists/changeling/powers/teratoma.dm index 482ac09834b..f0a89f41d6f 100644 --- a/code/modules/antagonists/changeling/powers/teratoma.dm +++ b/code/modules/antagonists/changeling/powers/teratoma.dm @@ -23,7 +23,7 @@ var/datum/antagonist/changeling/c = user.mind.has_antag_datum(/datum/antagonist/changeling) c.chem_charges -= chemical_cost //I'm taking your chemicals hostage! var/turf/A = get_turf(user) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", ROLE_TERATOMA, null, ROLE_TERATOMA, 5 SECONDS) //players must answer rapidly + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", ROLE_TERATOMA, null, 7.5 SECONDS) //players must answer rapidly if(!LAZYLEN(candidates)) //if we got at least one candidate, they're teratoma now to_chat(usr, "You fail at creating a tumor. Perhaps you should try again later?") c.chem_charges += chemical_cost //If it fails we want to refund the chemicals diff --git a/code/modules/antagonists/changeling/teratoma.dm b/code/modules/antagonists/changeling/teratoma.dm index e304ae4383c..3e5a4c51aaf 100644 --- a/code/modules/antagonists/changeling/teratoma.dm +++ b/code/modules/antagonists/changeling/teratoma.dm @@ -2,7 +2,7 @@ name = "Teratoma" roundend_category = "other" antagpanel_category = "Changeling" - job_rank = ROLE_TERATOMA + banning_key = ROLE_TERATOMA /datum/antagonist/teratoma/on_gain() owner.special_role = "Teratoma" @@ -45,7 +45,6 @@ name = "Maintenance Teratoma" roundend_category = "other" antagpanel_category = "Changeling" - job_rank = ROLE_TERATOMA /datum/antagonist/teratoma/hugbox/greet() ..() diff --git a/code/modules/antagonists/clock_cult/mobs/cogscarab.dm b/code/modules/antagonists/clock_cult/mobs/cogscarab.dm index 8d233a2d20d..d1be9760a77 100644 --- a/code/modules/antagonists/clock_cult/mobs/cogscarab.dm +++ b/code/modules/antagonists/clock_cult/mobs/cogscarab.dm @@ -65,11 +65,6 @@ GLOBAL_LIST_INIT(cogscarabs, list()) /obj/effect/mob_spawn/drone/cogscarab/attack_ghost(mob/user) if(is_banned_from(user.ckey, ROLE_SERVANT_OF_RATVAR) || QDELETED(src) || QDELETED(user)) return - if(CONFIG_GET(flag/use_age_restriction_for_jobs)) - if(!isnum(user.client.player_age)) //apparently what happens when there's no DB connected. just don't let anybody be a drone without admin intervention - if(user.client.player_age < 14) - to_chat(user, "You're too new to play as a drone! Please try again in [14 - user.client.player_age] days.") - return if(!SSticker.mode) to_chat(user, "Can't become a cogscarab before the game has started.") return diff --git a/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm b/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm index e4ce19d5f04..0d80bd2aa47 100644 --- a/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm +++ b/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm @@ -53,7 +53,7 @@ if(M.mind) M.mind.grab_ghost(TRUE) else - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [M.name], an inactive clock cultist?", ROLE_SERVANT_OF_RATVAR, null, ROLE_SERVANT_OF_RATVAR, 50, M) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [M.name], an inactive clock cultist?", ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 7.5 SECONDS, M) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(M)]) to replace an AFK player.") diff --git a/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm b/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm index ef41ed2bb61..d665cbeaee7 100644 --- a/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm +++ b/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm @@ -17,7 +17,7 @@ var/mob/dead/observer/selected /datum/clockcult/scripture/marauder/invoke() - candidates = pollGhostCandidates("Would you like to play as a clockwork marauder?", ROLE_SERVANT_OF_RATVAR, null, null, 100, POLL_IGNORE_CLOCKWORK) + candidates = pollGhostCandidates("Would you like to play as a clockwork marauder?", ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 10 SECONDS, POLL_IGNORE_CLOCKWORK_HELPER) if(LAZYLEN(candidates)) selected = pick(candidates) if(!selected) diff --git a/code/modules/antagonists/clock_cult/servant_of_ratvar.dm b/code/modules/antagonists/clock_cult/servant_of_ratvar.dm index 470215b2696..d904a53fe91 100644 --- a/code/modules/antagonists/clock_cult/servant_of_ratvar.dm +++ b/code/modules/antagonists/clock_cult/servant_of_ratvar.dm @@ -7,7 +7,8 @@ roundend_category = "clock cultists" antagpanel_category = "Clockcult" antag_moodlet = /datum/mood_event/cult - job_rank = ROLE_SERVANT_OF_RATVAR + banning_key = ROLE_SERVANT_OF_RATVAR + required_living_playtime = 4 //The class of the servant var/datum/action/innate/clockcult/transmit/transmit_spell diff --git a/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm b/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm index 666acf43f25..0a04a5f1316 100644 --- a/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm +++ b/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm @@ -35,7 +35,7 @@ vote_active = FALSE used = TRUE if(!eminence) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eminence?", ROLE_SERVANT_OF_RATVAR, null, null, 100, POLL_IGNORE_PYROSLIME) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eminence?", ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 10 SECONDS) if(LAZYLEN(candidates)) eminence = pick(candidates) else diff --git a/code/modules/antagonists/creep/creep.dm b/code/modules/antagonists/creep/creep.dm index 6537250de14..63b43371dad 100644 --- a/code/modules/antagonists/creep/creep.dm +++ b/code/modules/antagonists/creep/creep.dm @@ -2,7 +2,7 @@ name = "Obsessed" show_in_antagpanel = TRUE antagpanel_category = "Other" - job_rank = ROLE_OBSESSED + banning_key = ROLE_OBSESSED show_name_in_check_antagonists = TRUE roundend_category = "obsessed" count_against_dynamic_roll_chance = FALSE diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index 75ff9fe6815..0f8cf5cefed 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -8,7 +8,8 @@ var/datum/action/innate/cult/comm/communion = new var/datum/action/innate/cult/mastervote/vote = new var/datum/action/innate/cult/blood_magic/magic = new - job_rank = ROLE_CULTIST + banning_key = ROLE_CULTIST + required_living_playtime = 4 var/ignore_implant = FALSE var/give_equipment = FALSE var/datum/team/cult/cult_team diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm index adb892f2e73..e28c1220f3a 100644 --- a/code/modules/antagonists/cult/cult_comms.dm +++ b/code/modules/antagonists/cult/cult_comms.dm @@ -108,7 +108,7 @@ if(B.current && B.current != Nominee && !B.current.incapacitated()) SEND_SOUND(B.current, 'sound/magic/exit_blood.ogg') asked_cultists += B.current - var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 300, group = asked_cultists) + var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 30 SECONDS, group = asked_cultists) if(QDELETED(Nominee) || Nominee.incapacitated()) team.cult_vote_called = FALSE for(var/datum/mind/B in team.members) diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index fef483b3a3e..4f5d58cf7c7 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -603,7 +603,7 @@ structure_check() searches for nearby cultist structures required for the invoca mob_to_revive.grab_ghost() if(!mob_to_revive.client || mob_to_revive.client.is_afk()) set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", ROLE_CULTIST, null, ROLE_CULTIST, 50, mob_to_revive) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", ROLE_CULTIST, /datum/role_preference/antagonist/blood_cultist, 7.5 SECONDS, mob_to_revive) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.") diff --git a/code/modules/antagonists/devil/devil.dm b/code/modules/antagonists/devil/devil.dm index 15bc64418e0..8b548791769 100644 --- a/code/modules/antagonists/devil/devil.dm +++ b/code/modules/antagonists/devil/devil.dm @@ -88,7 +88,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", name = "Devil" roundend_category = "devils" antagpanel_category = "Devil" - job_rank = ROLE_DEVIL + banning_key = ROLE_DEVIL //Don't delete upon mind destruction, otherwise soul re-selling will break. delete_on_mind_deletion = FALSE show_to_ghosts = TRUE diff --git a/code/modules/antagonists/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm index bbe9b3978c7..0b8f25b0a25 100644 --- a/code/modules/antagonists/devil/imp/imp.dm +++ b/code/modules/antagonists/devil/imp/imp.dm @@ -64,6 +64,7 @@ name = "Imp" antagpanel_category = "Devil" show_in_roundend = FALSE + banning_key = ROLE_DEVIL /datum/antagonist/imp/on_gain() . = ..() diff --git a/code/modules/antagonists/devil/sintouched/sintouched.dm b/code/modules/antagonists/devil/sintouched/sintouched.dm index 29c3c77fd6d..014fdfcba30 100644 --- a/code/modules/antagonists/devil/sintouched/sintouched.dm +++ b/code/modules/antagonists/devil/sintouched/sintouched.dm @@ -10,6 +10,7 @@ name = "sintouched" roundend_category = "sintouched" antagpanel_category = "Devil" + banning_key = UNBANNABLE_ANTAGONIST var/sin var/static/list/sins = list(SIN_ACEDIA,SIN_GLUTTONY,SIN_GREED,SIN_SLOTH,SIN_WRATH,SIN_ENVY,SIN_PRIDE) diff --git a/code/modules/antagonists/eldritch_cult/eldritch_antag.dm b/code/modules/antagonists/eldritch_cult/eldritch_antag.dm index bd98f6e4d07..b752da58ec1 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_antag.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_antag.dm @@ -3,7 +3,8 @@ roundend_category = "Heretics" antagpanel_category = "Heretic" antag_moodlet = /datum/mood_event/heretics - job_rank = ROLE_HERETIC + banning_key = ROLE_HERETIC + required_living_playtime = 4 var/antag_hud_type = ANTAG_HUD_HERETIC // someone make all the other antags conform to this too lol var/antag_hud_name = "heretic" hijack_speed = 0.5 diff --git a/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm b/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm index 7ecba7e1df1..4a48677f763 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm @@ -4,7 +4,7 @@ roundend_category = "Heretics" antagpanel_category = "Heretic Beast" antag_moodlet = /datum/mood_event/heretics - job_rank = ROLE_HERETIC + banning_key = ROLE_HERETIC var/antag_hud_type = ANTAG_HUD_HERETIC var/antag_hud_name = "heretic_beast" var/datum/antagonist/heretic/master diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm index 458280d6e0e..65f0e45db0d 100644 --- a/code/modules/antagonists/ert/ert.dm +++ b/code/modules/antagonists/ert/ert.dm @@ -22,6 +22,7 @@ show_to_ghosts = TRUE antag_moodlet = /datum/mood_event/focused count_against_dynamic_roll_chance = FALSE + banning_key = ROLE_ERT /datum/antagonist/ert/on_gain() if(random_names) diff --git a/code/modules/antagonists/fugitive/fugitive.dm b/code/modules/antagonists/fugitive/fugitive.dm index 164253f3f1f..b543d1752ef 100644 --- a/code/modules/antagonists/fugitive/fugitive.dm +++ b/code/modules/antagonists/fugitive/fugitive.dm @@ -2,6 +2,7 @@ /datum/antagonist/fugitive name = "Fugitive" roundend_category = "Fugitive" + banning_key = ROLE_FUGITIVE silent = TRUE //greet called by the event show_in_antagpanel = FALSE prevent_roundtype_conversion = FALSE diff --git a/code/modules/antagonists/fugitive/hunter.dm b/code/modules/antagonists/fugitive/hunter.dm index aba80479982..e5dc82de09f 100644 --- a/code/modules/antagonists/fugitive/hunter.dm +++ b/code/modules/antagonists/fugitive/hunter.dm @@ -2,6 +2,7 @@ /datum/antagonist/fugitive_hunter name = "Fugitive Hunter" roundend_category = "Fugitive" + banning_key = ROLE_FUGITIVE_HUNTER silent = TRUE //greet called by the spawn show_in_antagpanel = FALSE prevent_roundtype_conversion = FALSE diff --git a/code/modules/antagonists/gang/gang.dm b/code/modules/antagonists/gang/gang.dm index 37377021b71..479ffb27b90 100644 --- a/code/modules/antagonists/gang/gang.dm +++ b/code/modules/antagonists/gang/gang.dm @@ -2,7 +2,7 @@ name = "Gangster" roundend_category = "gangsters" can_coexist_with_others = FALSE - job_rank = ROLE_GANG + banning_key = ROLE_GANG antagpanel_category = "Gang" var/hud_type = "gangster" var/message_name = "Gangster" diff --git a/code/modules/antagonists/greentext/greentext.dm b/code/modules/antagonists/greentext/greentext.dm index a40ab260538..3409e710410 100644 --- a/code/modules/antagonists/greentext/greentext.dm +++ b/code/modules/antagonists/greentext/greentext.dm @@ -3,6 +3,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE //Not that it will be there for long count_against_dynamic_roll_chance = FALSE + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/greentext/proc/forge_objectives() var/datum/objective/O = new /datum/objective("Succeed") diff --git a/code/modules/antagonists/guardian/guardian.dm b/code/modules/antagonists/guardian/guardian.dm index 192f8675362..3d46c14c325 100644 --- a/code/modules/antagonists/guardian/guardian.dm +++ b/code/modules/antagonists/guardian/guardian.dm @@ -5,6 +5,7 @@ show_in_antagpanel = FALSE var/datum/guardian_stats/stats var/datum/mind/summoner + banning_key = ROLE_HOLOPARASITE /datum/antagonist/guardian/roundend_report() var/list/parts = list() diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm index c79a4845af6..5892dec8abf 100644 --- a/code/modules/antagonists/highlander/highlander.dm +++ b/code/modules/antagonists/highlander/highlander.dm @@ -5,6 +5,7 @@ show_name_in_check_antagonists = TRUE can_elimination_hijack = ELIMINATION_ENABLED count_against_dynamic_roll_chance = FALSE + banning_key = BAN_ROLE_ALL_ANTAGONISTS /datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override) var/mob/living/L = owner.current || mob_override diff --git a/code/modules/antagonists/hivemind/hivemind.dm b/code/modules/antagonists/hivemind/hivemind.dm index 0fceed869ef..fa4d0033ee2 100644 --- a/code/modules/antagonists/hivemind/hivemind.dm +++ b/code/modules/antagonists/hivemind/hivemind.dm @@ -2,7 +2,8 @@ name = "Hivemind Host" roundend_category = "hiveminds" antagpanel_category = "Hivemind Host" - job_rank = ROLE_HIVE + banning_key = ROLE_HIVE + required_living_playtime = 4 antag_moodlet = /datum/mood_event/hivehost var/special_role = ROLE_HIVE var/list/hivemembers = list() @@ -156,7 +157,7 @@ /datum/antagonist/hivemind/on_gain() - owner.special_role = special_role + owner.special_role = ROLE_HIVE GLOB.hivehosts += src generate_flavour() create_actions() diff --git a/code/modules/antagonists/hivemind/vessel.dm b/code/modules/antagonists/hivemind/vessel.dm index 2fad52867b8..393417f79d7 100644 --- a/code/modules/antagonists/hivemind/vessel.dm +++ b/code/modules/antagonists/hivemind/vessel.dm @@ -2,7 +2,7 @@ /datum/antagonist/hivevessel name = "Awoken Vessel" - job_rank = ROLE_BRAINWASHED + banning_key = ROLE_HIVE_VESSEL roundend_category = "awoken vessels" antagpanel_category = "Other" show_name_in_check_antagonists = TRUE @@ -36,7 +36,7 @@ mind.remove_antag_datum(/datum/antagonist/brainwashed) /datum/antagonist/hivevessel/on_gain() - owner.special_role = special_role + owner.special_role = ROLE_HIVE_VESSEL owner.AddSpell(fist) ..() diff --git a/code/modules/antagonists/incursion/incursion.dm b/code/modules/antagonists/incursion/incursion.dm index d9ccbb95d0e..5358e20ac0e 100644 --- a/code/modules/antagonists/incursion/incursion.dm +++ b/code/modules/antagonists/incursion/incursion.dm @@ -1,8 +1,8 @@ /datum/antagonist/incursion name = "Syndicate Incursion Member" antagpanel_category = "Incursion" - job_rank = ROLE_INCURSION - var/special_role = ROLE_INCURSION + banning_key = ROLE_INCURSION + required_living_playtime = 4 var/datum/team/incursion/team antag_moodlet = /datum/mood_event/focused hijack_speed = 0.5 @@ -22,7 +22,7 @@ for(var/datum/objective/O in team.objectives) objectives += O log_objective(owner, O.explanation_text) - owner.special_role = special_role + owner.special_role = ROLE_INCURSION finalize_incursion() return ..() @@ -198,15 +198,15 @@ /datum/team/incursion/proc/generate_traitor_kill_objective(list/restricted_jobs) //Spawn someone as a traitor - var/list/datum/mind/people = SSticker.mode.get_alive_non_antagonsist_players_for_role(ROLE_EXCOMM, restricted_jobs) + var/list/datum/mind/people = SSticker.mode.get_alive_non_antagonsist_players_for_role(/datum/antagonist/traitor, /datum/role_preference/antagonist/excommunicate, restricted_jobs) if(!LAZYLEN(people)) log_game("Not enough players for incursion role. [LAZYLEN(people)]") return - var/datum/mind/target = SSticker.mode.antag_pick(people, ROLE_EXCOMM) + var/datum/mind/target = SSticker.mode.antag_pick(people, /datum/role_preference/antagonist/excommunicate) if(!target) log_game("No mind selected.") return - target.make_Traitor() + target.add_antag_datum(/datum/antagonist/traitor/excommunicate) to_chat(target, "You have been declared an ex-communicate of the syndicate and are being hunted down.") to_chat(target, "You have stolen syndicate objective documents, complete the objectives to throw off the syndicate and sabotage their efforts.") target.store_memory("You have been declared an ex-communicate of the syndicate and are being hunted down by a group of traitors. Be careful!") diff --git a/code/modules/antagonists/magic_servant/servant.dm b/code/modules/antagonists/magic_servant/servant.dm index 3277991446c..9717e869475 100644 --- a/code/modules/antagonists/magic_servant/servant.dm +++ b/code/modules/antagonists/magic_servant/servant.dm @@ -3,6 +3,7 @@ show_in_roundend = FALSE show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE + banning_key = ROLE_WIZARD /datum/antagonist/magic_servant/proc/setup_master(mob/M) var/datum/objective/O = new("Serve [M.real_name].") diff --git a/code/modules/antagonists/morph/morph.dm b/code/modules/antagonists/morph/morph.dm index 7aa8db699f0..e88a891f1b1 100644 --- a/code/modules/antagonists/morph/morph.dm +++ b/code/modules/antagonists/morph/morph.dm @@ -276,7 +276,7 @@ role_name = "morphling" /datum/round_event/ghost_role/morph/spawn_role() - var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) + var/list/candidates = get_candidates(ROLE_MORPH, /datum/role_preference/midround_ghost/morph) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/antagonists/morph/morph_antag.dm b/code/modules/antagonists/morph/morph_antag.dm index 7b0491e5de5..c648a24fdea 100644 --- a/code/modules/antagonists/morph/morph_antag.dm +++ b/code/modules/antagonists/morph/morph_antag.dm @@ -2,5 +2,6 @@ name = "Morph" show_name_in_check_antagonists = TRUE show_in_antagpanel = FALSE + banning_key = ROLE_MORPH //It does nothing! (Besides tracking) diff --git a/code/modules/antagonists/nightmare/nightmare.dm b/code/modules/antagonists/nightmare/nightmare.dm index 1739e249510..59ddb46b6f4 100644 --- a/code/modules/antagonists/nightmare/nightmare.dm +++ b/code/modules/antagonists/nightmare/nightmare.dm @@ -1,5 +1,6 @@ /datum/antagonist/nightmare name = "Nightmare" + banning_key = ROLE_NIGHTMARE show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE show_to_ghosts = TRUE diff --git a/code/modules/antagonists/ninja/ninja.dm b/code/modules/antagonists/ninja/ninja.dm index 03b0f9042c8..b697e6684bd 100644 --- a/code/modules/antagonists/ninja/ninja.dm +++ b/code/modules/antagonists/ninja/ninja.dm @@ -1,7 +1,7 @@ /datum/antagonist/ninja name = "Ninja" antagpanel_category = "Ninja" - job_rank = ROLE_NINJA + banning_key = ROLE_NINJA show_name_in_check_antagonists = TRUE show_to_ghosts = TRUE antag_moodlet = /datum/mood_event/focused diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm index 52accf3923d..8126e12ff1c 100644 --- a/code/modules/antagonists/nukeop/nukeop.dm +++ b/code/modules/antagonists/nukeop/nukeop.dm @@ -2,7 +2,8 @@ name = "Nuclear Operative" roundend_category = "syndicate operatives" //just in case antagpanel_category = "NukeOp" - job_rank = ROLE_OPERATIVE + banning_key = ROLE_OPERATIVE + required_living_playtime = 8 antag_moodlet = /datum/mood_event/focused show_to_ghosts = TRUE hijack_speed = 2 //If you can't take out the station, take the shuttle instead. @@ -144,7 +145,7 @@ nuke_team = new_team /datum/antagonist/nukeop/admin_add(datum/mind/new_owner,mob/admin) - new_owner.assigned_role = ROLE_SYNDICATE + new_owner.assigned_role = ROLE_OPERATIVE new_owner.add_antag_datum(src) message_admins("[key_name_admin(admin)] has nuke op'ed [key_name_admin(new_owner)].") log_admin("[key_name(admin)] has nuke op'ed [key_name(new_owner)].") diff --git a/code/modules/antagonists/official/official.dm b/code/modules/antagonists/official/official.dm index a35ef429734..d1d73bc79b7 100644 --- a/code/modules/antagonists/official/official.dm +++ b/code/modules/antagonists/official/official.dm @@ -6,6 +6,7 @@ var/datum/objective/mission var/datum/team/ert/ert_team show_to_ghosts = TRUE + banning_key = ROLE_ERT /datum/antagonist/official/greet() to_chat(owner, "You are a CentCom Official.") diff --git a/code/modules/antagonists/overthrow/overthrow.dm b/code/modules/antagonists/overthrow/overthrow.dm index eeb25929891..da118ce87b4 100644 --- a/code/modules/antagonists/overthrow/overthrow.dm +++ b/code/modules/antagonists/overthrow/overthrow.dm @@ -9,7 +9,7 @@ name = "Syndicate mutineer" roundend_category = "syndicate mutineers" antagpanel_category = "Syndicate Mutineers" - job_rank = ROLE_TRAITOR // simply use the traitor preference & jobban settings + banning_key = ROLE_OVERTHROW var/datum/team/overthrow/team var/static/list/possible_useful_items diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm index b8fbd18c70e..5804e64f448 100644 --- a/code/modules/antagonists/pirate/pirate.dm +++ b/code/modules/antagonists/pirate/pirate.dm @@ -1,6 +1,6 @@ /datum/antagonist/pirate name = "Space Pirate" - job_rank = ROLE_TRAITOR + banning_key = ROLE_SPACE_PIRATE roundend_category = "space pirates" antagpanel_category = "Pirate" show_to_ghosts = TRUE diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index 803d8fd916c..02370743481 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -438,7 +438,7 @@ break if(!key_of_revenant) message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...") - var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", ROLE_REVENANT, null, ROLE_REVENANT, 50, revenant) + var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", ROLE_REVENANT, /datum/role_preference/midround_ghost/revenant, 7.5 SECONDS, revenant) if(!LAZYLEN(candidates)) qdel(revenant) message_admins("No candidates were found for the new revenant. Oh well!") diff --git a/code/modules/antagonists/revenant/revenant_antag.dm b/code/modules/antagonists/revenant/revenant_antag.dm index 169d23d25c0..2c9aa4d8a87 100644 --- a/code/modules/antagonists/revenant/revenant_antag.dm +++ b/code/modules/antagonists/revenant/revenant_antag.dm @@ -3,6 +3,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE show_to_ghosts = TRUE + banning_key = ROLE_REVENANT /datum/antagonist/revenant/greet() owner.announce_objectives() diff --git a/code/modules/antagonists/revenant/revenant_spawn_event.dm b/code/modules/antagonists/revenant/revenant_spawn_event.dm index 10ee621a9b8..54368fd8721 100644 --- a/code/modules/antagonists/revenant/revenant_spawn_event.dm +++ b/code/modules/antagonists/revenant/revenant_spawn_event.dm @@ -26,7 +26,7 @@ message_admins("Event attempted to spawn a revenant, but there were only [deadMobs]/[REVENANT_SPAWN_THRESHOLD] dead mobs.") return WAITING_FOR_SOMETHING - var/list/candidates = get_candidates(ROLE_REVENANT, null, ROLE_REVENANT) + var/list/candidates = get_candidates(ROLE_REVENANT, /datum/role_preference/midround_ghost/revenant) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index d5dce9e21ef..17591952189 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -7,7 +7,7 @@ name = "Revolutionary" roundend_category = "revolutionaries" // if by some miracle revolutionaries without revolution happen antagpanel_category = "Revolution" - job_rank = ROLE_REV + banning_key = ROLE_REV antag_moodlet = /datum/mood_event/revolution var/hud_type = "rev" var/datum/team/revolution/rev_team @@ -159,6 +159,8 @@ /datum/antagonist/rev/head name = "Head Revolutionary" hud_type = "rev_head" + banning_key = ROLE_REV_HEAD + required_living_playtime = 4 var/remove_clumsy = FALSE var/give_flash = FALSE var/give_hud = TRUE @@ -289,6 +291,7 @@ /datum/antagonist/revolution_enemy name = "Enemy of the Revolution" show_in_antagpanel = FALSE + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/revolution_enemy/on_gain() owner.special_role = "revolution enemy" @@ -342,7 +345,7 @@ var/list/datum/mind/nonhuman_promotable = list() for(var/datum/mind/khrushchev in non_heads) if(khrushchev.current && !khrushchev.current.incapacitated() && !khrushchev.current.restrained() && khrushchev.current.client && khrushchev.current.stat != DEAD) - if(ROLE_REV in khrushchev.current.client.prefs.be_special) + if(khrushchev.current.client.should_include_for_role(ROLE_REV_HEAD, /datum/role_preference/antagonist/revolutionary)) if(ishuman(khrushchev.current)) promotable += khrushchev else diff --git a/code/modules/antagonists/role_preference/_role_preference.dm b/code/modules/antagonists/role_preference/_role_preference.dm new file mode 100644 index 00000000000..710ce5d2430 --- /dev/null +++ b/code/modules/antagonists/role_preference/_role_preference.dm @@ -0,0 +1,27 @@ +/datum/role_preference + var/name + /// What heading to display this entry under in the preferences menu. Use ROLE_PREFERENCE_CATEGORY defines. + var/category + /// The Antagonist datum typepath for this entry, if there is one. Used to get data about the role for display (bans etc) + var/datum/antagonist/antag_datum + /// The base abstract path for this subtype. + var/abstract_type = /datum/role_preference + /// If this preference can vary between characters. + var/per_character = FALSE + +/// Includes latejoin and roundstart antagonists +/datum/role_preference/antagonist + category = ROLE_PREFERENCE_CATEGORY_ANAGONIST + abstract_type = /datum/role_preference/antagonist + per_character = TRUE + +/// Includes autotraitor and gamemode midround assignments - being forced into an antagonist during a round (does not apply to conversion antags). +/datum/role_preference/midround_living + category = ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING + abstract_type = /datum/role_preference/midround_living + per_character = TRUE + +/// Includes anything polled from ghosts that does antagonist stuff +/datum/role_preference/midround_ghost + category = ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST + abstract_type = /datum/role_preference/midround_ghost diff --git a/code/modules/antagonists/role_preference/role_antagonists.dm b/code/modules/antagonists/role_preference/role_antagonists.dm new file mode 100644 index 00000000000..b952e5ad233 --- /dev/null +++ b/code/modules/antagonists/role_preference/role_antagonists.dm @@ -0,0 +1,43 @@ +/datum/role_preference/antagonist/blood_brother + name = "Blood Brother" + antag_datum = /datum/antagonist/brother + +/datum/role_preference/antagonist/blood_cultist + name = "Blood Cultist" + antag_datum = /datum/antagonist/cult + +/datum/role_preference/antagonist/clock_cultist + name = "Clock Cultist" + antag_datum = /datum/antagonist/servant_of_ratvar + +/datum/role_preference/antagonist/devil + name = "Devil" + antag_datum = /datum/antagonist/devil + +/datum/role_preference/antagonist/revolutionary + name = "Head Revolutionary" + antag_datum = /datum/antagonist/rev/head + +/datum/role_preference/antagonist/heretic + name = "Heretic" + antag_datum = /datum/antagonist/heretic + +/datum/role_preference/antagonist/hivemind_host + name = "Hivemind Host" + antag_datum = /datum/antagonist/hivemind + +/datum/role_preference/antagonist/incursionist + name = "Incursionist" + antag_datum = /datum/antagonist/incursion + +/datum/role_preference/antagonist/excommunicate + name = "Excommunicated Syndicate Agent" + antag_datum = /datum/antagonist/traitor/excommunicate + +/datum/role_preference/antagonist/gangster + name = "Gangster" + antag_datum = /datum/antagonist/gang + +/datum/role_preference/antagonist/internal_affairs + name = "Internal Affairs Agent" + antag_datum = /datum/antagonist/traitor/internal_affairs diff --git a/code/modules/antagonists/role_preference/role_changeling.dm b/code/modules/antagonists/role_preference/role_changeling.dm new file mode 100644 index 00000000000..cf0399b0bcf --- /dev/null +++ b/code/modules/antagonists/role_preference/role_changeling.dm @@ -0,0 +1,3 @@ +/datum/role_preference/antagonist/changeling + name = "Changeling" + antag_datum = /datum/antagonist/changeling diff --git a/code/modules/antagonists/role_preference/role_midrounds.dm b/code/modules/antagonists/role_preference/role_midrounds.dm new file mode 100644 index 00000000000..77a4c08d798 --- /dev/null +++ b/code/modules/antagonists/role_preference/role_midrounds.dm @@ -0,0 +1,69 @@ +/datum/role_preference/midround_ghost/blob + name = "Blob" + antag_datum = /datum/antagonist/blob + +/datum/role_preference/midround_ghost/xenomorph + name = "Xenomorph" + antag_datum = /datum/antagonist/xeno + +/datum/role_preference/midround_ghost/nightmare + name = "Nightmare" + antag_datum = /datum/antagonist/nightmare + +/datum/role_preference/midround_ghost/space_dragon + name = "Space Dragon" + antag_datum = /datum/antagonist/space_dragon + +/datum/role_preference/midround_ghost/abductor + name = "Abductor" + antag_datum = /datum/antagonist/abductor + +/datum/role_preference/midround_ghost/space_pirate + name = "Space Pirate" + antag_datum = /datum/antagonist/pirate + +/datum/role_preference/midround_ghost/revenant + name = "Revenant" + antag_datum = /datum/antagonist/revenant + +/* NSV13 - Disabled because we don't have this ported yet. +/datum/role_preference/midround_ghost/spider + name = "Spider" + antag_datum = /datum/antagonist/spider + +/datum/role_preference/midround_ghost/swarmer + name = "Swarmer" + antag_datum = /datum/antagonist/swarmer +*/ + +/datum/role_preference/midround_ghost/morph + name = "Morph" + antag_datum = /datum/antagonist/morph + +/datum/role_preference/midround_ghost/fugitive + name = "Fugitive" + antag_datum = /datum/antagonist/fugitive + +/datum/role_preference/midround_ghost/fugitive_hunter + name = "Fugitive Hunter" + antag_datum = /datum/antagonist/fugitive_hunter + +/datum/role_preference/midround_ghost/slaughter_demon + name = "Slaughter Demon" + antag_datum = /datum/antagonist/slaughter + +/datum/role_preference/midround_ghost/devil + name = "Devil (Midround)" + antag_datum = /datum/antagonist/devil + +/datum/role_preference/midround_ghost/ninja + name = "Ninja" + antag_datum = /datum/antagonist/ninja + +/datum/role_preference/midround_living/malfunctioning_ai + name = "Malfunctioning AI" + antag_datum = /datum/antagonist/traitor + +/datum/role_preference/midround_living/obsessed + name = "Obsessed" + antag_datum = /datum/antagonist/obsessed diff --git a/code/modules/antagonists/role_preference/role_operative.dm b/code/modules/antagonists/role_preference/role_operative.dm new file mode 100644 index 00000000000..6a4fe7a5b9e --- /dev/null +++ b/code/modules/antagonists/role_preference/role_operative.dm @@ -0,0 +1,7 @@ +/datum/role_preference/antagonist/nuclear_operative + name = "Nuclear Operative" + antag_datum = /datum/antagonist/nukeop + +/datum/role_preference/midround_ghost/nuclear_operative + name = "Nuclear Operative (Midround)" + antag_datum = /datum/antagonist/nukeop diff --git a/code/modules/antagonists/role_preference/role_traitor.dm b/code/modules/antagonists/role_preference/role_traitor.dm new file mode 100644 index 00000000000..46d11945a14 --- /dev/null +++ b/code/modules/antagonists/role_preference/role_traitor.dm @@ -0,0 +1,7 @@ +/datum/role_preference/antagonist/traitor + name = "Traitor" + antag_datum = /datum/antagonist/traitor + +/datum/role_preference/midround_living/traitor + name = "Traitor (Sleeper Agent)" + antag_datum = /datum/antagonist/traitor diff --git a/code/modules/antagonists/role_preference/role_wizard.dm b/code/modules/antagonists/role_preference/role_wizard.dm new file mode 100644 index 00000000000..31681493a58 --- /dev/null +++ b/code/modules/antagonists/role_preference/role_wizard.dm @@ -0,0 +1,7 @@ +/datum/role_preference/antagonist/wizard + name = "Wizard" + antag_datum = /datum/antagonist/wizard + +/datum/role_preference/midround_ghost/wizard + name = "Wizard (Midround)" + antag_datum = /datum/antagonist/wizard diff --git a/code/modules/antagonists/roundstart_special/special_antagonist.dm b/code/modules/antagonists/roundstart_special/special_antagonist.dm index 7d39652e9bf..603623e4830 100644 --- a/code/modules/antagonists/roundstart_special/special_antagonist.dm +++ b/code/modules/antagonists/roundstart_special/special_antagonist.dm @@ -24,8 +24,10 @@ var/max_occurrences = 1 var/holidayID = "" //Preferences - var/preference_type = ROLE_TRAITOR - var/special_role_flag = null //Will use antag rep if enabled + var/preference_type = null + /// If we should use antag rep. Do note that having a preference_type enables checking during gamemode execution. + var/use_antag_rep = TRUE + var/banning_key = ROLE_TRAITOR /datum/special_role/proc/setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) @@ -46,6 +48,7 @@ E.antagonist_datum = attached_antag_datum E.antag_name = role_name E.preference_type = preference_type + E.banning_key = banning_key E.protected_jobs = restricted_jobs E.typepath = /datum/round_event/create_special_antag E.weight = weight @@ -72,8 +75,8 @@ //The datum associated with the role /datum/antagonist/special - name = "Role that should not be accessable in game." - job_rank = ROLE_SYNDICATE + name = "Role that should not be accessible in game." + banning_key = BAN_ROLE_ALL_ANTAGONISTS show_in_antagpanel = FALSE show_name_in_check_antagonists = FALSE prevent_roundtype_conversion = FALSE diff --git a/code/modules/antagonists/santa/santa.dm b/code/modules/antagonists/santa/santa.dm index fb1d304f8bb..563e89059cb 100644 --- a/code/modules/antagonists/santa/santa.dm +++ b/code/modules/antagonists/santa/santa.dm @@ -3,6 +3,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE show_to_ghosts = TRUE + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/santa/on_gain() . = ..() diff --git a/code/modules/antagonists/separatist/separatist.dm b/code/modules/antagonists/separatist/separatist.dm index 20a6d84bdec..cc9a84836e6 100644 --- a/code/modules/antagonists/separatist/separatist.dm +++ b/code/modules/antagonists/separatist/separatist.dm @@ -6,6 +6,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE var/datum/team/nation/nation + banning_key = BAN_ROLE_ALL_ANTAGONISTS /datum/antagonist/separatist/create_team(datum/team/nation/new_team) if(!new_team) diff --git a/code/modules/antagonists/slaughter/slaughter_antag.dm b/code/modules/antagonists/slaughter/slaughter_antag.dm index 9f80462f578..557b2ec5c49 100644 --- a/code/modules/antagonists/slaughter/slaughter_antag.dm +++ b/code/modules/antagonists/slaughter/slaughter_antag.dm @@ -3,7 +3,7 @@ show_name_in_check_antagonists = TRUE var/objective_verb = "Kill" var/datum/mind/summoner - job_rank = ROLE_ALIEN + banning_key = ROLE_SLAUGHTER_DEMON show_in_antagpanel = FALSE show_to_ghosts = TRUE diff --git a/code/modules/antagonists/slaughter/slaughterevent.dm b/code/modules/antagonists/slaughter/slaughterevent.dm index 8ad463763ce..9c2fdaf3d13 100644 --- a/code/modules/antagonists/slaughter/slaughterevent.dm +++ b/code/modules/antagonists/slaughter/slaughterevent.dm @@ -14,7 +14,7 @@ role_name = "slaughter demon" /datum/round_event/ghost_role/slaughter/spawn_role() - var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) + var/list/candidates = get_candidates(ROLE_SLAUGHTER_DEMON, null) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm index 5cc275660d3..68ad2195fab 100644 --- a/code/modules/antagonists/space_dragon/space_dragon.dm +++ b/code/modules/antagonists/space_dragon/space_dragon.dm @@ -1,5 +1,6 @@ /datum/antagonist/space_dragon name = "Space Dragon" + banning_key = ROLE_SPACE_DRAGON show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE show_to_ghosts = TRUE diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm index 4c36b4e1354..819d8c07fd0 100644 --- a/code/modules/antagonists/survivalist/survivalist.dm +++ b/code/modules/antagonists/survivalist/survivalist.dm @@ -2,6 +2,7 @@ name = "Survivalist" show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE + banning_key = ROLE_SURVIVALIST var/greet_message = "" /datum/antagonist/survivalist/proc/forge_objectives() diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 506f2695465..02396b7388b 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -5,7 +5,8 @@ name = "Traitor" roundend_category = "traitors" antagpanel_category = "Traitor" - job_rank = ROLE_TRAITOR + banning_key = ROLE_TRAITOR + required_living_playtime = 4 antag_moodlet = /datum/mood_event/focused hijack_speed = 0.5 //10 seconds per hijack stage by default var/special_role = ROLE_TRAITOR @@ -435,3 +436,8 @@ /datum/antagonist/traitor/is_gamemode_hero() return SSticker.mode.name == "traitor" + +/datum/antagonist/traitor/excommunicate + name = "Excommunicate Traitor" + banning_key = ROLE_EXCOMM + special_role = ROLE_EXCOMM diff --git a/code/modules/antagonists/traitor/equipment/contractor.dm b/code/modules/antagonists/traitor/equipment/contractor.dm index 7827bf9f394..b97d5ad70c9 100644 --- a/code/modules/antagonists/traitor/equipment/contractor.dm +++ b/code/modules/antagonists/traitor/equipment/contractor.dm @@ -165,7 +165,7 @@ if (.) to_chat(user, "The uplink vibrates quietly, connecting to nearby agents...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_CONTRACTOR_SUPPORT) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", ROLE_CONTRACTOR_SUPPORT_UNIT, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/antagonists/traitor/traitor_spawner.dm b/code/modules/antagonists/traitor/traitor_spawner.dm index 7d42fa8fc4f..f3c31485d74 100644 --- a/code/modules/antagonists/traitor/traitor_spawner.dm +++ b/code/modules/antagonists/traitor/traitor_spawner.dm @@ -13,9 +13,9 @@ allowAntagTargets = TRUE latejoin_allowed = TRUE protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) - - special_role_flag = ROLE_TRAITOR - role_name = ROLE_TRAITOR + role_name = "Traitor" + preference_type = /datum/role_preference/antagonist/traitor + use_antag_rep = TRUE var/traitors_possible = 4 //hard limit on traitors if scaling is turned off diff --git a/code/modules/antagonists/valentines/heartbreaker.dm b/code/modules/antagonists/valentines/heartbreaker.dm index 39ffdfa1496..51e80761140 100644 --- a/code/modules/antagonists/valentines/heartbreaker.dm +++ b/code/modules/antagonists/valentines/heartbreaker.dm @@ -3,6 +3,7 @@ roundend_category = "valentines" show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE + banning_key = BAN_ROLE_ALL_ANTAGONISTS /datum/antagonist/heartbreaker/proc/forge_objectives() diff --git a/code/modules/antagonists/valentines/valentine.dm b/code/modules/antagonists/valentines/valentine.dm index df695fadce5..cc1f81ed1bf 100644 --- a/code/modules/antagonists/valentines/valentine.dm +++ b/code/modules/antagonists/valentines/valentine.dm @@ -5,6 +5,7 @@ prevent_roundtype_conversion = FALSE var/datum/mind/date count_against_dynamic_roll_chance = FALSE + banning_key = UNBANNABLE_ANTAGONIST /datum/antagonist/valentine/proc/forge_objectives() var/datum/objective/protect/protect_objective = new /datum/objective/protect diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm index 497bf49b00d..960d49bdc46 100644 --- a/code/modules/antagonists/wishgranter/wishgranter.dm +++ b/code/modules/antagonists/wishgranter/wishgranter.dm @@ -3,6 +3,7 @@ show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE can_elimination_hijack = ELIMINATION_ENABLED + banning_key = BAN_ROLE_ALL_ANTAGONISTS /datum/antagonist/wishgranter/proc/forge_objectives() var/datum/objective/elimination/highlander/elimination_objective = new diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index af5087b5c84..6909768dca2 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -338,7 +338,7 @@ break if(!chosen_ghost) //Failing that, we grab a ghost - var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", "Cultist", null, ROLE_CULTIST, 50, POLL_IGNORE_SHADE) + var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", ROLE_CULTIST, null, 5 SECONDS, ignore_category = POLL_IGNORE_CULT_SHADE) if(consenting_candidates.len) chosen_ghost = pick(consenting_candidates) if(!T) diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm index b08b67b13c7..46cbfb3c857 100644 --- a/code/modules/antagonists/wizard/wizard.dm +++ b/code/modules/antagonists/wizard/wizard.dm @@ -2,7 +2,8 @@ name = "Space Wizard" roundend_category = "wizards/witches" antagpanel_category = "Wizard" - job_rank = ROLE_WIZARD + banning_key = ROLE_WIZARD + required_living_playtime = 8 antag_moodlet = /datum/mood_event/focused hijack_speed = 0.5 var/strip = TRUE //strip before equipping @@ -182,12 +183,12 @@ /datum/antagonist/wizard/apply_innate_effects(mob/living/mob_override) var/mob/living/M = mob_override || owner.current update_wiz_icons_added(M, wiz_team ? TRUE : FALSE) //Don't bother showing the icon if you're solo wizard - M.faction |= ROLE_WIZARD + M.faction |= FACTION_WIZARD /datum/antagonist/wizard/remove_innate_effects(mob/living/mob_override) var/mob/living/M = mob_override || owner.current update_wiz_icons_removed(M) - M.faction -= ROLE_WIZARD + M.faction -= FACTION_WIZARD /datum/antagonist/wizard/get_admin_commands() diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm index 7845ec7d225..dfabb8366e4 100644 --- a/code/modules/antagonists/xeno/xeno.dm +++ b/code/modules/antagonists/xeno/xeno.dm @@ -11,7 +11,7 @@ /datum/antagonist/xeno name = "Xenomorph" - job_rank = ROLE_ALIEN + banning_key = ROLE_ALIEN show_in_antagpanel = FALSE prevent_roundtype_conversion = FALSE show_to_ghosts = TRUE diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 45d5cc86506..281f873cc73 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -28,9 +28,11 @@ var/mob_color //Change the mob's color var/assignedrole var/show_flavour = TRUE - var/banType = ROLE_LAVALAND + var/banType var/ghost_usable = TRUE var/use_cooldown = FALSE + /// If this should ignore admins disabling ghost roles (like lavaland roles), since it's actually an antagonist. + var/is_antagonist = FALSE //ATTACK GHOST IGNORING PARENT RETURN VALUE /obj/effect/mob_spawn/attack_ghost(mob/user) @@ -42,6 +44,10 @@ if(!uses) to_chat(user, "This spawner is out of charges!") return + if(!SSticker.HasRoundStarted()) + return + if(!user?.client?.can_take_ghost_spawner(banType, use_cooldown, is_ghost_role = !is_antagonist, is_admin_spawned = flags_1 & ADMIN_SPAWNED_1)) + return if(is_banned_from(user.key, banType)) to_chat(user, "You are jobanned!") return @@ -402,6 +408,17 @@ id_job = JOB_NAME_BARTENDER use_cooldown = TRUE +/obj/effect/mob_spawn/human/bartender/alive/beach + assignedrole = "Beach Bartender" + banType = ROLE_BEACH_BUM + outfit = /datum/outfit/spacebartender/beach + +/datum/outfit/spacebartender/beach/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + if(visualsOnly) + return + H.dna.add_mutation(STONER) + /datum/outfit/spacebartender name = "Space Bartender" uniform = /obj/item/clothing/under/rank/civilian/bartender @@ -434,6 +451,7 @@ flavour_text = "Ch'yea. You came here, like, on spring break, hopin' to pick up some bangin' hot chicks, y'knaw?" assignedrole = "Beach Bum" use_cooldown = TRUE + banType = ROLE_BEACH_BUM /obj/effect/mob_spawn/human/beach/alive/lifeguard short_desc = "You're a spunky lifeguard!" @@ -512,29 +530,6 @@ back = /obj/item/storage/backpack/security id = /obj/item/card/id/job/security_officer - -/obj/effect/mob_spawn/human/commander/alive - death = FALSE - roundstart = FALSE - mob_name = "\improper Nanotrasen Commander" - name = "sleeper" - icon = 'icons/obj/machines/sleeper.dmi' - icon_state = "sleeper" - short_desc = "You are a Nanotrasen Commander!" - use_cooldown = TRUE - -/obj/effect/mob_spawn/human/nanotrasensoldier/alive - death = FALSE - roundstart = FALSE - mob_name = "Private Security Officer" - name = "sleeper" - icon = 'icons/obj/machines/sleeper.dmi' - icon_state = "sleeper" - faction = "nanotrasenprivate" - short_desc = "You are a Nanotrasen Private Security Officer!" - use_cooldown = TRUE - - /////////////////Spooky Undead////////////////////// /obj/effect/mob_spawn/human/skeleton @@ -549,30 +544,16 @@ icon = 'icons/effects/blood.dmi' icon_state = "remains" short_desc = "By unknown powers, your skeletal remains have been reanimated!" - flavour_text = "Walk this mortal plain and terrorize all living adventurers who dare cross your path." + flavour_text = "Walk this mortal plane and terrorize all living adventurers who dare cross your path." assignedrole = "Skeleton" use_cooldown = TRUE + banType = ROLE_SKELETAL_REMAINS /obj/effect/mob_spawn/human/skeleton/alive/equip(mob/living/carbon/human/H) var/obj/item/implant/exile/implant = new/obj/item/implant/exile(H) implant.implant(H) H.set_species(/datum/species/skeleton) -/obj/effect/mob_spawn/human/zombie - name = "rotting corpse" - mob_name = "zombie" - mob_species = /datum/species/zombie - assignedrole = "Zombie" - -/obj/effect/mob_spawn/human/zombie/alive - death = FALSE - roundstart = FALSE - icon = 'icons/effects/blood.dmi' - icon_state = "remains" - short_desc = "By unknown powers, your rotting remains have been resurrected!" - flavour_text = "Walk this mortal plain and terrorize all living adventurers who dare cross your path." - use_cooldown = TRUE - /obj/effect/mob_spawn/human/abductor name = "abductor" mob_name = "alien" @@ -584,25 +565,6 @@ uniform = /obj/item/clothing/under/color/grey shoes = /obj/item/clothing/shoes/combat - -//For ghost bar. -/obj/effect/mob_spawn/human/alive/space_bar_patron - name = "Bar cryogenics" - mob_name = "Bar patron" - random = TRUE - permanent = TRUE - uses = -1 - outfit = /datum/outfit/spacebartender - assignedrole = "Space Bar Patron" - -//ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/effect/mob_spawn/human/alive/space_bar_patron/attack_hand(mob/user) - var/despawn = alert("Return to cryosleep? (Warning, Your mob will be deleted!)",,"Yes","No") - if(despawn != "Yes" || !loc || !Adjacent(user)) - return - user.visible_message("[user.name] climbs back into cryosleep...") - qdel(user) - /datum/outfit/cryobartender name = "Cryogenic Bartender" uniform = /obj/item/clothing/under/rank/civilian/bartender diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index 724aa39b410..f62053e356d 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -90,7 +90,7 @@ var/mob/living/current_wizard = null var/next_check = 0 var/cooldown = 600 - var/faction = ROLE_WIZARD + var/faction = FACTION_WIZARD var/braindead_check = 0 /obj/structure/academy_wizard_spawner/New() @@ -125,7 +125,7 @@ if(!current_wizard) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", ROLE_WIZARD, null, ROLE_WIZARD, 50, current_wizard) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 10 SECONDS, current_wizard) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) @@ -133,6 +133,7 @@ current_wizard.ghostize(FALSE) // on the off chance braindead defender gets back in current_wizard.key = C.key else + current_wizard.playable_bantype = ROLE_WIZARD current_wizard.ghostize(FALSE,SENTIENCE_FORCE) /obj/structure/academy_wizard_spawner/proc/summon_wizard() @@ -313,7 +314,7 @@ A.setup_master(user) servant_mind.transfer_to(H) - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", ROLE_WIZARD, null, ROLE_WIZARD, 50, H) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 10 SECONDS, H) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant") diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm index 12c18edec71..1eabeb89289 100644 --- a/code/modules/awaymissions/mission_code/snowdin.dm +++ b/code/modules/awaymissions/mission_code/snowdin.dm @@ -577,7 +577,7 @@ icon_state = "sleeper" roundstart = FALSE death = FALSE - faction = ROLE_SYNDICATE + faction = FACTION_SYNDICATE outfit = /datum/outfit/snowsyndie short_desc = "You are a syndicate operative recently awoken from cryostasis in an underground outpost." flavour_text = "You are a syndicate operative recently awoken from cryostasis in an underground outpost. Monitor Nanotrasen communications and record information. All intruders should be \ diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 401e4131f8b..1acd467413c 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -24,10 +24,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/tip_delay = 500 //tip delay in milliseconds //Antag preferences - var/list/be_special = list() //Special role selection - var/tmp/old_be_special = 0 //Bitflag version of be_special, used to update old savefiles and nothing more - //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were, - //autocorrected this round, not that you'd need to check that. + var/list/role_preferences = list() //Special role selection var/UI_style = null var/outline_color = COLOR_BLUE_GRAY @@ -135,11 +132,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/list/dat = list("
    ") dat += "Character Settings" + dat += "Antagonist Preferences" dat += "Game Preferences" var/shop_name = "[CONFIG_GET(string/metacurrency_name)] Shop" dat += "[shop_name]" dat += "OOC Preferences" - dat += "Roleplay Settings" //NSV13 - Roleplay Tab + dat += "Roleplay Settings" //NSV13 - Roleplay Tab dat += "
    " @@ -598,7 +596,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) if (1) // Game Preferences - dat += "
    " + dat += "" + // left box + dat += "" // left box closed + // right box + dat += "" + // right box closed + dat += "" + dat += "" + dat += "
    " dat += "

    General Settings

    " dat += "UI Style: [UI_style]
    " dat += "Outline: [toggles & PREFTOGGLE_OUTLINE_ENABLED ? "Enabled" : "Disabled"]
    " @@ -652,8 +652,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "
    " dat += "Income Updates: [(chat_toggles & CHAT_BANKCARD) ? "Allowed" : "Muted"]
    " - dat += "
    " + dat += "
    " + dat += "

    Graphics Settings

    " dat += "FPS: [clientfps]
    " dat += "Parallax (Fancy Space): " @@ -705,35 +708,98 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(CONFIG_GET(flag/preference_map_voting)) dat += "Preferred Map: [p_map]
    " - dat += "
    " - - dat += "

    Special Role Settings

    " - - if(is_banned_from(user.ckey, ROLE_SYNDICATE)) - dat += "You are banned from antagonist roles.
    " - src.be_special = list() + dat += "
    Customize Keybinds
    " - for (var/i in GLOB.special_roles) - if(is_banned_from(user.ckey, i)) - dat += "Be [capitalize(i)]: BANNED
    " + if(4) // antagonist preferences window + dat += "
    " + var/name + var/unspaced_slots = 0 + for(var/datum/character_save/CS as anything in character_saves) + unspaced_slots++ + if(unspaced_slots > 4) + dat += "
    " + unspaced_slots = 0 + name = CS.real_name + if(!name) + name = "Character [CS.slot_number]" + if(CS.slot_locked) + dat += "[name] (Locked) " else - var/days_remaining = null - if(ispath(GLOB.special_roles[i]) && CONFIG_GET(flag/use_age_restriction_for_jobs)) //If it's a game mode antag, check if the player meets the minimum age - var/mode_path = GLOB.special_roles[i] - var/datum/game_mode/temp_mode = new mode_path - days_remaining = temp_mode.get_remaining_days(user.client) - - if(days_remaining) - dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
    " - else - dat += "Be [capitalize(i)]: [(i in be_special) ? "Enabled" : "Disabled"]
    " - dat += "
    " - dat += "Midround Antagonist: [(toggles & PREFTOGGLE_MIDROUND_ANTAG) ? "Enabled" : "Disabled"]
    " + dat += "[name] " + dat += "
    " + dat += "" + // + dat += "" + // left box closed + + // + // -------------------------------------------- + // Midround antagonists + ghostspawn roles + dat += "" + // right box closed - dat += "" // i hate myself for this - dat += "" - dat += "
    " + // -------------------------------------------- + // warning pannel + var/ban_antagonists = is_banned_from(parent.ckey, BAN_ROLE_ALL_ANTAGONISTS) + var/ban_forced_antagonists = is_banned_from(parent.ckey, BAN_ROLE_FORCED_ANTAGONISTS) + var/ban_ghost = is_banned_from(parent.ckey, BAN_ROLE_ALL_GHOST) + if(ban_antagonists || ban_forced_antagonists || ban_ghost) + dat += "

    Notification

    " + if(ban_antagonists) + dat += "You are banned from all antagonist roles.
    \ + Show Info
    " + if(ban_forced_antagonists) + dat += "You are banned from all forced antagonist roles (such as brainwashing).
    \ + Show Info
    " + if(ban_ghost) + dat += "You are banned from all non-antagonist ghost roles.
    \ + Show Info
    " + // -------------------------------------------- + // Antagonist roles + dat += "

    Antagonists

    " + for (var/typepath in GLOB.role_preference_entries) + var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] + if(pref.category != ROLE_PREFERENCE_CATEGORY_ANAGONIST) + continue + var/ban_key = initial(pref.antag_datum.banning_key) + if(is_banned_from(parent.ckey, ban_key)) + dat += "[pref.name]: BANNED
    " + else + dat += "[pref.name] \ +
    - Character: [parent.role_preference_enabled(typepath) ? "Enabled" : "Disabled"]\ +
    - Global: Enable\ + Disable
    " + dat += "
    " + dat += "

    Midrounds (Living)

    " + for (var/typepath in GLOB.role_preference_entries) + var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] + if(pref.category != ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING) + continue + var/ban_key = initial(pref.antag_datum.banning_key) + if(is_banned_from(parent.ckey, ban_key)) + dat += "[pref.name]: BANNED
    " + else + dat += "[pref.name] \ +
    - Character: [parent.role_preference_enabled(typepath) ? "Enabled" : "Disabled"]\ +
    - Global: Enable\ + Disable
    " + dat += "

    Midrounds (Ghost)

    " + for (var/typepath in GLOB.role_preference_entries) + var/datum/role_preference/pref = GLOB.role_preference_entries[typepath] + if(pref.category != ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST) + continue + var/ban_key = initial(pref.antag_datum.banning_key) + if(is_banned_from(parent.ckey, ban_key)) + dat += "[pref.name]: BANNED
    " + else + dat += "[pref.name]: [parent.role_preference_enabled(typepath) ? "Enabled" : "Disabled"]
    " + dat += "
    Customize Keybinds
    " + dat += "
    " if(2) //Loadout var/list/type_blacklist = list() @@ -890,7 +956,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "" dat += "" - if(4) //NSV13 - Roleplay Tab - Start + if(5) + dat += "Character Settings" //NSV13 - Roleplay Tab - Start dat += "
    " var/name var/unspaced_slots = 0 @@ -2005,12 +2072,33 @@ GLOBAL_LIST_EMPTY(preferences_datums) toggles ^= PREFTOGGLE_DEADMIN_POSITION_SILICON - if("be_special") - var/be_special_type = href_list["be_special_type"] - if(be_special_type in be_special) - be_special -= be_special_type - else - be_special += be_special_type + if("role_preferences") + var/role_preference_type = href_list["role_preference_type"] + var/role_preference_path = text2path(role_preference_type) + var/datum/role_preference/role_pref = GLOB.role_preference_entries[role_preference_path] + if(istype(role_pref)) + var/list/prefsource = role_pref.per_character ? active_character.role_preferences_character : role_preferences + var/current = prefsource["[role_preference_type]"] + if(isnum(current)) + prefsource["[role_preference_type]"] = !current + else // not set, we assume it's on, so turn it off. + prefsource["[role_preference_type]"] = FALSE + + if("role_preferences_enableall") + var/role_preference_type = href_list["role_preference_type"] + var/role_preference_path = text2path(role_preference_type) + var/datum/role_preference/role_pref = GLOB.role_preference_entries[role_preference_path] + if(istype(role_pref) && role_pref.per_character) + for(var/datum/character_save/CS in character_saves) + CS.role_preferences_character["[role_preference_type]"] = TRUE + + if("role_preferences_disableall") + var/role_preference_type = href_list["role_preference_type"] + var/role_preference_path = text2path(role_preference_type) + var/datum/role_preference/role_pref = GLOB.role_preference_entries[role_preference_path] + if(istype(role_pref) && role_pref.per_character) + for(var/datum/character_save/CS in character_saves) + CS.role_preferences_character["[role_preference_type]"] = FALSE if("name") active_character.be_random_name = !active_character.be_random_name @@ -2052,9 +2140,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("pull_requests") chat_toggles ^= CHAT_PULLR - if("allow_midround_antag") - toggles ^= PREFTOGGLE_MIDROUND_ANTAG - if("parallaxup") parallax = WRAP(parallax + 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) if (parent && parent.mob && parent.mob.hud_used) diff --git a/code/modules/client/preferences2/character_save.dm b/code/modules/client/preferences2/character_save.dm index 59fc38cad52..2647311ca59 100644 --- a/code/modules/client/preferences2/character_save.dm +++ b/code/modules/client/preferences2/character_save.dm @@ -71,6 +71,7 @@ var/list/equipped_gear = list() var/joblessrole = BERANDOMJOB //defaults to 1 for fewer assistants var/uplink_spawn_loc = UPLINK_PDA + var/list/role_preferences_character = list() //Nsv13 squads - we CM now var/preferred_squad = "Able" //NSV13 - Pilots @@ -181,6 +182,11 @@ SAFE_READ_QUERY(39, medical_record) //NSV13 - Stop + // Role prefs + var/role_preferences_character_tmp + SAFE_READ_QUERY(40, role_preferences_character_tmp) //NSV13 - Moved from 32 to 40 due to Roleplaying stuff + role_preferences_character = json_decode(role_preferences_character_tmp) + //Sanitize. Please dont put query reads below this point. Please. @@ -261,6 +267,14 @@ job_preferences -= j all_quirks = SANITIZE_LIST(all_quirks) + role_preferences_character = SANITIZE_LIST(role_preferences_character) + // Remove any invalid entries + for(var/preference in role_preferences_character) + var/path = text2path(preference) + var/datum/role_preference/entry = GLOB.role_preference_entries[path] + if(istype(entry) && entry.per_character) + continue + role_preferences_character -= preference //NSV13 - Roleplay Stuff - Start flavor_text = html_decode(strip_html(flavor_text)) @@ -373,7 +387,8 @@ silicon_flavor_text, general_record, security_record, - medical_record + medical_record, + role_preferences ) VALUES ( :slot, :ckey, @@ -414,7 +429,8 @@ :silicon_flavor_text, :general_record, :security_record, - :medical_record + :medical_record, + :role_preferences ) "}, list( // Now for the above but in a fucking monsterous list @@ -457,7 +473,8 @@ "silicon_flavor_text" = silicon_flavor_text, "general_record" = general_record, "security_record" = security_record, - "medical_record" = medical_record + "medical_record" = medical_record, + "role_preferences" = json_encode(role_preferences_character) )) if(!insert_query.warn_execute()) diff --git a/code/modules/client/preferences2/preferences2.dm b/code/modules/client/preferences2/preferences2.dm index a57181f360e..fe6967c60c5 100644 --- a/code/modules/client/preferences2/preferences2.dm +++ b/code/modules/client/preferences2/preferences2.dm @@ -84,7 +84,7 @@ READPREF_JSONDEC(ignoring, PREFERENCE_TAG_IGNORING) READPREF_JSONDEC(key_bindings, PREFERENCE_TAG_KEYBINDS) READPREF_JSONDEC(purchased_gear, PREFERENCE_TAG_PURCHASED_GEAR) - READPREF_JSONDEC(be_special, PREFERENCE_TAG_BE_SPECIAL) + READPREF_JSONDEC(role_preferences, PREFERENCE_TAG_ROLE_PREFERENCES) //Sanitize asaycolor = sanitize_ooccolor(sanitize_hexcolor(asaycolor, 6, TRUE, initial(asaycolor))) @@ -104,7 +104,14 @@ ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit)) ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION) ghost_others = sanitize_inlist(ghost_others, GLOB.ghost_others_options, GHOST_OTHERS_DEFAULT_OPTION) - be_special = SANITIZE_LIST(be_special) + role_preferences = SANITIZE_LIST(role_preferences) + // Remove any invalid entries + for(var/preference in role_preferences) + var/path = text2path(preference) + var/datum/role_preference/entry = GLOB.role_preference_entries[path] + if(istype(entry) && !entry.per_character) + continue + role_preferences -= preference pda_theme = sanitize_inlist(pda_theme, GLOB.ntos_device_themes_default_content, initial(pda_theme)) pda_color = sanitize_hexcolor(pda_color, 6, TRUE, initial(pda_color)) @@ -166,7 +173,7 @@ PREP_WRITEPREF_JSONENC(ignoring, PREFERENCE_TAG_IGNORING) PREP_WRITEPREF_JSONENC(key_bindings, PREFERENCE_TAG_KEYBINDS) PREP_WRITEPREF_JSONENC(purchased_gear, PREFERENCE_TAG_PURCHASED_GEAR) - PREP_WRITEPREF_JSONENC(be_special, PREFERENCE_TAG_BE_SPECIAL) + PREP_WRITEPREF_JSONENC(role_preferences, PREFERENCE_TAG_ROLE_PREFERENCES) // QuerySelect can execute many queries at once. That name is dumb but w/e SSdbcore.QuerySelect(write_queries, TRUE, TRUE) @@ -220,7 +227,8 @@ silicon_flavor_text, general_record, security_record, - medical_record + medical_record, + role_preferences FROM [format_table_name("characters")] WHERE ckey=:ckey "}, list("ckey" = parent.ckey)) diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 6ca7eb5fad4..56f84d923e5 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -78,15 +78,6 @@ prefs.save_preferences() SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Arrivalrattle", "[!(prefs.toggles & PREFTOGGLE_DISABLE_ARRIVALRATTLE) ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, maybe you should rethink where your life went so wrong. -/client/verb/togglemidroundantag() - set name = "Toggle Midround Antagonist" - set category = "Preferences" - set desc = "Midround Antagonist" - prefs.toggles ^= PREFTOGGLE_MIDROUND_ANTAG - prefs.save_preferences() - to_chat(usr, "You will [(prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG) ? "now" : "no longer"] be considered for midround antagonist positions.") - SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Midround Antag", "[prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - /client/verb/toggletitlemusic() set name = "Hear/Silence Lobby Music" set category = "Preferences" diff --git a/code/modules/clothing/outfits/vr.dm b/code/modules/clothing/outfits/vr.dm index fa02a1aa264..bfd90cf8825 100644 --- a/code/modules/clothing/outfits/vr.dm +++ b/code/modules/clothing/outfits/vr.dm @@ -33,7 +33,7 @@ W.implant(H) var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(H) E.implant(H) - H.faction |= ROLE_SYNDICATE + H.faction |= FACTION_SYNDICATE H.update_icons() /obj/item/paper/fluff/vr/fluke_ops diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm index b089c96b5f5..e521dc6d02d 100755 --- a/code/modules/events/abductor.dm +++ b/code/modules/events/abductor.dm @@ -15,7 +15,7 @@ fakeable = FALSE //Nothing to fake here /datum/round_event/ghost_role/abductor/spawn_role() - var/list/mob/dead/observer/candidates = get_candidates(ROLE_ABDUCTOR, null, ROLE_ABDUCTOR) + var/list/mob/dead/observer/candidates = get_candidates(ROLE_ABDUCTOR, /datum/role_preference/midround_ghost/abductor) if(candidates.len < 2) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm index 3ff8a552a02..b3579514588 100644 --- a/code/modules/events/alien_infestation.dm +++ b/code/modules/events/alien_infestation.dm @@ -62,7 +62,7 @@ message_admins("An event attempted to spawn an alien but no suitable vents were found. Shutting down.") return MAP_ERROR - var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) + var/list/candidates = get_candidates(ROLE_ALIEN, /datum/role_preference/midround_ghost/xenomorph) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm index 662cab5c991..0869a6f35ee 100644 --- a/code/modules/events/blob.dm +++ b/code/modules/events/blob.dm @@ -22,7 +22,7 @@ /datum/round_event/ghost_role/blob/spawn_role() if(!GLOB.blobstart.len) return MAP_ERROR - var/list/candidates = get_candidates(ROLE_BLOB, null, ROLE_BLOB) + var/list/candidates = get_candidates(ROLE_BLOB, /datum/role_preference/midround_ghost/blob) if(!candidates.len) return NOT_ENOUGH_PLAYERS var/mob/dead/observer/new_blob = pick(candidates) diff --git a/code/modules/events/creep_awakening.dm b/code/modules/events/creep_awakening.dm index 1b5643bc53f..b45b45ada95 100644 --- a/code/modules/events/creep_awakening.dm +++ b/code/modules/events/creep_awakening.dm @@ -10,7 +10,7 @@ /datum/round_event/obsessed/start() for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list)) - if(!H.client || !(ROLE_OBSESSED in H.client.prefs.be_special)) + if(!H.client?.should_include_for_role(ROLE_OBSESSED, /datum/role_preference/midround_living/obsessed)) continue if(H.stat == DEAD) continue diff --git a/code/modules/events/devil.dm b/code/modules/events/devil.dm index 9073171c98c..7000d2b3366 100644 --- a/code/modules/events/devil.dm +++ b/code/modules/events/devil.dm @@ -19,7 +19,7 @@ return MAP_ERROR //selecting a candidate player - var/list/candidates = get_candidates(ROLE_DEVIL, null, ROLE_DEVIL) + var/list/candidates = get_candidates(ROLE_DEVIL, /datum/role_preference/midround_ghost/devil) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/fugitive_spawning.dm b/code/modules/events/fugitive_spawning.dm index 29b3e3149a3..5dd61663786 100644 --- a/code/modules/events/fugitive_spawning.dm +++ b/code/modules/events/fugitive_spawning.dm @@ -22,7 +22,7 @@ return MAP_ERROR var/turf/landing_turf = pick(possible_spawns) var/list/possible_backstories = list() - var/list/candidates = get_candidates(ROLE_TRAITOR, null, ROLE_TRAITOR) + var/list/candidates = get_candidates(ROLE_TRAITOR, /datum/role_preference/midround_ghost/fugitive) if(candidates.len >= 1) //solo refugees if(prob(30)) possible_backstories.Add("waldo") //less common as it comes with magicks and is kind of immershun shattering diff --git a/code/modules/events/ghost_role.dm b/code/modules/events/ghost_role.dm index 67ef4002e73..70546de1fa9 100644 --- a/code/modules/events/ghost_role.dm +++ b/code/modules/events/ghost_role.dm @@ -55,14 +55,14 @@ // players could be found, and just runtime if anything else happens return TRUE -/datum/round_event/ghost_role/proc/get_candidates(jobban, gametypecheck, be_special) +/datum/round_event/ghost_role/proc/get_candidates(banning_key, role_preference, poll_ignore = null) // Returns a list of candidates in priority order, with candidates from // `priority_candidates` first, and ghost roles randomly shuffled and // appended after var/list/mob/dead/observer/regular_candidates // don't get their hopes up if(priority_candidates.len < minimum_required) - regular_candidates = pollGhostCandidates("Do you wish to be considered for the special role of '[role_name]'?", jobban, gametypecheck, be_special) + regular_candidates = pollGhostCandidates("Do you wish to be considered for the special role of '[role_name]'?", banning_key, role_preference, ignore_category = poll_ignore) else regular_candidates = list() diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm index 6a75c1cf380..f887ab485d7 100644 --- a/code/modules/events/holiday/xmas.dm +++ b/code/modules/events/holiday/xmas.dm @@ -86,7 +86,7 @@ priority_announce("Santa is coming to town!", "Unknown Transmission", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/santa/start() - var/list/candidates = pollGhostCandidates("Santa is coming to town! Do you want to be Santa?", poll_time=150) + var/list/candidates = pollGhostCandidates("Santa is coming to town! Do you want to be Santa?", poll_time = 15 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) santa = new /mob/living/carbon/human(pick(GLOB.blobstart)) diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm index f0311378d3f..03ef2578f3b 100644 --- a/code/modules/events/nightmare.dm +++ b/code/modules/events/nightmare.dm @@ -12,7 +12,7 @@ fakeable = FALSE /datum/round_event/ghost_role/nightmare/spawn_role() - var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) + var/list/candidates = get_candidates(ROLE_NIGHTMARE, /datum/role_preference/midround_ghost/nightmare) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/operative.dm b/code/modules/events/operative.dm index e655d8b0e33..bea4dad09d1 100644 --- a/code/modules/events/operative.dm +++ b/code/modules/events/operative.dm @@ -11,7 +11,7 @@ fakeable = FALSE /datum/round_event/ghost_role/operative/spawn_role() - var/list/candidates = get_candidates(ROLE_OPERATIVE, null, ROLE_OPERATIVE) + var/list/candidates = get_candidates(ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm index a4764953150..6a6b5e564ff 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/pirates.dm @@ -65,7 +65,7 @@ GLOBAL_VAR_INIT(pirates_spawned, FALSE) if(!skip_answer_check && threat?.answered == PIRATE_RESPONSE_PAY) return - var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", ROLE_TRAITOR) + var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", ROLE_SPACE_PIRATE, /datum/role_preference/midround_ghost/space_pirate, 15 SECONDS) shuffle_inplace(candidates) var/datum/map_template/shuttle/pirate/default/ship = new diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm index 06cfa5e5843..7139a65377a 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/sentience.dm @@ -40,7 +40,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /datum/round_event/ghost_role/sentience/spawn_role() var/list/mob/dead/observer/candidates - candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) + candidates = get_candidates(ROLE_SENTIENT_ANIMAL, null) // find our chosen mob to breathe life into // Mobs have to be simple animals, mindless, on station, and NOT holograms. diff --git a/code/modules/events/space_dragon.dm b/code/modules/events/space_dragon.dm index 731cda46c4a..95e7f509cfd 100644 --- a/code/modules/events/space_dragon.dm +++ b/code/modules/events/space_dragon.dm @@ -17,7 +17,7 @@ priority_announce("It appears a lifeform with magical traces is approaching [station_name()], please stand-by.", "Lifesign Alert", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/ghost_role/space_dragon/spawn_role() - var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) + var/list/candidates = get_candidates(ROLE_SPACE_DRAGON, /datum/role_preference/midround_ghost/space_dragon) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/special_antag_event.dm b/code/modules/events/special_antag_event.dm index 6fbe7b39e73..50b1932d2eb 100644 --- a/code/modules/events/special_antag_event.dm +++ b/code/modules/events/special_antag_event.dm @@ -3,9 +3,10 @@ typepath = /datum/round_event/create_special_antag auto_add = FALSE //Antagonist data - var/antagonist_datum = /datum/antagonist/special + var/datum/antagonist/antagonist_datum = /datum/antagonist/special var/antag_name //The datum of the antag E.G. /datum/antagonist/special/undercover - var/preference_type = ROLE_TRAITOR + var/preference_type = /datum/role_preference/antagonist/traitor + var/banning_key = BAN_ROLE_ALL_ANTAGONISTS var/protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CAPTAIN, JOB_NAME_MASTERATARMS) //NSV13 - MPs, XO, MAA /datum/round_event_control/spawn_special_antagonist/runEvent() @@ -13,6 +14,7 @@ E.antag_datum = antagonist_datum E.role_name = antag_name E.preference_type = preference_type + E.banning_key = banning_key E.protected_jobs = protected_jobs E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1) E.control = src @@ -34,15 +36,16 @@ /datum/round_event/create_special_antag fakeable = FALSE var/role_name - var/antag_datum //The datum of the antag E.G. /datum/antagonist/special/undercover - var/preference_type = ROLE_TRAITOR + var/datum/antagonist/antag_datum //The datum of the antag E.G. /datum/antagonist/special/undercover + var/banning_key = BAN_ROLE_ALL_ANTAGONISTS + var/preference_type = /datum/role_preference/antagonist/traitor var/protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CAPTAIN, JOB_NAME_MASTERATARMS) //NSV13 - XO, MAA, MP /datum/round_event/create_special_antag/start() for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list)) - if(!H.client || !(preference_type in H.client.prefs.be_special) || !(H.client.prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG)) + if(!H.client) continue - if(is_banned_from(H, list(preference_type))) + if(!H.client.should_include_for_role(initial(antag_datum.banning_key), preference_type)) continue if(H.stat == DEAD) continue diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm index 1c8ef95baae..f69d962ce0c 100644 --- a/code/modules/events/wizard/imposter.dm +++ b/code/modules/events/wizard/imposter.dm @@ -10,7 +10,7 @@ if(!ishuman(M.current)) continue var/mob/living/carbon/human/W = M.current - var/list/candidates = pollGhostCandidates("Would you like to be an imposter wizard?", ROLE_WIZARD) + var/list/candidates = pollGhostCandidates("Would you like to be an imposter wizard?", ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, ignore_category = POLL_IGNORE_WIZARD_HELPER) if(!candidates) return //Sad Trombone var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm index 81e5588b5f2..afe3167fe08 100644 --- a/code/modules/food_and_drinks/food/snacks_meat.dm +++ b/code/modules/food_and_drinks/food/snacks_meat.dm @@ -178,7 +178,7 @@ qdel(src) /obj/item/reagent_containers/food/snacks/monkeycube/syndicate - faction = list("neutral", ROLE_SYNDICATE) + faction = list("neutral", FACTION_SYNDICATE) /obj/item/reagent_containers/food/snacks/monkeycube/gorilla name = "gorilla cube" diff --git a/code/modules/guardian/guardian.dm b/code/modules/guardian/guardian.dm index ba180d60701..3de93224483 100644 --- a/code/modules/guardian/guardian.dm +++ b/code/modules/guardian/guardian.dm @@ -581,7 +581,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians /mob/living/simple_animal/hostile/guardian/proc/ResetMe() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [summoner?.current?.name]'s [real_name]?", ROLE_HOLOPARASITE, null, FALSE, 10 SECONDS) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [summoner?.current?.name]'s [real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) key = C.key @@ -674,7 +674,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians return G.next_reset = world.time + GUARDIAN_RESET_COOLDOWN to_chat(src, "You attempt to reset [G.real_name]'s personality...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_HOLOPARASITE, null, FALSE, 100) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") diff --git a/code/modules/guardian/guardianbuilder.dm b/code/modules/guardian/guardianbuilder.dm index 4492b08cacd..e5a97fa23ee 100644 --- a/code/modules/guardian/guardianbuilder.dm +++ b/code/modules/guardian/guardianbuilder.dm @@ -225,7 +225,7 @@ used = FALSE return FALSE // IMPORTANT - if we're debugging, the user gets thrown into the stand - var/list/mob/dead/observer/candidates = debug_mode ? list(user) : pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_HOLOPARASITE, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = debug_mode ? list(user) : pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) var/mob/living/simple_animal/hostile/guardian/G = new(user, theme, guardian_color) diff --git a/code/modules/guardian/standarrow.dm b/code/modules/guardian/standarrow.dm index c2e6b3457c5..583ee3fc2b4 100644 --- a/code/modules/guardian/standarrow.dm +++ b/code/modules/guardian/standarrow.dm @@ -161,7 +161,7 @@ G.name = new_name /obj/item/stand_arrow/proc/get_stand(mob/living/carbon/H, datum/guardian_stats/stats) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Guardian Spirit of [H.real_name]?", ROLE_HOLOPARASITE, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Guardian Spirit of [H.real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) var/mob/living/simple_animal/hostile/guardian/G = new(H, GUARDIAN_MAGIC, rgb(rand(1, 255), rand(1, 255), rand(1, 255))) diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm index 81c921f6ef1..3a28fde67a2 100644 --- a/code/modules/jobs/jobs.dm +++ b/code/modules/jobs/jobs.dm @@ -175,7 +175,7 @@ GLOBAL_LIST_INIT(exp_jobsmap, list( GLOBAL_LIST_INIT(exp_specialmap, list( EXP_TYPE_LIVING = list(), // all living mobs EXP_TYPE_ANTAG = list(), - EXP_TYPE_SPECIAL = list("Lifebringer","Ash Walker","Exile","Servant Golem","Free Golem","Hermit","Translocated Vet","Escaped Prisoner","Hotel Staff","SuperFriend","Space Syndicate","Ancient Crew","Space Doctor","Space Bartender","Beach Bum","Skeleton","Zombie","Space Bar Patron","Lavaland Syndicate",JOB_NAME_PAI,"Ghost Role"), // Ghost roles + EXP_TYPE_SPECIAL = list("Lifebringer","Ash Walker","Exile","Servant Golem","Free Golem","Hermit","Translocated Vet","Escaped Prisoner","Hotel Staff","SuperFriend","Space Syndicate","Ancient Crew","Space Doctor","Beach Bum","Skeleton","Zombie","Lavaland Syndicate",JOB_NAME_PAI,"Ghost Role"), // Ghost roles EXP_TYPE_GHOST = list() // dead people, observers )) GLOBAL_PROTECT(exp_jobsmap) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 19d1d67f1c6..25fb54ec741 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -513,9 +513,7 @@ if(client.prefs.active_character.joblessrole != RETURNTOLOBBY) return TRUE // If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so. - var/has_antags = FALSE - if(client.prefs.be_special.len > 0) - has_antags = TRUE + var/has_antags = (length(client.prefs.role_preferences) + length(client.prefs.active_character?.role_preferences_character)) > 0 if(!length(client.prefs.active_character.job_preferences)) if(!ineligible_for_roles) to_chat(src, "You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.") diff --git a/code/modules/mob/dead/observer/notificationprefs.dm b/code/modules/mob/dead/observer/notificationprefs.dm deleted file mode 100644 index 6370747ad46..00000000000 --- a/code/modules/mob/dead/observer/notificationprefs.dm +++ /dev/null @@ -1,53 +0,0 @@ -/mob/dead/observer/verb/show_notificationprefs() - set category = "Ghost" - set name = "Notification preferences" - set desc = "Notification preferences" - - var/datum/notificationpanel/panel = new(usr) - - panel.ui_interact(usr) - - - -/datum/notificationpanel - var/client/user - -/datum/notificationpanel/New(user) - if (ismob(user)) - var/mob/M = user - if (!M.client) - CRASH("Ghost role notification panel attempted to open to a mob without a client") - src.user = M.client - else - src.user = user - - -/datum/notificationpanel/ui_state(mob/user) - return GLOB.observer_state - -/datum/notificationpanel/ui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "NotificationPreferences") - ui.open() - -/datum/notificationpanel/ui_data(mob/user) - . = list() - .["ignore"] = list() - for(var/key in GLOB.poll_ignore_desc) - .["ignore"] += list(list( - "key" = key, - "enabled" = (user.ckey in GLOB.poll_ignore[key]), - "desc" = GLOB.poll_ignore_desc[key] - )) - - -/datum/notificationpanel/ui_act(action, params) - if(..()) - return - switch (action) - if ("toggle_ignore") - var/key = params["key"] - if (key && islist(GLOB.poll_ignore[key])) - GLOB.poll_ignore[key] ^= list(user.ckey) - . = TRUE diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 6d6396d172f..fbae9c34847 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -3,7 +3,7 @@ icon = 'icons/mob/alien.dmi' gender = FEMALE //All xenos are girls!! dna = null - faction = list(ROLE_ALIEN) + faction = list(FACTION_ALIEN) ventcrawler = VENTCRAWLER_ALWAYS sight = SEE_MOBS see_in_dark = 4 diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm index 3fd29dfdfe0..6d4ada4ce49 100644 --- a/code/modules/mob/living/carbon/alien/organs.dm +++ b/code/modules/mob/living/carbon/alien/organs.dm @@ -115,12 +115,12 @@ var/recent_queen_death = 0 //Indicates if the queen died recently, aliens are heavily weakened while this is active. /obj/item/organ/alien/hivenode/Insert(mob/living/carbon/M, special = 0) - M.faction |= ROLE_ALIEN + M.faction |= FACTION_ALIEN ADD_TRAIT(M, TRAIT_XENO_IMMUNE, "xeno immune") return ..() /obj/item/organ/alien/hivenode/Remove(mob/living/carbon/M, special = 0) - M.faction -= ROLE_ALIEN + M.faction -= FACTION_ALIEN REMOVE_TRAIT(M, TRAIT_XENO_IMMUNE, "xeno immune") return ..() diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 1aee89c7515..dc4a6ac2b9e 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -90,7 +90,7 @@ bursting = TRUE - var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, null, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA) + var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, /datum/role_preference/midround_ghost/xenomorph, 10 SECONDS, POLL_IGNORE_ALIEN_LARVA) // separate poll from xeno event spawns if(QDELETED(src) || QDELETED(owner)) return diff --git a/code/modules/mob/living/living_sentience.dm b/code/modules/mob/living/living_sentience.dm index 2e349cbcd38..d778bac1562 100644 --- a/code/modules/mob/living/living_sentience.dm +++ b/code/modules/mob/living/living_sentience.dm @@ -1,15 +1,19 @@ //WHY ISN'T THIS COMPONENT +/// The ban type to check if somebody attempts to play this "playable mob" +/mob/living/var/playable_bantype + /mob/living/ghostize(can_reenter_corpse, sentience_retention) . = ..() switch(sentience_retention) if (SENTIENCE_RETAIN) if (playable) //so the alert goes through for observing ghosts - set_playable() + set_playable(playable_bantype) if (SENTIENCE_FORCE) - set_playable() + set_playable(playable_bantype) if (SENTIENCE_ERASE) playable = FALSE + playable_bantype = null /mob/living/attack_ghost(mob/user) . = ..() @@ -34,6 +38,10 @@ if(key) to_chat(user, "Someone else already took [name].") return TRUE + if(!SSticker.HasRoundStarted()) + return + if(!user?.client?.can_take_ghost_spawner(playable_bantype, TRUE, flags_1 & ADMIN_SPAWNED_1)) + return key = user.key log_game("[key_name(src)] took control of [name].") remove_from_spawner_menu() @@ -41,10 +49,11 @@ to_chat(src, "[get_spawner_flavour_text()]") return TRUE -/mob/living/proc/set_playable() +/mob/living/proc/set_playable(ban_type = null, poll_ignore_key = null) playable = TRUE + playable_bantype = ban_type if (!key) //check if there is nobody already inhibiting this mob - notify_ghosts("[name] can be controlled", null, enter_link="(Click to play)", source=src, action=NOTIFY_ATTACK, ignore_key = name) + notify_ghosts("[name] can be controlled", null, enter_link="(Click to play)", source=src, action=NOTIFY_ATTACK, ignore_key = poll_ignore_key) LAZYADD(GLOB.mob_spawners["[name]"], src) GLOB.poi_list |= src SSmobs.update_spawners() diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 1fd3611725a..dc57aca536b 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -904,7 +904,7 @@ /mob/living/silicon/robot/modules/syndicate icon_state = "synd_sec" - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) bubble_icon = "syndibot" req_access = list(ACCESS_SYNDICATE) lawupdate = FALSE diff --git a/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm index b873ae66255..9f666beb24f 100644 --- a/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm +++ b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm @@ -24,7 +24,7 @@ desc = "The Syndicate sends their regards." emagged = 2 noloot = TRUE - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) /mob/living/simple_animal/bot/secbot/grievous/nullcrate/ComponentInitialize() . = ..() diff --git a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm index b5dd3f01af4..c82610ca84f 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm @@ -47,12 +47,6 @@ /obj/effect/mob_spawn/drone/attack_ghost(mob/user) if(is_banned_from(user.ckey, ROLE_DRONE) || QDELETED(src) || QDELETED(user)) return - if(CONFIG_GET(flag/use_age_restriction_for_jobs)) - if(!isnum_safe(user.client.player_age)) //apparently what happens when there's no DB connected. just don't let anybody be a drone without admin intervention - return - if(user.client.player_age < DRONE_MINIMUM_AGE) - to_chat(user, "You're too new to play as a drone! Please try again in [DRONE_MINIMUM_AGE - user.client.player_age] days.") - return if(!SSticker.mode) to_chat(user, "Can't become a drone before the game has started.") return diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm index 057facfda3e..0d22d332ef3 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm @@ -18,7 +18,7 @@ health = 30 maxHealth = 120 //If you murder other drones and cannibalize them you can get much stronger initial_language_holder = /datum/language_holder/drone/syndicate - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) speak_emote = list("hisses") bubble_icon = "syndibot" heavy_emp_damage = 10 diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index 80f89524fb8..08c9c2448b6 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -462,7 +462,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians var/mob/living/simple_animal/hostile/guardian/G = input(src, "Pick the guardian you wish to reset", "Guardian Reset") as null|anything in sortNames(guardians) if(G) to_chat(src, "You attempt to reset [G.real_name]'s personality...") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_PAI, null, FALSE, 100) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") @@ -540,7 +540,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians return used = TRUE to_chat(user, "[use_message]") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index 3cd5f245d6a..c85b891ea68 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -24,7 +24,7 @@ attack_sound = 'sound/weapons/bladeslice.ogg' atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 15 - faction = list(ROLE_ALIEN) + faction = list(FACTION_ALIEN) status_flags = CANPUSH minbodytemp = 0 see_in_dark = 8 diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index f17d40a8148..3cb408d47c7 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -144,7 +144,7 @@ gender = FEMALE speak_emote = list("squeaks") gold_core_spawnable = NO_SPAWN - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) AIStatus = AI_OFF /// Keeping track of the nuke disk for the functionality of storing it. var/obj/item/disk/nuclear/disky diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 484f5249ecb..b3758fc7464 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -156,7 +156,7 @@ While using this makes the system rely on OnFire, it still gives options for tim addtimer(CALLBACK(src, PROC_REF(spawn_elite)), 30) return visible_message("Something within [src] stirs...") - var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, src, POLL_IGNORE_SENTIENCE_POTION) + var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", ROLE_LAVALAND_ELITE, null, 10 SECONDS, src) if(candidates.len) audible_message("The stirring sounds increase in volume!") elitemind = pick(candidates) diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 9d75eb659f7..af8d6db1272 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -40,7 +40,7 @@ loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 15 - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) check_friendly_fire = 1 status_flags = CANPUSH del_on_death = TRUE @@ -287,7 +287,7 @@ environment_smash = ENVIRONMENT_SMASH_NONE attacktext = "cuts" attack_sound = 'sound/weapons/bladeslice.ogg' - faction = list(ROLE_SYNDICATE) + faction = list(FACTION_SYNDICATE) atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 mob_size = MOB_SIZE_TINY diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm index c365014d62f..e4f104a8afb 100644 --- a/code/modules/mob/living/simple_animal/hostile/wizard.dm +++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm @@ -20,7 +20,7 @@ a_intent = INTENT_HARM atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 15 - faction = list(ROLE_WIZARD) + faction = list(FACTION_WIZARD) status_flags = CANPUSH retreat_distance = 3 //out of fireball range diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 6e449acdfdd..fd3cd1dd0c7 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -113,7 +113,7 @@ . = ..() set_nutrition(SLIME_DEFAULT_NUTRITION) if(transformeffects & SLIME_EFFECT_LIGHT_PINK) - set_playable() + set_playable(ROLE_SENTIENCE) /mob/living/simple_animal/slime/Destroy() set_target(null) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 157565c96aa..c6e307add8b 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -490,15 +490,17 @@ log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.") message_admins("[key_name_admin(usr)] has offered control of ([ADMIN_LOOKUPFLW(M)]) to ghosts") var/poll_message = "Do you want to play as [M.real_name]?" + var/ban_key = BAN_ROLE_ALL_ANTAGONISTS if(M.mind && M.mind.assigned_role) poll_message = "[poll_message] Job:[M.mind.assigned_role]." if(M.mind && M.mind.special_role) poll_message = "[poll_message] Status:[M.mind.special_role]." else if(M.mind) - var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/) + var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist) if(A) poll_message = "[poll_message] Status:[A.name]." - var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, null, FALSE, 100, M) + ban_key = A.banning_key + var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, 10 SECONDS, M, ignore_category = FALSE) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/ninja/ninja_event.dm index 8b8ea63bc9c..a3b656b5f09 100644 --- a/code/modules/ninja/ninja_event.dm +++ b/code/modules/ninja/ninja_event.dm @@ -45,7 +45,7 @@ Contents: return MAP_ERROR //selecting a candidate player - var/list/candidates = get_candidates(ROLE_NINJA, null, ROLE_NINJA) + var/list/candidates = get_candidates(ROLE_NINJA, /datum/role_preference/midround_ghost/ninja) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 9f42c08f3a4..d165cdd8943 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -583,15 +583,17 @@ /obj/item/projectile/magic/wipe/proc/possession_test(var/mob/living/carbon/M) var/datum/brain_trauma/special/imaginary_friend/trapped_owner/trauma = M.gain_trauma(/datum/brain_trauma/special/imaginary_friend/trapped_owner) var/poll_message = "Do you want to play as [M.real_name]?" + var/ban_key = BAN_ROLE_ALL_ANTAGONISTS if(M.mind?.assigned_role) poll_message = "[poll_message] Job:[M.mind.assigned_role]." if(M.mind?.special_role) poll_message = "[poll_message] Status:[M.mind.special_role]." else if(M.mind) - var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/) + var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist) if(A) poll_message = "[poll_message] Status:[A.name]." - var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, null, FALSE, 100, M) + ban_key = A.banning_key + var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, 10 SECONDS, M, ignore_category = FALSE) if(M.stat == DEAD)//boo. return if(LAZYLEN(candidates)) diff --git a/code/modules/religion/rites.dm b/code/modules/religion/rites.dm index 345cbcad35d..d9fc56a01e8 100644 --- a/code/modules/religion/rites.dm +++ b/code/modules/religion/rites.dm @@ -395,8 +395,7 @@ var/turf/altar_turf = get_turf(religious_tool) new /obj/effect/temp_visual/cult/blood/long(altar_turf) new /obj/effect/temp_visual/dir_setting/curse/long(altar_turf) - var/list/jobbans = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE) - var/list/candidates = pollGhostCandidates("Do you wish to be resurrected as a Holy Summoned Undead?", jobbans, null, FALSE,) + var/list/candidates = pollGhostCandidates("Do you wish to be resurrected as a Holy Summoned Undead?", ROLE_HOLY_SUMMONED, null, FALSE) if(!length(candidates)) to_chat(user, "The soul pool is empty...") new /obj/effect/gibspawner/human/bodypartless(altar_turf) @@ -558,8 +557,7 @@ var/turf/altar_turf = get_turf(religious_tool) new /obj/effect/temp_visual/bluespace_fissure/long(altar_turf) user.visible_message("A tear in reality appears above the altar!") - var/list/jobbans = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE) - var/list/candidates = pollGhostCandidates("Do you wish to be summoned as a Holy Carp?", jobbans, null, FALSE) + var/list/candidates = pollGhostCandidates("Do you wish to be summoned as a Holy Carp?", ROLE_HOLY_SUMMONED, null, FALSE) if(!length(candidates)) new /obj/effect/gibspawner/generic(altar_turf) user.visible_message("The carp pool was not strong enough to bring forth a space carp.") diff --git a/code/modules/research/xenobiology/crossbreeding/warping.dm b/code/modules/research/xenobiology/crossbreeding/warping.dm index 87fda9a828e..2ae9c6b746d 100644 --- a/code/modules/research/xenobiology/crossbreeding/warping.dm +++ b/code/modules/research/xenobiology/crossbreeding/warping.dm @@ -713,7 +713,7 @@ GLOBAL_DATUM(blue_storage, /obj/item/storage/backpack/holding/bluespace) return to_chat(user, "The rune is trying to repair [host.name]'s soul!") - var/list/candidates = pollCandidatesForMob("Do you want to replace the soul of [host.name]?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, host, POLL_IGNORE_SHADE)//todo: fix desc + var/list/candidates = pollCandidatesForMob("Do you want to replace the soul of [host.name]?", ROLE_SENTIENCE, null, 5 SECONDS, host, POLL_IGNORE_SHADE) if(length(candidates) && !host.key) //check if anyone wanted to play as the dead person and check if no one's in control of the body one last time. var/mob/dead/observer/ghost = pick(candidates) diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 221e42c019c..dfa8e98894e 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -748,7 +748,7 @@ to_chat(user, "You offer [src] to [SM]...") being_used = TRUE - var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]? (Sentience Potion)", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm + var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]? (Sentience Potion)", ROLE_SENTIENCE, null, 5 SECONDS, SM) if(length(candidates)) var/mob/dead/observer/C = pick(candidates) SM.key = C.key diff --git a/code/modules/ruins/lavaland_ruin_code.dm b/code/modules/ruins/lavaland_ruin_code.dm index 7e1e2b1c871..be12c9f2f6c 100644 --- a/code/modules/ruins/lavaland_ruin_code.dm +++ b/code/modules/ruins/lavaland_ruin_code.dm @@ -115,6 +115,7 @@ outfit = /datum/outfit/lavaland_syndicate assignedrole = "Lavaland Syndicate" use_cooldown = TRUE + banType = ROLE_LAVALAND_SYNDICATE /obj/effect/mob_spawn/human/lavaland_syndicate/special(mob/living/new_spawn) new_spawn.grant_language(/datum/language/codespeak) @@ -133,7 +134,7 @@ implants = list(/obj/item/implant/weapons_auth) /datum/outfit/lavaland_syndicate/post_equip(mob/living/carbon/human/H) - H.faction |= ROLE_SYNDICATE + H.faction |= FACTION_SYNDICATE /obj/effect/mob_spawn/human/lavaland_syndicate/comms name = "Syndicate Comms Agent" diff --git a/code/modules/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/ruins/spaceruin_code/hilbertshotel.dm index b8f42696f2d..ea141a15cdc 100644 --- a/code/modules/ruins/spaceruin_code/hilbertshotel.dm +++ b/code/modules/ruins/spaceruin_code/hilbertshotel.dm @@ -465,6 +465,7 @@ GLOBAL_VAR_INIT(hhmysteryRoomNumber, 1337) back = /obj/item/storage/backpack/satchel/leather suit = /obj/item/clothing/suit/toggle/labcoat use_cooldown = TRUE + banType = ROLE_HOTEL_STAFF /obj/item/paper/crumpled/docslogs name = "Research Logs" diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm index c49e6cc64ce..88ae992e973 100644 --- a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm +++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm @@ -35,7 +35,7 @@ /datum/orbital_objective/assassination/generate_objective_stuff(turf/chosen_turf) var/mob/living/carbon/human/created_human = new(chosen_turf) //Maybe polling ghosts would be better than the shintience code - created_human.set_playable() + created_human.set_playable(ROLE_SURVIVALIST) created_human.mind_initialize() //Remove nearby dangers for(var/mob/living/simple_animal/hostile/SA in range(10, created_human)) diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm index de86fe6ff67..c9d288670df 100644 --- a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm +++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm @@ -36,7 +36,7 @@ /datum/orbital_objective/vip_recovery/generate_objective_stuff(turf/chosen_turf) var/mob/living/carbon/human/created_human = new(chosen_turf) //Maybe polling ghosts would be better than the shintience code - created_human.set_playable() + created_human.set_playable(ROLE_EXPLORATION_VIP) created_human.mind_initialize() //Remove nearby dangers for(var/mob/living/simple_animal/hostile/SA in range(10, created_human)) diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm index 29ddd49e9f9..5cdee0bd98b 100644 --- a/code/modules/shuttle/syndicate.dm +++ b/code/modules/shuttle/syndicate.dm @@ -29,7 +29,7 @@ . = ..() /obj/machinery/computer/shuttle_flight/syndicate/allowed(mob/M) - if(issilicon(M) && !(ROLE_SYNDICATE in M.faction)) + if(issilicon(M) && !(FACTION_SYNDICATE in M.faction)) return FALSE return ..() diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index edf8ab0bad3..77c992afe94 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -57,8 +57,10 @@ #include "create_and_destroy.dm" #endif +#include "antag_datums.dm" #include "dynamic_ruleset_sanity.dm" #include "keybinding_init.dm" +#include "gamemode_sanity.dm" #include "reagent_id_typos.dm" #include "reagent_recipe_collisions.dm" #include "spawn_humans.dm" diff --git a/code/modules/unit_tests/antag_datums.dm b/code/modules/unit_tests/antag_datums.dm new file mode 100644 index 00000000000..fdea39f2ea3 --- /dev/null +++ b/code/modules/unit_tests/antag_datums.dm @@ -0,0 +1,15 @@ +/// Verifies that antag datums have banning_keys. +/datum/unit_test/antag_datum_sanity + +/datum/unit_test/antag_datum_sanity/Run() + for (var/datum/antagonist/antag as anything in subtypesof(/datum/antagonist)) + if(ispath(antag, /datum/antagonist/custom)) + continue + var/name = initial(antag.name) + if (!name || name == "Antagonist") + Fail("[antag] has no name set!") + if (!initial(antag.banning_key)) + Fail("[antag] has no banning_key set!") + var/category = initial(antag.antagpanel_category) + if (initial(antag.show_in_antagpanel) && (!category || category == "Uncategorized")) + Fail("[antag] shows in the antag panel, but has no category set!") diff --git a/code/modules/unit_tests/dynamic_ruleset_sanity.dm b/code/modules/unit_tests/dynamic_ruleset_sanity.dm index 61200a29bb3..84a111a0745 100644 --- a/code/modules/unit_tests/dynamic_ruleset_sanity.dm +++ b/code/modules/unit_tests/dynamic_ruleset_sanity.dm @@ -10,6 +10,17 @@ Fail("[ruleset] has a scaling_cost, but is also a lone/highlander ruleset.") else if (!has_scaling_cost && !is_lone) Fail("[ruleset] has no scaling cost, but is also not a lone/highlander ruleset.") + var/name = initial(ruleset.name) + if(!name) + Fail("[ruleset] has no name!") + if(name == "Extended" || name == "Meteor") // These rulesets don't spawn antags and are exempt. + continue + var/datum/antagonist/antag_datum = initial(ruleset.antag_datum) + if (!ispath(antag_datum, /datum/antagonist) || !initial(antag_datum.banning_key)) + Fail("[ruleset] has no antag_datum with a banning key!") + var/role_pref = initial(ruleset.role_preference) + if (!role_pref || !ispath(role_pref, /datum/role_preference)) + Fail("[ruleset] has no role preference!") for (var/datum/dynamic_ruleset/midround/ruleset as anything in subtypesof(/datum/dynamic_ruleset/midround) - /datum/dynamic_ruleset/midround/from_ghosts) var/midround_ruleset_style = initial(ruleset.midround_ruleset_style) diff --git a/code/modules/unit_tests/gamemode_sanity.dm b/code/modules/unit_tests/gamemode_sanity.dm new file mode 100644 index 00000000000..2ac7e458f0b --- /dev/null +++ b/code/modules/unit_tests/gamemode_sanity.dm @@ -0,0 +1,20 @@ +/// Verifies that gamemodes have various fields +/datum/unit_test/gamemode_sanity + +/datum/unit_test/gamemode_sanity/Run() + for (var/datum/game_mode/mode as anything in subtypesof(/datum/game_mode)) + var/name = initial(mode.name) + if (!name) + Fail("[mode] has no name set!") + var/config_tag = initial(mode.config_tag) + if (!config_tag) + Fail("[mode] has no config_tag set!") + // These gamemodes don't spawn antags directly and are exempt. + if(!initial(mode.required_enemies) && !initial(mode.recommended_enemies)) + continue + var/datum/antagonist/antag_datum = initial(mode.antag_datum) + if (!ispath(antag_datum, /datum/antagonist) || !initial(antag_datum.banning_key)) + Fail("[mode] has no antag_datum with a banning key!") + var/role_pref = initial(mode.role_preference) + if (!role_pref || !ispath(role_pref, /datum/role_preference)) + Fail("[mode] has no role_preference set!") diff --git a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm index 8ba217223f2..97efac692b7 100644 --- a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm +++ b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm @@ -131,7 +131,7 @@ man.key = M.ckey /datum/xenoartifact_trait/minor/sentient/proc/get_canidate(obj/item/xenoartifact/X, mob/M) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", ROLE_SENTIENCE, null, FALSE, 8 SECONDS, POLL_IGNORE_SENTIENCE_POTION) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", ROLE_SENTIENT_XENOARTIFACT, null, 8 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) setup_sentience(X, C.ckey) @@ -197,6 +197,7 @@ short_desc = "You're a maleviolent sentience, possesing an ancient alien artifact." flavour_text = "Return to your master..." use_cooldown = TRUE + banType = ROLE_SENTIENT_XENOARTIFACT invisibility = 101 var/obj/item/xenoartifact/artifact diff --git a/config/dbconfig.txt b/config/dbconfig.txt index 17550ca61b1..6d1e40ed08e 100644 --- a/config/dbconfig.txt +++ b/config/dbconfig.txt @@ -3,11 +3,11 @@ ## administration, and the in game library. ## Should SQL be enabled? Uncomment to enable -#SQL_ENABLED +SQL_ENABLED ## Server the MySQL database can be found at. ## Examples: localhost, 200.135.5.43, www.mysqldb.com, etc. -ADDRESS localhost +ADDRESS 127.0.0.1 ## MySQL server port (default is 3306). PORT 3306 diff --git a/config/dynamic.json b/config/dynamic.json index 55cd5dc0f04..f7a22b96803 100644 --- a/config/dynamic.json +++ b/config/dynamic.json @@ -7,20 +7,9 @@ "weight": 5, "required_candidates": 1, "minimum_required_age": 0, - "requirements": [ - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10 - ], + "requirements": [8, 8, 8, 8, 8, 8, 8, 8, 8, 8], "antag_cap": { - "denominator": 24 + "denominator": 38 }, "protected_roles": [ "Security Officer", @@ -29,9 +18,7 @@ "Head of Security", "Captain" ], - "restricted_roles": [ - "Cyborg" - ] + "restricted_roles": ["Cyborg"] } }, "Midround": {}, diff --git a/html/admin/banpanel.css b/html/admin/banpanel.css index f19ef6e1ba2..86fe6f84e33 100644 --- a/html/admin/banpanel.css +++ b/html/admin/banpanel.css @@ -1,7 +1,7 @@ .middle { - margin-left: 5px; - margin-right: 5px; - max-width: 125px; + margin-left: 5px; + margin-right: 5px; + max-width: 125px; } .right { @@ -70,10 +70,18 @@ background-color: #6eaa2c; } -.ghostandotherroles { +.ghostroles { background-color: #5c00e6; } .antagonistpositions { - background-color: #6d3f40; + background-color: #6d3f40; +} + +.forcedantagonistpositions { + background-color: #064d10; +} + +.other { + background-color: #cf7474; } diff --git a/nsv13.dme b/nsv13.dme index 08d24ee8f07..54578d4e2ae 100644 --- a/nsv13.dme +++ b/nsv13.dme @@ -1777,6 +1777,13 @@ #include "code\modules\antagonists\revenant\revenant_blight.dm" #include "code\modules\antagonists\revenant\revenant_spawn_event.dm" #include "code\modules\antagonists\revolution\revolution.dm" +#include "code\modules\antagonists\role_preference\_role_preference.dm" +#include "code\modules\antagonists\role_preference\role_antagonists.dm" +#include "code\modules\antagonists\role_preference\role_changeling.dm" +#include "code\modules\antagonists\role_preference\role_midrounds.dm" +#include "code\modules\antagonists\role_preference\role_operative.dm" +#include "code\modules\antagonists\role_preference\role_traitor.dm" +#include "code\modules\antagonists\role_preference\role_wizard.dm" #include "code\modules\antagonists\roundstart_special\special_antagonist.dm" #include "code\modules\antagonists\roundstart_special\undercover\undercover.dm" #include "code\modules\antagonists\santa\santa.dm" @@ -2506,7 +2513,6 @@ #include "code\modules\mob\dead\new_player\sprite_accessories.dm" #include "code\modules\mob\dead\observer\login.dm" #include "code\modules\mob\dead\observer\logout.dm" -#include "code\modules\mob\dead\observer\notificationprefs.dm" #include "code\modules\mob\dead\observer\observer.dm" #include "code\modules\mob\dead\observer\observer_movement.dm" #include "code\modules\mob\dead\observer\orbit.dm" @@ -3844,6 +3850,9 @@ #include "nsv13\code\modules\antagonists\simple_teamchat.dm" #include "nsv13\code\modules\antagonists\boarders\boarders.dm" #include "nsv13\code\modules\antagonists\boarders\pirate_boarders.dm" +#include "nsv13\code\modules\antagonists\ghostship\ghost_ship.dm" +#include "nsv13\code\modules\antagonists\role_preference\role_antagonists.dm" +#include "nsv13\code\modules\antagonists\role_preference\role_midrounds.dm" #include "nsv13\code\modules\atmospherics\gasmixtures\reactions.dm" #include "nsv13\code\modules\atmospherics\machinery\components\binary_devices\constrictor.dm" #include "nsv13\code\modules\atmospherics\machinery\components\unary_devices\outlet_injector.dm" diff --git a/nsv13/code/controllers/subsystem/overmap_mode.dm b/nsv13/code/controllers/subsystem/overmap_mode.dm index a634ee5957e..c0690a54508 100644 --- a/nsv13/code/controllers/subsystem/overmap_mode.dm +++ b/nsv13/code/controllers/subsystem/overmap_mode.dm @@ -659,7 +659,7 @@ SUBSYSTEM_DEF(overmap_mode) if("Cancel") return if("Open") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [initial(target_ship.faction)] [initial(target_ship.name)]?", ROLE_GHOSTSHIP, null, null, 20 SECONDS, POLL_IGNORE_GHOSTSHIP) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [initial(target_ship.faction)] [initial(target_ship.name)]?", ROLE_GHOSTSHIP, /datum/role_preference/midround_ghost/ghost_ship, 20 SECONDS, POLL_IGNORE_GHOSTSHIP) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) target_ghost = C diff --git a/nsv13/code/game/gamemodes/bloodling.dm b/nsv13/code/game/gamemodes/bloodling.dm index 87706ff243f..a075a456632 100644 --- a/nsv13/code/game/gamemodes/bloodling.dm +++ b/nsv13/code/game/gamemodes/bloodling.dm @@ -2,7 +2,8 @@ name = "bloodling" config_tag = "bloodling" report_type = "bloodling" - antag_flag = ROLE_BLOODLING + role_preference = /datum/role_preference/antagonist/bloodling + antag_datum = /datum/antagonist/bloodling false_report_weight = 10 restricted_jobs = list("AI", "Cyborg") protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_BRIGPHYSICIAN) @@ -108,19 +109,21 @@ Helper proc to spawn the lil' blood alien creature in a vent! Adapted from alien if(master) //There's already a master return if(bloodlings.len < bloodling_amount) - if(ROLE_BLOODLING in character.client.prefs.be_special) - if(!is_banned_from(character.ckey, list(ROLE_BLOODLING, ROLE_SYNDICATE)) && !QDELETED(character)) - if(age_check(character.client)) - if(!(character.job in restricted_jobs)) - if(!master) //Make him the master - master = spawn_bloodling() - if(!master) - return FALSE //yeah okay your shit map doesn't support bloodling RIP - master.key = character.client.ckey - bloodlings += master.mind - qdel(character) //Bye! - //Otherwise, make him a new thrall... - character.mind.add_antag_datum(/datum/antagonist/changeling/bloodling_thrall) + if(!QDELETED(character) && character.client?.should_include_for_role( + banning_key = initial(antag_datum.banning_key), + role_preference_key = role_preference, + req_hours = initial(antag_datum.required_living_playtime), + )) + if(!(character.job in restricted_jobs)) if(!(character.job in restricted_jobs)) + if(!master) //Make him the master + master = spawn_bloodling() + if(!master) + return FALSE //yeah okay your shit map doesn't support bloodling RIP + master.key = character.client.ckey + bloodlings += master.mind + qdel(character) //Bye! + //Otherwise, make him a new thrall... + character.mind.add_antag_datum(/datum/antagonist/changeling/bloodling_thrall) /datum/game_mode/bloodling/generate_report() diff --git a/nsv13/code/game/gamemodes/pvp/pvp.dm b/nsv13/code/game/gamemodes/pvp/pvp.dm index e2e82902605..703a8eb2491 100644 --- a/nsv13/code/game/gamemodes/pvp/pvp.dm +++ b/nsv13/code/game/gamemodes/pvp/pvp.dm @@ -16,8 +16,8 @@ GLOBAL_LIST_EMPTY(syndi_crew_leader_spawns) required_players = 24 //40 // 40 to make 20 v 20 required_enemies = 12 //20 recommended_enemies = 15 - antag_flag = ROLE_SYNDI_CREW - enemy_minimum_age = 0 + role_preference = /datum/role_preference/antagonist/pvp + antag_datum = /datum/antagonist/nukeop/syndi_crew announce_span = "danger" announce_text = "The Syndicate are planning an all out assault!\n\ diff --git a/nsv13/code/game/gamemodes/pvp/roles.dm b/nsv13/code/game/gamemodes/pvp/roles.dm index 91c2b025464..98dfa96d77f 100644 --- a/nsv13/code/game/gamemodes/pvp/roles.dm +++ b/nsv13/code/game/gamemodes/pvp/roles.dm @@ -53,7 +53,7 @@ /datum/antagonist/nukeop/syndi_crew name = "Syndicate crew" nukeop_outfit = /datum/outfit/syndicate/no_crystals/syndi_crew - job_rank = ROLE_SYNDI_CREW + banning_key = ROLE_SYNDI_CREW tips = "galactic_conquest" give_objectives = FALSE //Their objective is to win the game @@ -230,7 +230,7 @@ Singleton to handle conquest roles. This exists to populate the roles list and n /datum/antagonist/nukeop/syndi_crew/strategist name = "Syndicate Strategist" nukeop_outfit = /datum/outfit/syndicate/no_crystals/syndi_crew/strategist - job_rank = ROLE_SYNDI_CREW + banning_key = ROLE_SYNDI_CREW /datum/outfit/syndicate/no_crystals/syndi_crew/strategist name = "Syndicate Strategist" @@ -383,7 +383,7 @@ Singleton to handle conquest roles. This exists to populate the roles list and n /datum/antagonist/nukeop/syndi_crew/clown name = "Syndicate Clown" nukeop_outfit = /datum/outfit/syndicate/clownop/no_crystals/jojo_reference - job_rank = ROLE_SYNDI_CREW + banning_key = ROLE_SYNDI_CREW /datum/antagonist/nukeop/syndi_crew/clown/give_alias() owner.current.fully_replace_character_name(owner.current.real_name, owner.current.client.prefs.active_character.custom_names["clown"]) diff --git a/nsv13/code/game/objects/effects/spawners/custom_ghost_role_spawners.dm b/nsv13/code/game/objects/effects/spawners/custom_ghost_role_spawners.dm index 8eadc64edbc..4bee8db8109 100644 --- a/nsv13/code/game/objects/effects/spawners/custom_ghost_role_spawners.dm +++ b/nsv13/code/game/objects/effects/spawners/custom_ghost_role_spawners.dm @@ -29,7 +29,7 @@ . = ..() var/area/A = get_area(src) if(A) - notify_ghosts("A Syndicate Crewmember is about to thaw from cryo on \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_SYNDICATE) + notify_ghosts("A Syndicate Crewmember is about to thaw from cryo on \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE) /obj/effect/mob_spawn/human/nsv13/nt_prisoner/ name = "a prisoner stasis pod" diff --git a/nsv13/code/modules/antagonists/bloodling.dm b/nsv13/code/modules/antagonists/bloodling.dm index 69c3dc87153..e30d74b9320 100644 --- a/nsv13/code/modules/antagonists/bloodling.dm +++ b/nsv13/code/modules/antagonists/bloodling.dm @@ -283,6 +283,7 @@ Infestation! If given a human, it makes them a changeling thrall. If given any o var/antag_hud_type = ANTAG_HUD_BLOODLING var/antag_hud_name = "bloodling_thrall" var/component_type = /datum/component/bloodling + banning_key = ROLE_BLOODLING /datum/antagonist/bloodling/greet() to_chat(owner.current, "We are the master!") @@ -813,7 +814,7 @@ Depending on what creature the entity gives life to, this can be EXTREMELY stron refund_biomass(user, biomass_cost) return FALSE - var/list/candidates = pollCandidatesForMob("Do you want to play as a bloodling minion?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, M, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm + var/list/candidates = pollCandidatesForMob("Do you want to play as a bloodling minion?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, M) // see poll_ignore.dm if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) var/datum/component/bloodling/B = user.GetComponent(/datum/component/bloodling) diff --git a/nsv13/code/modules/antagonists/ghostship/ghost_ship.dm b/nsv13/code/modules/antagonists/ghostship/ghost_ship.dm new file mode 100644 index 00000000000..000247bb89c --- /dev/null +++ b/nsv13/code/modules/antagonists/ghostship/ghost_ship.dm @@ -0,0 +1,8 @@ +/datum/antagonist/ghost_ship + name = "Ghost Ship" + show_name_in_check_antagonists = TRUE + show_in_antagpanel = FALSE + show_in_roundend = FALSE + banning_key = ROLE_GHOSTSHIP + +///Used for tracking and because the role_preferences needs an antag_datum to point at. diff --git a/nsv13/code/modules/antagonists/role_preference/role_antagonists.dm b/nsv13/code/modules/antagonists/role_preference/role_antagonists.dm new file mode 100644 index 00000000000..b46cee28337 --- /dev/null +++ b/nsv13/code/modules/antagonists/role_preference/role_antagonists.dm @@ -0,0 +1,7 @@ +/datum/role_preference/antagonist/bloodling + name = "Bloodling" + antag_datum = /datum/antagonist/bloodling + +/datum/role_preference/antagonist/pvp + name = "Galactic Conquest" + antag_datum = /datum/antagonist/nukeop/syndi_crew diff --git a/nsv13/code/modules/antagonists/role_preference/role_midrounds.dm b/nsv13/code/modules/antagonists/role_preference/role_midrounds.dm new file mode 100644 index 00000000000..38bf28a2bc9 --- /dev/null +++ b/nsv13/code/modules/antagonists/role_preference/role_midrounds.dm @@ -0,0 +1,7 @@ +/datum/role_preference/midround_ghost/ghost_ship + name = "Ghost Ship" + antag_datum = /datum/antagonist/ghost_ship + +/datum/role_preference/midround_ghost/boarder + name = "Boarder" + antag_datum = /datum/antagonist/traitor/boarder diff --git a/nsv13/code/modules/cargo/objective_cargo.dm b/nsv13/code/modules/cargo/objective_cargo.dm index 231b2242a96..15a86587059 100644 --- a/nsv13/code/modules/cargo/objective_cargo.dm +++ b/nsv13/code/modules/cargo/objective_cargo.dm @@ -42,7 +42,7 @@ /obj/structure/closet/crate/large/freight_objective/proc/poll_for_ghost_sentience() for ( var/mob/living/simple_animal/M in contents ) if ( rand( 1, 20 ) == 20 ) // Random sentient mob event! - var/list/candidates = pollCandidatesForMob("Do you want to play as [M]?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, M, POLL_IGNORE_SENTIENCE_POTION) + var/list/candidates = pollCandidatesForMob("Do you want to play as [M]?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, M) M.AIStatus = AI_ON // Keep the mob asleep unless the poll receives no candidates if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) diff --git a/nsv13/code/modules/overmap/ai-skynet.dm b/nsv13/code/modules/overmap/ai-skynet.dm index eab22f31145..ef7f5a28f00 100644 --- a/nsv13/code/modules/overmap/ai-skynet.dm +++ b/nsv13/code/modules/overmap/ai-skynet.dm @@ -716,7 +716,7 @@ Adding tasks is easy! Just define a datum for it. var/target_location = locate(rand(round(world.maxx/2) + 10, world.maxx - 39), rand(40, world.maxy - 39), OM.z) var/obj/structure/overmap/selected_ship = pick(ship_list) var/target_ghost - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [initial(selected_ship.faction)] [initial(selected_ship.name)]?", ROLE_GHOSTSHIP, null, null, 20 SECONDS, POLL_IGNORE_GHOSTSHIP) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [initial(selected_ship.faction)] [initial(selected_ship.name)]?", ROLE_GHOSTSHIP, /datum/role_preference/midround_ghost/ghost_ship, 20 SECONDS, POLL_IGNORE_GHOSTSHIP) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) target_ghost = C diff --git a/nsv13/code/modules/overmap/boarding/boarding.dm b/nsv13/code/modules/overmap/boarding/boarding.dm index 5b96c56c9ed..cdad8f74b11 100644 --- a/nsv13/code/modules/overmap/boarding/boarding.dm +++ b/nsv13/code/modules/overmap/boarding/boarding.dm @@ -34,7 +34,7 @@ //20 or more players? You're allowed "real" boarders. if(player_check >= min_players_for_ghosts) // Remove the low pop boarder camping - candidates = pollCandidatesForMob("Do you want to play as a boarding team member?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 10 SECONDS, src) + candidates = pollCandidatesForMob("Do you want to play as a boarding team member?", ROLE_OPERATIVE, /datum/role_preference/midround_ghost/boarder, 10 SECONDS, src) //No candidates? Well! Guess you get to deal with some KNPCs :)))))) if(!length(candidates)) return spawn_knpcs(amount, faction_selection) diff --git a/nsv13/code/modules/overmap/boarding/ghost_role_spawners.dm b/nsv13/code/modules/overmap/boarding/ghost_role_spawners.dm index 307cbf05edd..ed63d498484 100644 --- a/nsv13/code/modules/overmap/boarding/ghost_role_spawners.dm +++ b/nsv13/code/modules/overmap/boarding/ghost_role_spawners.dm @@ -1,3 +1,25 @@ +/obj/effect/mob_spawn/human/syndicate + name = "Syndicate Operative" + roundstart = FALSE + death = FALSE + icon = 'icons/obj/machines/sleeper.dmi' + icon_state = "sleeper_s" + outfit = /datum/outfit/syndicate_empty + assignedrole = "Space Syndicate" //I know this is really dumb, but Syndicate operative is nuke ops + +/datum/outfit/syndicate_empty + name = "Syndicate Operative Empty" + uniform = /obj/item/clothing/under/syndicate + shoes = /obj/item/clothing/shoes/combat + gloves = /obj/item/clothing/gloves/combat + ears = /obj/item/radio/headset/syndicate/alt + back = /obj/item/storage/backpack + implants = list(/obj/item/implant/weapons_auth) + id = /obj/item/card/id/syndicate + +/datum/outfit/syndicate_empty/post_equip(mob/living/carbon/human/H) + H.faction |= FACTION_SYNDICATE + /datum/outfit/syndicate_empty/boarding/captain name = "Syndicate Captain (Boarding)" id = /obj/item/card/id/syndicate/nuke_leader diff --git a/nsv13/code/modules/overmap/overmap_ghosts.dm b/nsv13/code/modules/overmap/overmap_ghosts.dm index 5f0f1f385ee..56b960f254a 100644 --- a/nsv13/code/modules/overmap/overmap_ghosts.dm +++ b/nsv13/code/modules/overmap/overmap_ghosts.dm @@ -16,7 +16,7 @@ if("Cancel") return if("Open") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [src.faction] [src.name]?", ROLE_GHOSTSHIP, null, null, 20 SECONDS, POLL_IGNORE_GHOSTSHIP) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [src.faction] [src.name]?", ROLE_GHOSTSHIP, /datum/role_preference/midround_ghost/ghost_ship, 20 SECONDS, POLL_IGNORE_GHOSTSHIP) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) target_ghost = C @@ -84,6 +84,10 @@ ghost.hud_type = /datum/hud //Mostly blank hud ghost.key = target.key + //More or less a modified version of how the morph antag gets the antag datum. + if(ghost.mind) + ghost.mind.add_antag_datum(/datum/antagonist/ghost_ship) + //Allows player to hear hails mobs_in_ship += ghost diff --git a/tgui/packages/tgui/interfaces/NotificationPreferences.js b/tgui/packages/tgui/interfaces/NotificationPreferences.js deleted file mode 100644 index 91d992ee420..00000000000 --- a/tgui/packages/tgui/interfaces/NotificationPreferences.js +++ /dev/null @@ -1,41 +0,0 @@ -import { useBackend } from '../backend'; -import { Section, Button } from '../components'; -import { Window } from '../layouts'; - -export const NotificationPreferences = (props, context) => { - const { act, data } = useBackend(context); - - const ignoresPreSort = data.ignore || []; - const ignores = ignoresPreSort.sort((a, b) => { - const descA = a.desc.toLowerCase(); - const descB = b.desc.toLowerCase(); - if (descA < descB) { - return -1; - } - if (descA > descB) { - return 1; - } - return 0; - }); - - return ( - - -
    - {ignores.map(ignore => ( -
    -
    -
    - ); -}; From 785a3286c940f02b52b66cc932e5fe8b0f0a0228 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:56:54 -0700 Subject: [PATCH 23/36] Automatic changelog generation for PR #2488 [ci skip] --- html/changelogs/AutoChangeLog-pr-2488.yml | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2488.yml diff --git a/html/changelogs/AutoChangeLog-pr-2488.yml b/html/changelogs/AutoChangeLog-pr-2488.yml new file mode 100644 index 00000000000..4cc4183c652 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2488.yml @@ -0,0 +1,27 @@ +author: itsmeow, EvilDragonfiend +delete-after: true +changes: + - rscadd: All non-ghost role antagonist preferences are now per-character. Global + toggle buttons toggle the preference on all characters at once. + - refactor: Rewrote antag preference internals to allow for better control of antag + preferences and more distinct usages. + - refactor: Cleaned up antag bans, with more distinct settings and new general categories. + - tweak: All antag prefs will start on by default rather than off. The same applies + to newly added ones. + - bugfix: Fixed various antag bans being bypassable. + - bugfix: Fixed various ghost polls using the wrong preference or ban types. + - rscdel: Removed BYOND account age checks from antag roles in favor of living hours. + - rscdel: Removed "Midround Antagonist" preference in favor of unique preferences + per midround. + - rscadd: Added distinct role preferences for Antagonists (Roundstart/Latejoin) + and Midrounds (Ghost/Living). + - rscadd: Almost all ghost antagonist polls have a "Never for this round" option + now. + - rscdel: Removed the ghost notification preferences menu (poll ignore menu). + - tweak: The lavaland beach bartender can now speak Stoner. + - rscdel: Removed some unused mob spawners from the code. + - rscadd: Added 4hr playtime checks to roundstart/latejoin antag roles. Wizard and + nukeops are 8hr. + - bugfix: Fixed a typo in the skeleton spawner description. + - bugfix: Fixed unnecessary DB queries due to is_banned_from not properly ckey-ifying + the ckey parameter and causing cache misses. From b09a101a55841e75b59655437d503321ebfafce4 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 15 Dec 2023 23:03:46 +0000 Subject: [PATCH 24/36] Automatic changelog compile [ci skip] --- html/changelog.html | 30 +++++++++++++++++++++ html/changelogs/.all_changelog.yml | 33 +++++++++++++++++++++++ html/changelogs/AutoChangeLog-pr-2488.yml | 27 ------------------- html/changelogs/AutoChangeLog-pr-2573.yml | 6 ----- html/changelogs/AutoChangeLog-pr-2582.yml | 5 ---- 5 files changed, 63 insertions(+), 38 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-2488.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-2573.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-2582.yml diff --git a/html/changelog.html b/html/changelog.html index 8486fbfef3e..e7e3e7e3a49 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,36 @@ -->
    +

    15 December 2023

    +

    Bokkiewokkie updated:

    +
      +
    • Added special offers to traders, which have to be unlocked with faction ticket points
    • +
    • Made gun parts available for purchase at Whiterapids' Munitions
    • +
    +

    DeltaFire15 updated:

    +
      +
    • Ethereal starvation no longer has a nutrition window where increased brute vulnerability was not handled.
    • +
    +

    itsmeow, EvilDragonfiend updated:

    +
      +
    • All non-ghost role antagonist preferences are now per-character. Global toggle buttons toggle the preference on all characters at once.
    • +
    • Rewrote antag preference internals to allow for better control of antag preferences and more distinct usages.
    • +
    • Cleaned up antag bans, with more distinct settings and new general categories.
    • +
    • All antag prefs will start on by default rather than off. The same applies to newly added ones.
    • +
    • Fixed various antag bans being bypassable.
    • +
    • Fixed various ghost polls using the wrong preference or ban types.
    • +
    • Removed BYOND account age checks from antag roles in favor of living hours.
    • +
    • Removed "Midround Antagonist" preference in favor of unique preferences per midround.
    • +
    • Added distinct role preferences for Antagonists (Roundstart/Latejoin) and Midrounds (Ghost/Living).
    • +
    • Almost all ghost antagonist polls have a "Never for this round" option now.
    • +
    • Removed the ghost notification preferences menu (poll ignore menu).
    • +
    • The lavaland beach bartender can now speak Stoner.
    • +
    • Removed some unused mob spawners from the code.
    • +
    • Added 4hr playtime checks to roundstart/latejoin antag roles. Wizard and nukeops are 8hr.
    • +
    • Fixed a typo in the skeleton spawner description.
    • +
    • Fixed unnecessary DB queries due to is_banned_from not properly ckey-ifying the ckey parameter and causing cache misses.
    • +
    +

    01 December 2023

    Bokkiewokkie updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 20dcd9588c9..1caba00d14a 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -1828,3 +1828,36 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. covertcorvid: - config: Made repo config client version match the server - server: Increased max recommended client version to 515.1608 +2023-12-15: + Bokkiewokkie: + - rscadd: Added special offers to traders, which have to be unlocked with faction + ticket points + - balance: Made gun parts available for purchase at Whiterapids' Munitions + DeltaFire15: + - bugfix: Ethereal starvation no longer has a nutrition window where increased brute + vulnerability was not handled. + itsmeow, EvilDragonfiend: + - rscadd: All non-ghost role antagonist preferences are now per-character. Global + toggle buttons toggle the preference on all characters at once. + - refactor: Rewrote antag preference internals to allow for better control of antag + preferences and more distinct usages. + - refactor: Cleaned up antag bans, with more distinct settings and new general categories. + - tweak: All antag prefs will start on by default rather than off. The same applies + to newly added ones. + - bugfix: Fixed various antag bans being bypassable. + - bugfix: Fixed various ghost polls using the wrong preference or ban types. + - rscdel: Removed BYOND account age checks from antag roles in favor of living hours. + - rscdel: Removed "Midround Antagonist" preference in favor of unique preferences + per midround. + - rscadd: Added distinct role preferences for Antagonists (Roundstart/Latejoin) + and Midrounds (Ghost/Living). + - rscadd: Almost all ghost antagonist polls have a "Never for this round" option + now. + - rscdel: Removed the ghost notification preferences menu (poll ignore menu). + - tweak: The lavaland beach bartender can now speak Stoner. + - rscdel: Removed some unused mob spawners from the code. + - rscadd: Added 4hr playtime checks to roundstart/latejoin antag roles. Wizard and + nukeops are 8hr. + - bugfix: Fixed a typo in the skeleton spawner description. + - bugfix: Fixed unnecessary DB queries due to is_banned_from not properly ckey-ifying + the ckey parameter and causing cache misses. diff --git a/html/changelogs/AutoChangeLog-pr-2488.yml b/html/changelogs/AutoChangeLog-pr-2488.yml deleted file mode 100644 index 4cc4183c652..00000000000 --- a/html/changelogs/AutoChangeLog-pr-2488.yml +++ /dev/null @@ -1,27 +0,0 @@ -author: itsmeow, EvilDragonfiend -delete-after: true -changes: - - rscadd: All non-ghost role antagonist preferences are now per-character. Global - toggle buttons toggle the preference on all characters at once. - - refactor: Rewrote antag preference internals to allow for better control of antag - preferences and more distinct usages. - - refactor: Cleaned up antag bans, with more distinct settings and new general categories. - - tweak: All antag prefs will start on by default rather than off. The same applies - to newly added ones. - - bugfix: Fixed various antag bans being bypassable. - - bugfix: Fixed various ghost polls using the wrong preference or ban types. - - rscdel: Removed BYOND account age checks from antag roles in favor of living hours. - - rscdel: Removed "Midround Antagonist" preference in favor of unique preferences - per midround. - - rscadd: Added distinct role preferences for Antagonists (Roundstart/Latejoin) - and Midrounds (Ghost/Living). - - rscadd: Almost all ghost antagonist polls have a "Never for this round" option - now. - - rscdel: Removed the ghost notification preferences menu (poll ignore menu). - - tweak: The lavaland beach bartender can now speak Stoner. - - rscdel: Removed some unused mob spawners from the code. - - rscadd: Added 4hr playtime checks to roundstart/latejoin antag roles. Wizard and - nukeops are 8hr. - - bugfix: Fixed a typo in the skeleton spawner description. - - bugfix: Fixed unnecessary DB queries due to is_banned_from not properly ckey-ifying - the ckey parameter and causing cache misses. diff --git a/html/changelogs/AutoChangeLog-pr-2573.yml b/html/changelogs/AutoChangeLog-pr-2573.yml deleted file mode 100644 index e02a644fd5a..00000000000 --- a/html/changelogs/AutoChangeLog-pr-2573.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: Bokkiewokkie -delete-after: true -changes: - - rscadd: Added special offers to traders, which have to be unlocked with faction - ticket points - - balance: Made gun parts available for purchase at Whiterapids' Munitions diff --git a/html/changelogs/AutoChangeLog-pr-2582.yml b/html/changelogs/AutoChangeLog-pr-2582.yml deleted file mode 100644 index f2fc69757ba..00000000000 --- a/html/changelogs/AutoChangeLog-pr-2582.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: DeltaFire15 -delete-after: true -changes: - - bugfix: Ethereal starvation no longer has a nutrition window where increased brute - vulnerability was not handled. From aec1672362eb85fda4e75b17920331cecbd42a4d Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Sat, 16 Dec 2023 00:13:34 +0000 Subject: [PATCH 25/36] Automatic changelog compile [ci skip] --- html/changelog.html | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index e7e3e7e3a49..af75afe6794 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -180,47 +180,6 @@

      Bokkiewokkie updated:

    • Removed the unintended ability for the mission extension to select any random objective
    • Made it possible for admins to select a target for the "clear system" objective
    - -

    14 October 2023

    -

    Bobbanz1 updated:

    -
      -
    • Adds the Hammerhead, Serendipity and Gladius as votable maps to the codebase, server map config is hopefully not affected?
    • -
    • Fixes an issue where exiting a tug would move your camera off-center if the tug had been moved.
    • -
    -

    Bokkiewokkie updated:

    -
      -
    • Removed Randy from the yellow pages
    • -
    • Tweaked some of Randy's item generation code
    • -
    -

    Haliris updated:

    -
      -
    • fixed atmosbots displaying their temperature in Celsius rather than Kelvin.
    • -
    -

    Kenionatus, Bobbanz1 updated:

    -
      -
    • Allows the players to decide through the Cargo Order Console regarding whether or not to receive any mail.
    • -
    -

    SerynEngi updated:

    -
      -
    • AGCNR pump control computers now control the pump pressure on a click instead of an alt-click
    • -
    • AGCNR pump control computer UI now has a cancel button.
    • -
    • Plasma canister to Atlas atmos.
    • -
    • Mining Ready room to Atlas cargo
    • -
    • Disposals fixed for the CE, CMO and RD offices on the Atlas
    • -
    • Fixed the Atlas atmos return line in atmospherics
    • -
    • added AI Core-lawset board to AI upload on the Atlas
    • -
    -

    itsmeow, stylemistake, willox, Bizzonium, AnturK updated:

    -
      -
    • Updated development-side TGUI bootstrapping, updated internal TGUI API
    • -
    • Fixed some bugs related to TGUI Boxes not computing class names properly.
    • -
    • Refactored TGUI asset loading.
    • -
    -

    someone543 updated:

    -
      -
    • fixed Kadesh cruisers from being boarded
    • -
    • borgs can now leftclick to enter transport small crafts
    • -
    GoonStation 13 Development Team From c4477e1f6530d405633c73c7008323d902a096af Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Tue, 19 Dec 2023 00:13:48 +0000 Subject: [PATCH 26/36] Automatic changelog compile [ci skip] --- html/changelog.html | 8 -------- 1 file changed, 8 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index af75afe6794..c9f86f738c2 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -172,14 +172,6 @@

    Bokkiewokkie updated:

  • Added sabre plushie
  • Added mining sabre plushie
  • - -

    17 October 2023

    -

    Bokkiewokkie updated:

    -
      -
    • Fixed random clear missions not having a system
    • -
    • Removed the unintended ability for the mission extension to select any random objective
    • -
    • Made it possible for admins to select a target for the "clear system" objective
    • -
    GoonStation 13 Development Team From 590de2d7a9e8dae16dfbf9daef686ee3aed6f2cf Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 22 Dec 2023 00:13:38 +0000 Subject: [PATCH 27/36] Automatic changelog compile [ci skip] --- html/changelog.html | 9 --------- 1 file changed, 9 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index c9f86f738c2..c9dbc879937 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -163,15 +163,6 @@

    someone543 updated:

    • added back MP start landmark icon
    - -

    20 October 2023

    -

    Bokkiewokkie updated:

    -
      -
    • Added light fighter plushie
    • -
    • Added heavy fighter plushie
    • -
    • Added sabre plushie
    • -
    • Added mining sabre plushie
    • -
    GoonStation 13 Development Team From aa6128b8a2a56026e08be6a40f82eb3e3fc2465f Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 29 Dec 2023 00:11:19 +0000 Subject: [PATCH 28/36] Automatic changelog compile [ci skip] --- html/changelog.html | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/html/changelog.html b/html/changelog.html index c9dbc879937..f01a7a5ec06 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -153,16 +153,6 @@

    someone543 updated:

  • Added coffee machines to PVP ships
  • Syndicate coffee drinkers now get a altered mood event
  • - -

    27 October 2023

    -

    Bobbanz1 updated:

    -
      -
    • Fixes the issue where small crafts such as Sabres, dropships and ghost ships were showing non-existing armour quadrants!
    • -
    -

    someone543 updated:

    -
      -
    • added back MP start landmark icon
    • -
    GoonStation 13 Development Team From dba48051a9a0c5acb5193fa63a66753b86884b1f Mon Sep 17 00:00:00 2001 From: DeltaFire <46569814+DeltaFire15@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:12:24 +0100 Subject: [PATCH 29/36] Fixes FTL pylons getting stuck in the shutdown state (#2586) --- nsv13/code/modules/overmap/FTL/components/drive.dm | 2 ++ nsv13/code/modules/overmap/FTL/components/drive_pylon.dm | 9 +-------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/nsv13/code/modules/overmap/FTL/components/drive.dm b/nsv13/code/modules/overmap/FTL/components/drive.dm index 35323a02579..a1880a21904 100644 --- a/nsv13/code/modules/overmap/FTL/components/drive.dm +++ b/nsv13/code/modules/overmap/FTL/components/drive.dm @@ -363,6 +363,8 @@ Preset classes of FTL drive with pre-programmed behaviours jump_speed_pylon = initial(jump_speed_pylon) if(shutdown_pylons) for(var/obj/machinery/atmospherics/components/binary/drive_pylon/P as() in pylons) + if(P.pylon_state == PYLON_STATE_OFFLINE || P.pylon_state == PYLON_STATE_SHUTDOWN) + continue P.set_state(PYLON_STATE_SHUTDOWN) cooldown = TRUE addtimer(CALLBACK(src, PROC_REF(post_cooldown), auto_spool_enabled), FTL_COOLDOWN) diff --git a/nsv13/code/modules/overmap/FTL/components/drive_pylon.dm b/nsv13/code/modules/overmap/FTL/components/drive_pylon.dm index aefbebf3fc6..f779618c35d 100644 --- a/nsv13/code/modules/overmap/FTL/components/drive_pylon.dm +++ b/nsv13/code/modules/overmap/FTL/components/drive_pylon.dm @@ -387,16 +387,9 @@ else to_chat(user, "You don't think it would be wise to touch this right now.") - -#undef PYLON_STATE_OFFLINE -#undef PYLON_STATE_STARTING -#undef PYLON_STATE_WARMUP -#undef PYLON_STATE_SPOOLING -#undef PYLON_STATE_ACTIVE -#undef PYLON_STATE_SHUTDOWN - #undef MAX_WASTE_OUTPUT_PRESSURE #undef MAX_WASTE_STORAGE_PRESSURE +#undef WASTE_GAS_HEAT #undef PYLON_ACTIVE_EXPONENT #undef POWER_FAIL_TOLERANCE From 9f3496e6d20d716828dadcc522a8aeea5f06ff07 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 29 Dec 2023 13:13:04 -0700 Subject: [PATCH 30/36] Automatic changelog generation for PR #2586 [ci skip] --- html/changelogs/AutoChangeLog-pr-2586.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2586.yml diff --git a/html/changelogs/AutoChangeLog-pr-2586.yml b/html/changelogs/AutoChangeLog-pr-2586.yml new file mode 100644 index 00000000000..bc9a41a02dc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2586.yml @@ -0,0 +1,5 @@ +author: DeltaFire15 +delete-after: true +changes: + - bugfix: Thirring Drive Pylons should no longer be able to get stuck in the "shutting + down" state. From f818ccda15948f8f0b081809de756a07e8b215d1 Mon Sep 17 00:00:00 2001 From: DeltaFire <46569814+DeltaFire15@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:14:15 +0100 Subject: [PATCH 31/36] Syndicate (AI) boarding has a tell again (#2587) --- nsv13/code/modules/overmap/boarding/boarding.dm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nsv13/code/modules/overmap/boarding/boarding.dm b/nsv13/code/modules/overmap/boarding/boarding.dm index cdad8f74b11..39ed9034bb9 100644 --- a/nsv13/code/modules/overmap/boarding/boarding.dm +++ b/nsv13/code/modules/overmap/boarding/boarding.dm @@ -107,6 +107,11 @@ message_admins("KNPC boarder spawn aborted. This ship does not support KNPCs (add some patrol nodes!)") throw EXCEPTION("KNPC boarder spawn aborted. This ship does not support KNPCs (add some patrol nodes!)") + switch(faction_selection) + if("syndicate") + relay('nsv13/sound/effects/ship/boarding_pod.ogg', "You can hear several tethers attaching to the ship.") + else //No other special cases exist but this is a switch anyways to support them in the future (pirates have no tell) + var/obj/structure/closet/supplypod/syndicate_odst/toLaunch = new() var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding] toLaunch.forceMove(shippingLane) From 08104855f70e293eb15417e23c73f79b276f3452 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 29 Dec 2023 13:14:56 -0700 Subject: [PATCH 32/36] Automatic changelog generation for PR #2587 [ci skip] --- html/changelogs/AutoChangeLog-pr-2587.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2587.yml diff --git a/html/changelogs/AutoChangeLog-pr-2587.yml b/html/changelogs/AutoChangeLog-pr-2587.yml new file mode 100644 index 00000000000..4c07d6b7778 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2587.yml @@ -0,0 +1,4 @@ +author: DeltaFire15 +delete-after: true +changes: + - bugfix: Syndicate AI boarders no longer silently breach your hull. From fe7be834c88fcb88b01ee312dd591cf5925a487d Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:03:49 +0000 Subject: [PATCH 33/36] Automatic changelog compile [ci skip] --- html/changelog.html | 7 +++++++ html/changelogs/.all_changelog.yml | 5 +++++ html/changelogs/AutoChangeLog-pr-2586.yml | 5 ----- html/changelogs/AutoChangeLog-pr-2587.yml | 4 ---- 4 files changed, 12 insertions(+), 9 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-2586.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-2587.yml diff --git a/html/changelog.html b/html/changelog.html index f01a7a5ec06..385ec467cea 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,13 @@ -->
    +

    29 December 2023

    +

    DeltaFire15 updated:

    +
      +
    • Syndicate AI boarders no longer silently breach your hull.
    • +
    • Thirring Drive Pylons should no longer be able to get stuck in the "shutting down" state.
    • +
    +

    15 December 2023

    Bokkiewokkie updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 1caba00d14a..f153c4851ae 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -1861,3 +1861,8 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - bugfix: Fixed a typo in the skeleton spawner description. - bugfix: Fixed unnecessary DB queries due to is_banned_from not properly ckey-ifying the ckey parameter and causing cache misses. +2023-12-29: + DeltaFire15: + - bugfix: Syndicate AI boarders no longer silently breach your hull. + - bugfix: Thirring Drive Pylons should no longer be able to get stuck in the "shutting + down" state. diff --git a/html/changelogs/AutoChangeLog-pr-2586.yml b/html/changelogs/AutoChangeLog-pr-2586.yml deleted file mode 100644 index bc9a41a02dc..00000000000 --- a/html/changelogs/AutoChangeLog-pr-2586.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: DeltaFire15 -delete-after: true -changes: - - bugfix: Thirring Drive Pylons should no longer be able to get stuck in the "shutting - down" state. diff --git a/html/changelogs/AutoChangeLog-pr-2587.yml b/html/changelogs/AutoChangeLog-pr-2587.yml deleted file mode 100644 index 4c07d6b7778..00000000000 --- a/html/changelogs/AutoChangeLog-pr-2587.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: DeltaFire15 -delete-after: true -changes: - - bugfix: Syndicate AI boarders no longer silently breach your hull. From 79c3e9b079017747b33eb201a507e96ea321c16c Mon Sep 17 00:00:00 2001 From: Bokkiewokkie <43698041+Bokkiewokkie@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:21:49 +0100 Subject: [PATCH 34/36] Syndicate small craft repainting (#2570) --- .../game/general_quarters/dropship_types.dm | 2 +- .../modules/overmap/fighters/_fighters.dm | 2 +- nsv13/icons/overmap/nanotrasen/carrier.dmi | Bin 8058 -> 10112 bytes nsv13/icons/overmap/syndicate/syn_rapier.dmi | Bin 0 -> 10625 bytes nsv13/icons/overmap/syndicate/syn_raptor.dmi | Bin 8000 -> 0 bytes nsv13/icons/overmap/syndicate/syn_sabre.dmi | Bin 0 -> 9818 bytes nsv13/icons/overmap/syndicate/syn_viper.dmi | Bin 8538 -> 0 bytes 7 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 nsv13/icons/overmap/syndicate/syn_rapier.dmi delete mode 100644 nsv13/icons/overmap/syndicate/syn_raptor.dmi create mode 100644 nsv13/icons/overmap/syndicate/syn_sabre.dmi delete mode 100644 nsv13/icons/overmap/syndicate/syn_viper.dmi diff --git a/nsv13/code/game/general_quarters/dropship_types.dm b/nsv13/code/game/general_quarters/dropship_types.dm index 80bf3107f12..322745c3c05 100644 --- a/nsv13/code/game/general_quarters/dropship_types.dm +++ b/nsv13/code/game/general_quarters/dropship_types.dm @@ -189,7 +189,7 @@ Credit to TGMC for the interior sprites for all these! /obj/structure/overmap/small_craft/transport/sabre/syndicate name = "\improper Syndicate Utility Vessel" desc = "A boarding craft for rapid troop deployment. It contains a full combat medical bay for establishing FOBs." - icon = 'nsv13/icons/overmap/syndicate/syn_raptor.dmi' + icon = 'nsv13/icons/overmap/syndicate/syn_sabre.dmi' req_one_access = list(ACCESS_SYNDICATE) faction = "syndicate" start_emagged = TRUE diff --git a/nsv13/code/modules/overmap/fighters/_fighters.dm b/nsv13/code/modules/overmap/fighters/_fighters.dm index 05fa41abc6d..7b930cdfd01 100644 --- a/nsv13/code/modules/overmap/fighters/_fighters.dm +++ b/nsv13/code/modules/overmap/fighters/_fighters.dm @@ -455,7 +455,7 @@ Been a mess since 2018, we'll fix it someday (probably) /obj/structure/overmap/small_craft/combat/light/syndicate //PVP MODE name = "Syndicate Light Fighter" desc = "The Syndicate's answer to Nanotrasen's light fighter craft, this fighter is designed to maintain aerial supremacy." - icon = 'nsv13/icons/overmap/syndicate/syn_viper.dmi' + icon = 'nsv13/icons/overmap/syndicate/syn_rapier.dmi' req_one_access = ACCESS_SYNDICATE faction = "syndicate" start_emagged = TRUE diff --git a/nsv13/icons/overmap/nanotrasen/carrier.dmi b/nsv13/icons/overmap/nanotrasen/carrier.dmi index d3b665e71983f13ec38aa0df2f125970791c8f32..3f36f9f1806a575650db84bbdffb0352a71702de 100644 GIT binary patch literal 10112 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 diff --git a/nsv13/icons/overmap/syndicate/syn_rapier.dmi b/nsv13/icons/overmap/syndicate/syn_rapier.dmi new file mode 100644 index 0000000000000000000000000000000000000000..2ae4524cf7ac1c2c5d5c67cfeae4e5e4be5e0f70 GIT binary patch literal 10625 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 WK>JxZqQO6l0kFGz;6hV*{dNFhY z>Y<4gk={Lkp(d0-LJ9dc=Y5{ogcgV__?FJ0SHQujr+{;OzT8MZv*nmg1P9$ zY_8Cw7u6S}JMum@d}ydtyGTI8M512ZlT%!yZ>~rBbe0Amb$_!7Q!OiDVv3y1s6wdZ zXtnHgq-alGw&6lzPq4Cg@X3=q11+zm=!okYJqGyjyl?$~?0n*Z(jmVkJ_v;N1cyWi1u)@{v(jyo%YFw&EeQ3zkyX2)*;@%C{+ds|WN^!{K-m;ARj6#mT?_FU6-_2(H znE;p)2B5`+6@YWm5TA8;g6%Q0cg)RQ8W^RerCdSQXTh5E^cE;AEL?HBR`~7PO}&pl zUMT=Tv2Q{C)S7J56pYJHsv|ER1zHSRDQqTM|Dyvh6L`{{%yfd|#al!1GaN4( z3B}wZaDWg$B_%J9q>Ghx9nb;88p8_dLUCY@3gWB1+ITI1VzPQnq z+mj^>A+(bXGl6KKD$gsP;ka`82-yA1%M>4@cEAm;YY;dg%3Ekx!P4@wq5~746T5(< zv~*Vx8=z#8nT5|C-=+zIJ{7_-2ySN09Ml@@;qmzMWfW%cfw7?hCOpBHU**`&9B2Wv znx|YZvhD+^xwYJ%fFHADk54t-nP*tyY&s2LLVkLy4b{`ioTJ z)oT@o@@jY>eu~fp5&^(y?7HkM2u|PLWqr%i@>_kalyi#zqeQ~N(#^P=*L;Enz@o~tH4#h*g(dm~L zHKn7aHtyluFY>)>Z zY)j~hYr|J&u9^`#i$a+$CLnm&+JDV2-j z5sx^{Jm|F^$(6td{pcoKU{J)Crqu=q2cH%eN`-!VfKV*$u_D>y*e{ha3pc^yONxtc zK}z;5STV1g6S9=v(LOaerYf@d`=u1PYZiR9L}~Zy$UTJV{>sPtnrizhVn^OdzL0_D z8fX`{Or9N!p82p6Mk62d5)L-AMDBjKz;s+LaLg8hkM@@Bm+vz09JBSjhW2M8T17Bz zk(-c#WJ5a5-hoXE3CE0$*=+eK=8cVwr8l?Vz>VREB|^yII26Ns!#5+1|pL*m9^QEnRl$&H>F1{JXK?|N#|m##QcTS&F76r z0QE1FV+f!(olh8Nnc?{Lw2RAeSX0PN@Hh5d)NU_#Ss7ZtC?ohT)V3{WAGdN~#muF* ztm0t80@K~^CIq#@T~B8fk(Ne#RlaVVUYZW=Hr@+;LRNu98>O2)EZDL>@v3u`X|1;7 z>2hhtbjjYPLh|3Y2AZkSMr~Bu6iS^NL)kfK+5$NT)2}W!!CDmhT57A1Gn~*;6rir$ z@7*?MaQf1%TXZMd9mZ4^OyRGP)KY5)htdr1w!01vVRqws(=rZzzE1tt(?cT{46+h> zc9nU>I4fxKLoau!J^$#bz*1Ak{l@-AobN)WT}K#qdl5>)ku1V{0o;o}m5PdrSU5R3 zZ?uy_>(85szA@3!(PQ3Lpo{Ea4c^?~Pu;wBUQS4QW?g!b@vbv&j?3VoycEaW2I4zzT=7FXlsE zv%dr{mLBAz=C#Xz^^Z*y$TR3E9$wyuYy7qbIYM-aQ%758ADLTP!X8gr&Ngjt4=CIY zRB^s}DIuh+X<6MpFb#4>QM%6@S{~U@=~h~gBlzuv8uzj}km&=fWT!QX4Wp}3GBFsU z`4g&_t5t=NhYk%Wz}0mxi*gz|P(Wqm!^#r)6D#bQSkPz?NOAu(~ID0+09yOZUSLYh3 z_(eG7R$h|PO|%A`x~#ch^h~ekgsHW)bz78+Ho)K_{EtXl&XSeeTwGi@&R*n^akD_9 zo1kML5jyXnj_8}dq>`h9sF*9z zgXL^mfw9fw3d6(0Y9c3&7eX%Jm(TUBl#PwYv5SpUU7QgVO9Y?dSe-?S*UZ;nK5g3* zy>)N^A+i{85!8F`fBjYd4_UzT-7aOwOy-x<5{bmubwB8;&r6yz|=<&vI1=pPdwxl;tnazMS;h|5+CsR9#tF`Kk!r z)zxKgYnxU9w=9=TsaJ`&?V20f2#t+)j6hQ3t6{r| zHcj+z6a5qSUto0Q0meGw+P~q$`S%9HlS&5;-*}i6PaTD@KN%tO?*LNny0s}awmCG0 zJ+SSzV@c2o_xUpPn*)`86z3ahR*kxu``V!8rHVMy;bD%-c)Wr}8XS;(;?!P~mHSEM zcZ`jVm$VfEN9P&KnTifNS@F+4f5`q(;NjtMHEbGwa^)bF^erAO=d^j|Bs0+e^atb% z{uD|cr$uUuywDagwyO|zrXQfaaBb8H-`>@LaZ<^J%1{8G{O9we10>1=bP9F#UF|)+ z!drEM{GC20FmE23un33N=S$NILI=KbFLxAMpI>gz3^C2@;jA4Gi1e8lwhLF>BS(Jy zGgfme=heKygYN!M4~fao zGUMT20Q1Jps+0xV_(gzYSRM2P_iXjN*+I}#1?C*i4Dl|0Rg2O!^9T+Iu8hnBPU8I7 zgqG!xcP4jDwT)#<>KWgB{TPWv%1Essl3O^mksvX-1^N%EH6O}LvN!}=RzoRr0_5h0^YJalQyR3L+FI*hZ2I8kVz6k7*FSAYvws;L2FF&%) zGAS@}p6=`GTbB7SUYeFv1&g1gQT;L?sigjK3_~%H%bUL64KF0ivWhPbH>2$>9rp)c zxg;hgMz_yGuIUDUsLGj%{HNv*1ezSt>y}%~(}byc)V#-ju5;51%CWCOZpR_6b+|I+ z#tjY|e5~Kft^LgqF5W9sHwf8aDdAJHu}!T;V0-9e_tABJcFA6>2C=v44Ov-#q~#l4 zjJ+b8E^41JBxcA+@0y%SqSoXlbsLJ;XRNHYmqe!QtUUlT!l=HR1?z_=#Ti4Wcyw$CVZ&p8o7*yYK1dW=PK| z3Pn9%`iKAGWnvJzJ}>XA@u&=ayq3dwbfu9T)zDN=(;sZA$wJN}9D9FgV%~Ukgn&A} zK}y@F!oqJuMn@BM8IX0Eny!vDplSk09+o}>ZWrS|5ik6d7gV#^+s3;#YW7W}hcy45 z$7A2_(gy-mBg1drY+D#guJ&-bK7m&UPVpi~F$6&+R9W!=?RliX{%DSO88a9AbB`N+ zWBJXYq*2C)E6zxgzb!<0Zt=LGPY!p>wXYi{G`m9R%v=vrA9QM7Z?hf4dnVsjU)Nn7 z!y7dOtoHMx8guVo#X(aojErIk=i$hN-qiGp_nofBLn_uIm1pWI{=h`t9f3 zEkR!oOlS|G@Y;52IuE7a`?Gf4S;>3iIuAR?MMVB9PWP_8aX-@VCFu&^IEVtb30g%x zB{KP2Qi)M`5RQ+ZAKzR7@j{kQ@xiUOYVq9aWP&WfM8ZMmBHP2@@HA+S-(Ct9dj)r6 zf3`i=Z1#Y~Czy=<`V9oo83FVN# z_uPq}yQW3LerH@9*@6A2SE{IAY<;oi zaj`(V)QEP)6^7tVHHn$!Hph+86Yj;sN|Za!-i*wpo6C&wI4)%Ki?d!?u@J71g6ug|3B2ZWm; ze@ciz$-(&;(BVChERWW$NRtx7o@u~ct$ea51`mTWcji;DmQCXng9FR&nnSdPdn)Go zrSHOJSRF<#-#*TwCBLBxJW?-NWeQ*icFsl28-*u(>@$eb$P$WWzQmdu@p(06RD?){ zODY4Jw!<{%r`<{YRr4%kG3-9FeB~?5zj)JBcyCPv z4Rw1sR-$S}eqC38@`4Ryb}oy2cqlabUPo2HT3WSohy{c|$3j7kgcTC)W5|SHV#{d~ zQkC8gE5KCg9)8;T3re9z$ukQN76fJ-cW9z`x1P0MzQtni=XHOpzaf#3aROkb=Fwn+ z{)=@PCz~nlq-JoS$e^o4%hq4=x2~H}yaS5YscAnPwVm)P`BW?iUrN@>N3|zOi(EdyB)XM=383=KDl_+X|T_d-QamWaVL(Uy|SqNY`1%CM%$Af}2jgj0*iXMYWuxIUcJxr+)yoz?Ny*~I z&^u((Zg0Be#eAk7v@t)fc3s&hKV2HZ@uK%F;mVJy4Hhkpu4NFkL_95q9mhCIBE|+x zJ@EKzhyYh&tDK3gQIRtKtqn15l>dboD9bCE2vCQq5GtERd( zm@t{NOJK@f4xcn!o%dj`AgihbRc=}p4t19fk=^|p%BD{iv}Y2`DlV$1{?tmII2Waf zc8$w~0l!2llLATyhu`m3M)bU4tlX%Pte%Vb?*75Z`{nlx-fv?r|-M{Yc0b z&pwpI+t`kq>8Ph!OeI2GYS^@wrYk{{I`R?&b0c0tgLn)i1n>DRnGaMIy53$ODp`CG z?tHm=E(8+Q(!HW!vIMrrY;y|fx4Gm#^o6WqaL=eT?yj9$uha#i--$M)x=#I?Us*Ia z2$n5lnL(;#2tE>Gmq#05DK^;y9-1Tl!Bin4L2DJpy^PnzZM|QcGJM!6llPQaOK5KE zgh};?M9;N8jDX1>M1Q~*@KjOvzS=TeP8850pz~u=B6r9ah0i6ihkRNTL-ha1i!%*m zL6b_9OT-{2hJ11ol#y4|H^@(ZqZ)t9wI-=bJa;xiz5LsgWV*XM90;YN`_dY@F~RZ? z#Y7cB)2FgLerMcB6FbqeO&XAG_AiV15_>HFFnG%GaB(L#@*27I1mpza9m~L~yl}t+ zrJ0^O&DcH`C|&)%j(=f(bE=}>(YdQMrjh<-5}WSJy1E*PcjAFFfrZ-+z0A8#KSKoE zXlde$CA_Di=~Hz-#wYTTm17!4hPx5a+l};-*XCH*#Mvm>24&NA`5Ie^vym4O&N}%< z$+xx(C?I|s5}P1nYwqRSQ4Ay46E_%8uJ^RZm6i43qEkZ&-U7EZ*)e6fLRCK_2zEse zEwco#N@gFsv1MkuG-yvY9|bKa$hDWDO+1?H#!E2}!K{Ae!I|QVPMaD$e%GtQ9ZJ21 zJ%tV(3NPUZX5=iDS%FAk@DJ}>RJ4}{y+teot;=?u8eBOK0D0FYIU>7c_agsxI{a8a*Z?goHGY8zO-eBy(Rt-L0rE;5W6o z?)S-7!RD)?2nI#kbvJ*HFLeJ~N1CVL9Y`8uY^p(hLjrM+WC8G#D05}9PMf}K_R&|R zk74sOdn7?K%CP^L0rJxr_sUxeCthd-#}=1l>+2dO7zJNX=m;!((!pn@u8+NYm2@28 z8f?CQ(tSJqMZk7tYm7J7+^!5}AM$qPE3d0WM>%OrvAGtW<3Cg9#o8e_+`Bt@@a#!}PUb*q)|xDKv@mL!MqnI}E7 z*{=;rKN!%2g~rYN#V=&Lyu$=PR4_1sc1=~>vnY!>@*xw_>IxO35@uVe6(8oA)h+IY zMAvY>H#wFWboV)^D~U$07;8I8HW9YZmxTPjbJ&)}FgpRLghmQ>Ys7mu`F2HW+t$y% zhT(Tdr4`$DEHy)2=3c7_-pyKl;yoEcq&7-z8V*f-YHp{3eX%kYr}gLgHkmbNa0Or8 z_f<;64dpch^>neNyE|zXyUSg@_9sVc4@4m9??h69jozHq43ZL2==ir3AczT{gWQLg z?LqeM-*Fca_~kUbN220aqRKHY+f`XLak>h=W~C2x_7>hdB~<&qd3T&xVzTWmluzhMQe)AFEOPnKf)U1GAW#ZYFXUyC;H+IOl8*A9{v|JvN5Z-47f+CRk@HR z-QVt5%)e@SlWMN`j$QRH2nW}{035V5Z-g9?953X_wfG~_Pun1pLyhG_U$y_?FV*xd zLEcAy+du+^vg`F1Rx_cU4)g48>rditvd;v~`*RCV8X*&H!Y3(pdFkQV91m?_wzlF3 z=ylg=T&g^`J59s#sl5k%Mj~{P?Rb_^*fq0~?pW2jp!B7hwkA0)%0iB-_+8TUq&bQ2tH+XYZEs+q`oj_rYF!l8PNZ6;k7UJuEKy}u07|2Dz zQ0`U5^#MFLr7CkT#(n>(_&&CIImNOxMke@vM)X6R3D$hL6ao@sW&iOzmVEPH{Qti? zfvhteFd)wrMANh>&ejo$-TiW-a@r&?*`iTZnz+^zZRx6~JTRI2h0A?_*Rf#1#E_d| za7;wuW!N@SQT&F;%t4MkY(^dmZD(fVR2H?;e4A7J*Olq+t6T8btBl27#@U_4K%$LA zY=F0w@^%gaD-}`yPXD?l-_h%3Dxvmgz;kh^s?aFMGLsS=%~2Kuf-XTG3aWue9u~s? s`)>U_C40Z19c~}~7vR4t=W?JK_N7 Date: Wed, 3 Jan 2024 08:22:32 -0700 Subject: [PATCH 35/36] Automatic changelog generation for PR #2570 [ci skip] --- html/changelogs/AutoChangeLog-pr-2570.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2570.yml diff --git a/html/changelogs/AutoChangeLog-pr-2570.yml b/html/changelogs/AutoChangeLog-pr-2570.yml new file mode 100644 index 00000000000..8de6135cd05 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2570.yml @@ -0,0 +1,5 @@ +author: Bokkiewokkie +delete-after: true +changes: + - imageadd: Changed Syndicate small craft color scheme to be more red + - code_imp: Renamed syndicate sabre and rapier sprite files From b6fc4ce9675fd6139bda1cfc51b6ccf4a255cf85 Mon Sep 17 00:00:00 2001 From: ss13-beebot <56381746+ss13-beebot@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:04:06 +0000 Subject: [PATCH 36/36] Automatic changelog compile [ci skip] --- html/changelog.html | 7 +++++++ html/changelogs/.all_changelog.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-2570.yml | 5 ----- 3 files changed, 11 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-2570.yml diff --git a/html/changelog.html b/html/changelog.html index 385ec467cea..2586070a7a1 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,13 @@ -->
      +

      03 January 2024

      +

      Bokkiewokkie updated:

      +
        +
      • Changed Syndicate small craft color scheme to be more red
      • +
      • Renamed syndicate sabre and rapier sprite files
      • +
      +

      29 December 2023

      DeltaFire15 updated:

        diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index f153c4851ae..f57aa3b2506 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -1866,3 +1866,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - bugfix: Syndicate AI boarders no longer silently breach your hull. - bugfix: Thirring Drive Pylons should no longer be able to get stuck in the "shutting down" state. +2024-01-03: + Bokkiewokkie: + - imageadd: Changed Syndicate small craft color scheme to be more red + - code_imp: Renamed syndicate sabre and rapier sprite files diff --git a/html/changelogs/AutoChangeLog-pr-2570.yml b/html/changelogs/AutoChangeLog-pr-2570.yml deleted file mode 100644 index 8de6135cd05..00000000000 --- a/html/changelogs/AutoChangeLog-pr-2570.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Bokkiewokkie -delete-after: true -changes: - - imageadd: Changed Syndicate small craft color scheme to be more red - - code_imp: Renamed syndicate sabre and rapier sprite files