aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/labeler.yml9
-rw-r--r--.github/nativefuncs.json25
-rw-r--r--.github/pull_request_template.md8
-rw-r--r--.github/workflows/auto-label-pr.yml14
-rw-r--r--.github/workflows/compile-check.yml2
-rw-r--r--.github/workflows/encoding.yml8
-rw-r--r--.github/workflows/merge-conflict-auto-label.yml3
-rw-r--r--CONTRIBUTING.md50
-rw-r--r--Northstar.Client/keyvalues/resource/fontfiletable.txt10
-rw-r--r--Northstar.Client/mod/resource/fontfiletable.txt40
-rw-r--r--Northstar.Client/mod/resource/northstar_client_localisation_english.txt8
-rw-r--r--Northstar.Client/mod/resource/northstar_client_localisation_french.txt26
-rw-r--r--Northstar.Client/mod/resource/northstar_client_localisation_german.txt68
-rw-r--r--Northstar.Client/mod/resource/northstar_client_localisation_italian.txt39
-rw-r--r--Northstar.Client/mod/resource/northstar_client_localisation_japanese.txt73
-rw-r--r--Northstar.Client/mod/resource/northstar_client_localisation_polish.txt394
-rw-r--r--Northstar.Client/mod/resource/northstar_client_localisation_portuguese.txt40
-rw-r--r--Northstar.Client/mod/resource/northstar_client_localisation_russian.txt24
-rw-r--r--Northstar.Client/mod/resource/northstar_client_localisation_spanish.txt1
-rw-r--r--Northstar.Client/mod/resource/northstar_client_localisation_tchinese.txt28
-rw-r--r--Northstar.Client/mod/resource/ui/menus/mode_select.menu343
-rw-r--r--Northstar.Client/mod/scripts/vscripts/cl_northstar_client_init.nut3
-rw-r--r--Northstar.Client/mod/scripts/vscripts/presence/cl_presence.nut13
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_ns_connect_password.nut2
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_ns_moddownload.nut35
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_ns_modmenu.nut4
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut109
-rw-r--r--Northstar.Custom/keyvalues/playlists_v2.txt81
-rw-r--r--Northstar.Custom/mod.json13
-rw-r--r--Northstar.Custom/mod/models/weapons/shotgun_doublebarrel/ptpov_shotgun_doublebarrel.mdlbin1268987 -> 1277105 bytes
-rw-r--r--Northstar.Custom/mod/models/weapons/shotgun_doublebarrel/w_shotgun_doublebarrel.mdlbin743828 -> 924194 bytes
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/_testing.nut302
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/earn_meter/cl_earn_meter.gnut6
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_inf.gnut11
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/sh_damage_types.nut4
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/weapons/_arc_cannon.nut46
-rw-r--r--Northstar.Custom/paks/mp_weapon_shotgun_doublebarrel.rpakbin264566 -> 197556 bytes
-rw-r--r--Northstar.Custom/paks/mp_weapon_shotgun_doublebarrel.starpakbin16539736 -> 9605416 bytes
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_items.nut2
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_loadouts_mp.gnut20
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut16
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut10
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/faction_xp.gnut9
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut10
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut6
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut41
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut607
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut33
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut22
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut7
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut31
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/item_inventory/sv_item_inventory.gnut2
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut2
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_challenges.gnut270
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut190
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_codecallbacks.gnut25
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut530
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_score.nut19
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_stats.nut39
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut4
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut771
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/sh_progression.nut24
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/titan_xp.gnut22
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/weapon_xp.gnut7
-rw-r--r--README.md2
68 files changed, 3383 insertions, 1183 deletions
diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 000000000..cc83d7cd3
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,9 @@
+# Add 'needs code review' label to any changes within the entire repository
+needs code review:
+- changed-files:
+ - any-glob-to-any-file: '**'
+
+# Add 'needs testing' label to any changes within the entire repository
+needs testing:
+- changed-files:
+ - any-glob-to-any-file: '**'
diff --git a/.github/nativefuncs.json b/.github/nativefuncs.json
index 1148d3e58..110429031 100644
--- a/.github/nativefuncs.json
+++ b/.github/nativefuncs.json
@@ -19,6 +19,12 @@
"argTypes":"string modName, bool enabled"
},
{
+ "name":"NSIsModRemote",
+ "helpText":"",
+ "returnTypeString":"bool",
+ "argTypes":"string modName"
+ },
+ {
"name":"NSGetModDescriptionByModName",
"helpText":"",
"returnTypeString":"string",
@@ -267,6 +273,12 @@
"argTypes":"string modName, bool enabled"
},
{
+ "name":"NSIsModRemote",
+ "helpText":"",
+ "returnTypeString":"bool",
+ "argTypes":"string modName"
+ },
+ {
"name":"NSGetModDescriptionByModName",
"helpText":"",
"returnTypeString":"string",
@@ -467,6 +479,12 @@
"argTypes":"string modName, bool enabled"
},
{
+ "name":"NSIsModRemote",
+ "helpText":"",
+ "returnTypeString":"bool",
+ "argTypes":"string modName"
+ },
+ {
"name":"NSGetModDescriptionByModName",
"helpText":"",
"returnTypeString":"string",
@@ -503,6 +521,13 @@
"argTypes":"string modName"
},
{
+ "name": "NSFetchVerifiedModsManifesto",
+ "helpText": "Retrieves the verified mods list from the central authority (GitHub).",
+ "returnTypeString": "void",
+ "argTypes": ""
+
+ },
+ {
"name": "NSIsModDownloadable",
"helpText": "checks whether a mod is verified and can be auto-downloaded",
"returnTypeString": "bool",
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 8cda06a32..e4dd3515d 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -16,3 +16,11 @@ Note that commit messages in PRs will generally be squashed to keep commit histo
-->
Replace this line with a description of your change (and screenshots/screenrecordings if applicable).
+
+### Code review:
+
+Replace this line with anything specific to look out for during code reviews.
+
+### Testing:
+
+Replace this line with instructions on how to test your pull request. The more detailed, the easier it is for reviewers to test, the faster your PR gets merged.
diff --git a/.github/workflows/auto-label-pr.yml b/.github/workflows/auto-label-pr.yml
new file mode 100644
index 000000000..659ff351d
--- /dev/null
+++ b/.github/workflows/auto-label-pr.yml
@@ -0,0 +1,14 @@
+name: Auto-Labeler
+on:
+ pull_request_target:
+ types:
+ - opened
+
+jobs:
+ labeler:
+ permissions:
+ contents: read
+ pull-requests: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/labeler@v5
diff --git a/.github/workflows/compile-check.yml b/.github/workflows/compile-check.yml
index cb7ab1d08..8803f4f4c 100644
--- a/.github/workflows/compile-check.yml
+++ b/.github/workflows/compile-check.yml
@@ -8,7 +8,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout Repo
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: "mods"
diff --git a/.github/workflows/encoding.yml b/.github/workflows/encoding.yml
index a88e3961a..b1d851a14 100644
--- a/.github/workflows/encoding.yml
+++ b/.github/workflows/encoding.yml
@@ -3,19 +3,19 @@ on: [push, pull_request]
jobs:
check-loc-encoding:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Check localization files encoding
run: |
files=$(ls Northstar.Client/mod/resource/northstar_client_localisation_*.txt)
IFS=$'\n'; files=($files); unset IFS; ! file --mime "${files[@]}" | grep -v "charset=utf-16le"
check-missing-translations:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Look out for missing translations
run: node .github/build/find-missing-translations.js
continue-on-error: true
diff --git a/.github/workflows/merge-conflict-auto-label.yml b/.github/workflows/merge-conflict-auto-label.yml
index e237726af..abb7cabde 100644
--- a/.github/workflows/merge-conflict-auto-label.yml
+++ b/.github/workflows/merge-conflict-auto-label.yml
@@ -1,8 +1,11 @@
name: Merge Conflict Auto Label
on:
+ workflow_dispatch: # Manual run
push:
branches:
- main
+ schedule:
+ - cron: "10 21 * * *" # Runs at 21:10; time was chosen based on contributor activity and low GitHub Actions cron load.
jobs:
triage:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..64b8c0c3c
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,50 @@
+# Contributing
+> NOTE: This is the first iteration of this file. You're welcome to pull request changes
+
+### Contents
+- [Making issues](#Making-issues)
+- [Making pull requests](#Making-pull-requests)
+- [Formatting code](#Formatting-code)
+
+## Making issues
+When creating issues, whether to track a bug or suggest a feature, please try to follow this set of rules:
+1. When filing a bug report issue, please attach a log file ( Located in `R2Northstar/logs/` ).
+2. **Short, consise.** No-one wants to read an essay on why x should be added.
+3. When applicable attach a short video / screen shots to better convey what the issue is about.
+
+## Making pull requests
+When creating a pull request please follow this set of rules:
+1. **1 Fix/Feature should equal to 1 Pull Request.** The more you do in 1 PR the longer it'll take to merge.
+2. Mark your Pull Request as draft if it isnt finished just yet.
+3. Properly format your code. As we currently don't have a formatter we're very lax on this. That doesn't mean you don't have to try to format your code.
+4. **Mention how to test your changes / add a test mod to make it easier to test**
+
+## Formatting code
+A basic set of rules you should follow when creating a Pull Request
+
+### Comment your code
+- If you're adding a new file you should add a doc comment noting what the file does and its origin
+ ```cpp
+ ///-----------------------------------------------------------------------------
+ /// Origin: Northstar
+ /// Purpose: handles server-side rui
+ ///-----------------------------------------------------------------------------
+ ```
+ Alternative to `Origin: Northstar` would be `Origin: Respawn`
+- Each function should have a header doc comment
+ ```cpp
+ ///-----------------------------------------------------------------------------
+ /// Sends a string message to player
+ /// Returns true if it succeeded
+ ///-----------------------------------------------------------------------------
+ bool function NSSendInfoMessageToPlayer( entity player, string text )
+ ```
+### Functions
+- Functions should have spaces in the parentheses
+ ```cpp
+ bool function NSSendInfoMessageToPlayer( entity player, string text )
+ ```
+- If a function need to be threaded off using `thread` it should have a `_Threaded` suffix
+
+### File
+- Files should use tabs for indentation
diff --git a/Northstar.Client/keyvalues/resource/fontfiletable.txt b/Northstar.Client/keyvalues/resource/fontfiletable.txt
new file mode 100644
index 000000000..f0b91c07e
--- /dev/null
+++ b/Northstar.Client/keyvalues/resource/fontfiletable.txt
@@ -0,0 +1,10 @@
+FontFileTable
+{
+ "arial unicode ms" "resource/Lato-Regular.ttf"
+
+ "lucida console" "resource/NorthstarMono.ttf" [$PC]
+
+ "arial" "resource/Lato-Regular.ttf"
+ "arial bold" "resource/Lato-Regular.ttf"
+ "arial narrow" "resource/Lato-Regular.ttf"
+}
diff --git a/Northstar.Client/mod/resource/fontfiletable.txt b/Northstar.Client/mod/resource/fontfiletable.txt
deleted file mode 100644
index b42381341..000000000
--- a/Northstar.Client/mod/resource/fontfiletable.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-FontFileTable
-{
- "Default" "resource/MetronicPro-Regular.vfont" [!$JAPANESE && !$TCHINESE]
- "Default" "resource/NotoSansJP-Regular.vfont" [$JAPANESE]
- "Default" "resource/NotoSansTC-Regular.vfont" [$TCHINESE]
-
- "DefaultBold" "resource/MetronicPro-SemiBold.vfont" [!$JAPANESE && !$TCHINESE]
- "DefaultBold" "resource/NotoSansJP-Regular.vfont" [$JAPANESE]
- "DefaultBold" "resource/NotoSansTC-Regular.vfont" [$TCHINESE]
-
- "Titanfall" "resource/Titanfall-Regular.vfont" [!$JAPANESE && !$TCHINESE && !$RUSSIAN]
- "Titanfall" "resource/NotoSansJP-Regular.vfont" [$JAPANESE]
- "Titanfall" "resource/NotoSansTC-Regular.vfont" [$TCHINESE]
- "Titanfall" "resource/MetronicPro-SemiBold.vfont" [$RUSSIAN]
-
- "marlett" "vgui/fonts/marlett.ttf"
-
- // Everything below is DONOTSHIP / Dev only
-
- "arial unicode ms" "resource/Lato-Regular.ttf"
-
- "lucida console" "resource/NorthstarMono.ttf" [$PC]
- "lucida console" "resource/MetronicPro-Regular.vfont" [$GAMECONSOLE]
-
- "tahoma" "fonts\\tahoma.ttf" [$PC]
- "tahoma" "resource/MetronicPro-Regular.vfont" [$GAMECONSOLE]
-
- "tahoma bold" "fonts\\tahomabd.ttf" [$PC]
- "tahoma bold" "resource/MetronicPro-SemiBold.vfont" [$GAMECONSOLE]
-
- "courier new" "fonts\\cour.ttf" [$PC]
- "courier new" "vgui/fonts/cour.ttf" [$GAMECONSOLE]
-
- "times new roman" "fonts\\times.ttf" [$PC]
- "times new roman" "vgui/fonts/times.ttf" [$GAMECONSOLE]
-
- "arial" "resource/Lato-Regular.ttf"
- "arial bold" "resource/Lato-Regular.ttf"
- "arial narrow" "resource/Lato-Regular.ttf"
-}
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_english.txt b/Northstar.Client/mod/resource/northstar_client_localisation_english.txt
index e6518febb..29d7cddf8 100644
--- a/Northstar.Client/mod/resource/northstar_client_localisation_english.txt
+++ b/Northstar.Client/mod/resource/northstar_client_localisation_english.txt
@@ -320,7 +320,7 @@ Press Yes if you agree to this. This choice can be changed in the mods menu at a
"UNAUTHORIZED_PWD" "Wrong password"
"STRYDER_RESPONSE" "Couldn't parse stryder response"
"PLAYER_NOT_FOUND" "Couldn't find player account"
- "INVALID_MASTERSERVER_TOKEN" "Invalid or expired masterserver token"
+ "INVALID_MASTERSERVER_TOKEN" "Invalid or expired masterserver token, try restarting EA App."
"JSON_PARSE_ERROR" "Error parsing json response"
"UNSUPPORTED_VERSION" "The version you are using is no longer supported"
@@ -373,12 +373,14 @@ Press Yes if you agree to this. This choice can be changed in the mods menu at a
"WRONG_MOD_VERSION" "Server has mod \"%s1\" v%s2 while you have v%s3"
"MOD_NOT_VERIFIED" "(mod is not verified, and couldn't be downloaded automatically)"
"MOD_DL_DISABLED" "(automatic mod downloading is disabled)"
+ "MANIFESTO_FETCHING_TITLE" "Setting up mod download"
+ "MANIFESTO_FETCHING_TEXT" "Retrieving the list of verified mods..."
"DOWNLOADING_MOD_TITLE" "Downloading mod"
"DOWNLOADING_MOD_TITLE_W_PROGRESS" "Downloading mod (%s1%)"
"DOWNLOADING_MOD_TEXT" "Downloading %s1 v%s2..."
"DOWNLOADING_MOD_TEXT_W_PROGRESS" "Downloading %s1 v%s2...\n(%s3/%s4 MB)"
- "CHECKSUMING_TITLE" "Checksuming mod"
- "CHECKSUMING_TEXT" "Verifying contents of %s1 v%s2..."
+ "CHECKSUMING_TITLE" "Verifying mod integrity"
+ "CHECKSUMING_TEXT" "Validating files of %s1 v%s2..."
"EXTRACTING_MOD_TITLE" "Extracting mod (%s1%)"
"EXTRACTING_MOD_TEXT" "Extracting %s1 v%s2...\n(%s3/%s4 MB)"
"FAILED_DOWNLOADING" "Failed downloading mod"
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_french.txt b/Northstar.Client/mod/resource/northstar_client_localisation_french.txt
index b0e579cff..9444a39e3 100644
--- a/Northstar.Client/mod/resource/northstar_client_localisation_french.txt
+++ b/Northstar.Client/mod/resource/northstar_client_localisation_french.txt
@@ -316,7 +316,7 @@ Choisissez Oui si vous êtes d'accord. Ce choix peut être modifié à tout inst
"UNAUTHORIZED_PWD" "Mot de passe incorrect"
"STRYDER_RESPONSE" "Impossible d'analyser la réponse de Stryder"
"PLAYER_NOT_FOUND" "Impossible de trouver le compte du joueur"
- "INVALID_MASTERSERVER_TOKEN" "Jeton du server maître invalide ou expiré"
+ "INVALID_MASTERSERVER_TOKEN" "Token du server maître invalide ou expiré, veuillez relancer l'application EA."
"JSON_PARSE_ERROR" "Une erreur est survenue durant l'analyse JSON"
"UNSUPPORTED_VERSION" "La version que vous utilisez n'est plus supportée"
@@ -348,7 +348,7 @@ Choisissez Oui si vous êtes d'accord. Ce choix peut être modifié à tout inst
"PROGRESSION_ENABLED_HEADER" "Progression activée !"
"PROGRESSION_DISABLED_HEADER" "Progression désactivée !"
"PROGRESSION_DISABLED_BODY" "^CCCC0000La progression a été désactivée.^\n\nLes Titans, Armes, Factions, Skins, et autres seront débloqués et utilisables en tout temps.\n\nPeut être changé à n'importe que moment dans le salon multijoueurs."
- "PROGRESSION_TOGGLE_DISABLED_BODY" "Les Titans, Armes, Factions, Skins et autres seront débloqués par la monté en niveau ou par leur achats en mérites.\n\nPeut être changé à n'importe que moment dans le salon multijoueurs.\n\n^CC000000Warning : Si vous équiper des objets que vous n'avez pas encore débloqués, ils seront déséquipés !"
+ "PROGRESSION_TOGGLE_DISABLED_BODY" "Les Titans, Armes, Factions, Skins et autres seront débloqués par la monté en niveau ou par leur achats en mérites.\n\nPeut être changé à n'importe que moment dans le salon multijoueurs.\n\n^CC000000Warning : Si vous équipez des objets que vous n'avez pas encore débloqués, ils seront déséquipés !"
"PROGRESSION_ENABLED_BODY" "^CCCC0000La progression a été activée.^\n\nLes Titans, Armes, Factions, Skins et autres seront débloqués par la monté en niveau ou par leur achats en mérites.\n\nPeut être changé à n'importe que moment dans le salon multijoueurs."
"TOGGLE_PROGRESSION" "Activer la progression"
"Y_BUTTON_TOGGLE_PROGRESSION" "%[Y_BUTTON|]% Activer la progression"
@@ -357,5 +357,27 @@ Choisissez Oui si vous êtes d'accord. Ce choix peut être modifié à tout inst
"AUTHENTICATION_FAILED_HELP" "Aide"
"AUTHENTICATION_FAILED_ERROR_CODE" "Code d'erreur : ^DB6F2C00%s1^"
"AUTHENTICATION_FAILED_BODY" "L'authentification avec Atlas a échoué."
+ "MISSING_MOD" "Mod manquant \"%s1\" v%s2"
+ "MOD_REQUIRED_WARNING" " : Ce mod peut être (dé)chargé automatiquement en rejoignant un serveur"
+ "EXTRACTING_MOD_TITLE" "Extraction du mod (%s1%)"
+ "MOD_NOT_VERIFIED" "(ce mod n'est pas vérifié, et n'a donc pas pu être automatiquement téléchargé)"
+ "MOD_DL_DISABLED" "(le téléchargement automatique de mods est désactivé)"
+ "DOWNLOADING_MOD_TITLE" "Téléchargement du mod"
+ "DOWNLOADING_MOD_TITLE_W_PROGRESS" "Téléchargement du mod (%s1%)"
+ "DOWNLOADING_MOD_TEXT" "Téléchargement de %s1 v%s2..."
+ "WRONG_MOD_VERSION" "Le serveur requiert la version v%s2 du mod \"%s1\" (vous avez la version v%s3)"
+ "DOWNLOADING_MOD_TEXT_W_PROGRESS" "Téléchargement de %s1 v%s2...\n(%s3/%s4 Mo)"
+ "CHECKSUMING_TITLE" "Vérification de la somme de contrôle du mod"
+ "CHECKSUMING_TEXT" "Vérification du contenu de %s1 v%s2..."
+ "EXTRACTING_MOD_TEXT" "Extraction de %s1 v%s2...\n(%s3/%s4 Mo)"
+ "FAILED_DOWNLOADING" "Echec du téléchargement du mod"
+ "FAILED_READING_ARCHIVE" "Une erreur est survenue lors de la lecture de l'archive."
+ "FAILED_WRITING_TO_DISK" "Une erreur est survenue lors de l'extraction des fichiers."
+ "MOD_FETCHING_FAILED" "L'archive n'a pas pu être téléchargée depuis Thunderstore."
+ "MOD_CORRUPTED" "La somme de contrôle de l'archive ne correspond pas à la signature vérifiée."
+ "NO_DISK_SPACE_AVAILABLE" "L'espace restant sur votre disque est insuffisant."
+ "MOD_FETCHING_FAILED_GENERAL" "L'extraction du mod a échoué. Consultez le journal pour plus d'informations."
+ "MANIFESTO_FETCHING_TITLE" "Préparation du téléchargement du mod"
+ "MANIFESTO_FETCHING_TEXT" "Récupération de la liste des mods vérifiés..."
}
}
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_german.txt b/Northstar.Client/mod/resource/northstar_client_localisation_german.txt
index f1994a243..726ad6ac8 100644
--- a/Northstar.Client/mod/resource/northstar_client_localisation_german.txt
+++ b/Northstar.Client/mod/resource/northstar_client_localisation_german.txt
@@ -17,9 +17,9 @@
Drücke Ja, um zuzustimmen. Du kannst diese Entscheidung jederzeit im Modmenü ändern."
"BACK_AUTHENTICATION_AGREEMENT" "Authentifizierungs-Einwilligung"
"AUTHENTICATION_AGREEMENT" "Authentifizierungs-Einwilligung"
- "AUTHENTICATION_AGREEMENT_RESTART" "Ein Neustart ist notwendig, um diese Änderung zu übernehmen"
+ "AUTHENTICATION_AGREEMENT_RESTART" "Ein Neustart ist notwendig, um diese Änderung zu übernehmen."
- "DIALOG_AUTHENTICATING_MASTERSERVER" "Authentifizierung mit Master Server"
+ "DIALOG_AUTHENTICATING_MASTERSERVER" "Authentifizierung mit Master Server."
"AUTHENTICATIONAGREEMENT_NO" "Du hast dich gegen die Authentifizierung mit Northstar entschieden. Du kannst die Authentifizierungs-Einwilligung im Modmenü ansehen."
"MENU_TITLE_SERVER_BROWSER" "Server Browser"
@@ -312,5 +312,69 @@ Drücke Ja, um zuzustimmen. Du kannst diese Entscheidung jederzeit im Modmenü
"UNSUPPORTED_VERSION" "Die Version die du benutzt ist nicht länger unterstützt"
"SNS_LEADER_BANKRUPT_SUB" "%s1 Wurde Von %s2 Zurückgesetzt"
"SNS_BANKRUPT_SUB" "Dein Punkestand wurde von %s1 zurückgesetzt"
+ "respawnprotection" "Respawn Schutzdauer"
+ "SNS_BANKRUPT" "Bankrott!"
+ "SNS_LEADER_BANKRUPT" "Punktzahlführer Bankrott!"
+ "sns_reset_pulse_blade_cooldown_on_pulse_blade_kill" "Kill Cooldown Zurücksetzungen"
+ "player_force_respawn" "Erzwungener Respawn"
+ "SHOW_ONLY_NOT_REQUIRED" "Nur optionale Mods"
+ "SHOW_ONLY_REQUIRED" "Nur notwendige Mods"
+ "PROGRESSION_TOGGLE_DISABLED_HEADER" "Fortschritt aktivieren?"
+ "TOGGLE_PROGRESSION" "Fortschritt umschalten"
+ "PROGRESSION_TOGGLE_ENABLED_HEADER" "Fortschritt deaktivieren?"
+ "PROGRESSION_TOGGLE_ENABLED_BODY" "Titans, Waffen, Fraktionen, Skins, usw werden freigeschaltet und sind zu jeder Zeit verfügbar .\n\nDies kann in der Mehrspielerlobby zu jedem Zeitpunkt geändert werden."
+ "MATCH_COUNTDOWN_LENGTH" "Countdown für privates Match"
+ "LOG_UNKNOWN_CLIENTCOMMANDS" "Unbekannte Clientbefehle loggen"
+ "DISALLOWED_TACTICALS" "Verbotene Taktiken"
+ "TACTICAL_REPLACEMENT" "Taktischer Austausch"
+ "AUTHENTICATION_FAILED_HEADER" "Authentifizierung fehlgeschlagen"
+ "AUTHENTICATION_FAILED_BODY" "Authentifizierung mit Atlas fehlgeschlagen!"
+ "AUTHENTICATION_FAILED_ERROR_CODE" "Fehlercode: ^DB6F2C00%s1^"
+ "AUTHENTICATION_FAILED_HELP" "Hilfe"
+ "NORTHSTAR_BASE_SETTINGS" "Northstar Grundeinstellungen"
+ "ONLY_HOST_MATCH_SETTINGS" "Nur der Host kann die Einstellungen eines privaten Matches ändern"
+ "ONLY_HOST_CAN_START_MATCH" "Nur der Host kann das Match starten"
+ "MISSING_MOD" "Fehlender Mod \"%s1\" v%s2"
+ "MOD_DL_DISABLED" "(automatisches Herunterladen ist deaktiviert)"
+ "DOWNLOADING_MOD_TITLE" "Lade Mod herunter"
+ "DOWNLOADING_MOD_TITLE_W_PROGRESS" "Lade Mod (%s1%)"
+ "DOWNLOADING_MOD_TEXT" "Lade %s1 v%s2..."
+ "DOWNLOADING_MOD_TEXT_W_PROGRESS" "Lade %s1 v%s2...\n(%s3/%s4 MB)"
+ "CHECKSUMING_TITLE" "Überprüfe Mod Prüfsumme"
+ "MOD_NOT_VERIFIED" "(mod ist nicht verifiziert und kann nicht automatisch herunterladen werden)"
+ "MOD_REQUIRED_WARNING" " : Dieser Mod könnte Sie (un)ausgestattet hinterlassen, sobald Sie einem Server beitreten"
+ "MOD_SETTINGS" "Mod Einstellungen"
+ "DISALLOWED_WEAPONS" "Verbotene Waffen"
+ "REPLACEMENT_WEAPON" "Austausch Waffen"
+ "SHOULD_RETURN_TO_LOBBY" "Zur Lobby nach Matchende zurückkehren"
+ "ARE_YOU_SURE" "Sind Sie sich sicher?"
+ "MOD_SETTINGS_SERVER" "Server"
+ "MOD_SETTINGS_RESET" "Zurücksetzen"
+ "MOD_SETTINGS_RESET_ALL" "Alles zurücksetzen"
+ "NO_RESULTS" "Keine Ergebnisse."
+ "NO_MODS" "Keine Einstellungen verfügbar! Installieren sie weitere Mods über^5588FF00northstar.thunderstore.io^0."
+ "PROGRESSION_ENABLED_HEADER" "Fortschritt aktiviert!"
+ "PROGRESSION_DISABLED_HEADER" "Fortschritt deaktiviert!"
+ "WILL_RESET_ALL_SETTINGS" "Dadurch werden ALLE Einstellungen, die zu dieser Kategorie gehören, zurückgesetzt.\n\nDies kann nicht rückgängig gemacht werden."
+ "WILL_RESET_SETTING" "Dies setzten die Einstellungen %s1 auf deren Ursprungeswert zurück.\n\nDies kann nicht rückgängig gemacht werden."
+ "Y_BUTTON_TOGGLE_PROGRESSION" "%[Y_BUTTON|]% Fortschritt umschalten"
+ "PROGRESSION_TOGGLE_DISABLED_BODY" "Titans, Waffen, Fraktionen, Skins usw. müssen durch Levelaufstieg freigeschaltet oder mit Verdiensten gekauft werden.\n\nDies kann jederzeit in der Mehrspieler-Lobby geändert werden.\n\n^CC000000Warnung: Wenn Sie derzeit ausgerüstete Gegenstände besitzen, die Sie nicht freigeschaltet haben, werden diese zurückgesetzt!"
+ "PROGRESSION_ENABLED_BODY" "^CCCC0000Fortschritt wurde aktiviert.^\n\nTitans, Waffen, Fraktionen, Skins usw. müssen durch Levelaufstieg freigeschaltet oder mit Verdiensten gekauft werden.\n\nDies kann jederzeit in der Mehrspieler-Lobby geändert werden."
+ "PROGRESSION_DISABLED_BODY" "^CCCC0000Fortschritt wurde deaktiviert.^\n\nTitans, Waffen, Fraktionen, Skins usw. werden alle freigeschaltet und jederzeit nutzbar sein.\n\nDies kann jederzeit in der Mehrspieler-Lobby geändert werden."
+ "CHECKSUMING_TEXT" "Verifiziere Inhalte von %s1 v%s2..."
+ "EXTRACTING_MOD_TITLE" "Extrahiere Mod (%s1%)"
+ "EXTRACTING_MOD_TEXT" "Extrahiere %s1 v%s2...\n(%s3/%s4 MB)"
+ "FAILED_DOWNLOADING" "Herunterladen der Mod fehlgeschlagen"
+ "FAILED_READING_ARCHIVE" "Während des Lesens der Mod Archivs ist ein Fehler aufgetreten."
+ "FAILED_WRITING_TO_DISK" "Während des Extrahierens der Mod in das Dateisysteme ist ein Fehler aufgetreten."
+ "WRONG_MOD_VERSION" "Der Server verfügt über Mod \"%s1\" v%s2 während Sie v%s3 haben"
+ "MOD_FETCHING_FAILED" "Mod Archiv konnte nicht von Thunderstore heruntergeladen werden."
+ "MOD_CORRUPTED" "Prüfsumme des heruntergeladenen Archivs stimmt nicht mit der verifizierten Signatur überein."
+ "NO_DISK_SPACE_AVAILABLE" "Sie verfügen nicht über ausreichend Speicherplatz auf ihrer Festplatte."
+ "MOD_FETCHING_FAILED_GENERAL" "Mod Extraktion fehlgeschlagen. Überprüfen Sie die Logs für weitere Details."
+ "gg_assist_reward" "Assist Anteilige Belohnung"
+ "SCOREBOARD_BANKRUPTS" "Bankrott Kills"
+ "sns_offhand_kill_value" "Freihand Killwert"
+ "PROGRESSION_ANNOUNCEMENT_BODY" "^CCCC0000Fortschritt kann jetzt aktiviert werden!^\n\nNorthstar unterstützt nun den standardmäßigen Fortschritt, was bedeutet, dass Sie Waffen, Skins, Titans usw. durch Levelaufstieg und das Abschließen von Herausforderungen freischalten können.\n\nSie können den Fortschritt mit dem Button am unteren Rand des Lobbybildschirms aktivieren.\n\nDies kann jederzeit geändert werden."
}
}
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_italian.txt b/Northstar.Client/mod/resource/northstar_client_localisation_italian.txt
index 38e67dea4..089edf35b 100644
--- a/Northstar.Client/mod/resource/northstar_client_localisation_italian.txt
+++ b/Northstar.Client/mod/resource/northstar_client_localisation_italian.txt
@@ -316,7 +316,7 @@ Premi Sì se sei d'accordo. Questa scelta può essere modificata in qualsiasi mo
"UNAUTHORIZED_PWD" "Password errata"
"STRYDER_RESPONSE" "Non è stato possibile analizzare la risposta di Stryder"
"PLAYER_NOT_FOUND" "Non è stato trovato l'account player"
- "INVALID_MASTERSERVER_TOKEN" "Token Masterserver invalido o scaduto"
+ "INVALID_MASTERSERVER_TOKEN" "Token Masterserver invalido o scaduto, prova a riavviare l'App EA"
"JSON_PARSE_ERROR" "Errore nell'analisi della risposta json"
"UNSUPPORTED_VERSION" "La versione che stai usando non è più supportata"
@@ -380,5 +380,42 @@ Premi Sì se sei d'accordo. Questa scelta può essere modificata in qualsiasi mo
"sns_wme_kill_value" "Valore per uccisione Wingman d'Elite"
"sns_reset_kill_value" "Valore per uccisione Lama Impulsi/Esecuzione"
"PL_tffa_desc" "Ogni pilota per sè, distruggi tutti i titan nemici."
+ "player_force_respawn" "Respawn Forzato"
+ "PROGRESSION_TOGGLE_DISABLED_HEADER" "Attivare Progressione?"
+ "TOGGLE_PROGRESSION" "Attiva/Disattiva Progressione"
+ "Y_BUTTON_TOGGLE_PROGRESSION" "%[Y_BUTTON|]% Attiva/Disattiva Progressione"
+ "PROGRESSION_TOGGLE_ENABLED_HEADER" "Disattivare la Progressione?"
+ "AUTHENTICATION_FAILED_BODY" "Fallimento nell'autenticare con Atlas"
+ "AUTHENTICATION_FAILED_ERROR_CODE" "Codice di errore: ^DB6F2C00%s1^"
+ "AUTHENTICATION_FAILED_HELP" "Aiuto"
+ "AUTHENTICATION_FAILED_HEADER" "Autenticazione Fallita"
+ "MISSING_MOD" "Mod mancante \"%s1\" v%s2"
+ "MOD_NOT_VERIFIED" "(Mod non verificata, non è stato possibile il download automatico)"
+ "MOD_DL_DISABLED" "(Il download automatico delle mod è disabilitato)"
+ "DOWNLOADING_MOD_TEXT" "Download %s1 v%s2..."
+ "CHECKSUMING_TEXT" "Verifica contenuti %s1 v%s2..."
+ "EXTRACTING_MOD_TITLE" "Estrazione mod (%s1%)"
+ "FAILED_DOWNLOADING" "Download della mod fallito"
+ "NO_DISK_SPACE_AVAILABLE" "Non c'è abbastanza spazio sul disco."
+ "MOD_FETCHING_FAILED_GENERAL" "Estrazione mod fallita. Controlla i file di log per più dettagli."
+ "DOWNLOADING_MOD_TITLE" "Download mod in corso"
+ "DOWNLOADING_MOD_TITLE_W_PROGRESS" "Download in corso della mod (%s1%)"
+ "FAILED_READING_ARCHIVE" "C'è stato un errore durante la lettura dell'archivio della mod."
+ "FAILED_WRITING_TO_DISK" "C'è stato un errore durante l'estrazione della mod al filesystem."
+ "MOD_FETCHING_FAILED" "Impossibile scaricare l'archivio mod da Thunderstore."
+ "MOD_CORRUPTED" "La firma dell'archivio scaricato non corrisponde con quella verificata."
+ "DOWNLOADING_MOD_TEXT_W_PROGRESS" "Download %s1 v%s2...\n(%s3/%s4 MB)"
+ "EXTRACTING_MOD_TEXT" "Estraendo %s1 v%s2...\n(%s3/%s4 MB)"
+ "MOD_REQUIRED_WARNING" " : Questa mod potrebbe venire (non)caricata entrando in un server"
+ "PROGRESSION_ENABLED_HEADER" "Progressione Attivata!"
+ "PROGRESSION_ENABLED_BODY" "^CCCC0000La progessione è stata abilitata.^\n\nTitan, Armi, Fazioni, Skin, etc. dovranno essere sbloccate livellando, o comprate con i Meriti.\n\nQuesto può essere cambiato in qualsiasi momento nella lobby multigiocatore."
+ "PROGRESSION_DISABLED_HEADER" "Progressione Disabilitata!"
+ "PROGRESSION_DISABLED_BODY" "^CCCC0000La progressione è stata disabilitata.^\n\nTitan, Armi, Fazioni, Skin, etc. saranno sbloccate e utilizzabili in qualsiasi momento.\n\nQuesto può essere cambiato in qualsiasi momento nella lobby multigiocatore."
+ "WRONG_MOD_VERSION" "Il server ha la mod \"%s1\" v%s2 mentre tu hai v%s3"
+ "MANIFESTO_FETCHING_TITLE" "Iniziando download mod"
+ "MANIFESTO_FETCHING_TEXT" "Recuperando lista delle mod verificate..."
+ "PROGRESSION_TOGGLE_ENABLED_BODY" "Titan, Armi, Fazioni, Skin, etc. saranno tutti sbloccati e utilizzabili in ogni momento.\n\nQuesto può essere cambiato in qualsiasi momento nella lobby Multigiocatore."
+ "PROGRESSION_TOGGLE_DISABLED_BODY" "Titan, Armi, Factions, Skin, etc. avranno bisogno di essere sbloccate livellando, o comprate con i Meriti.\n\nQuesto può essere cambiato in ogni momento nella lobby Multiplayer.\n\n^CC000000Attenzione: se al momento hai equipaggiato degli item che non hai sbloccato, verranno resettati!"
+ "PROGRESSION_ANNOUNCEMENT_BODY" "^CCCC0000La progressione può essere abilitata ora!^\n\nNorthstar ora supporta la progressione vanilla, ciò significa che puoi scegliere di sbloccare Armi, Skin, Titan, etc. attraverso i livelli e le sfide.\n\nPuoi abilitare la progressione cliccando il pulsante in basso nella schermata lobby.\n\nQuesto può essere cambiato in qualsiasi momento."
}
}
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_japanese.txt b/Northstar.Client/mod/resource/northstar_client_localisation_japanese.txt
index b7fadeaff..798d603e0 100644
--- a/Northstar.Client/mod/resource/northstar_client_localisation_japanese.txt
+++ b/Northstar.Client/mod/resource/northstar_client_localisation_japanese.txt
@@ -233,8 +233,8 @@
"GAMEMODE_fastball" "ファストボール"
"PL_fastball" "ファストボール"
"PL_fastball_lobby" "ファストボール ロビー"
- "PL_fastball_desc" "ライブファイア。パネルをハックし、味方を蘇生できる"
- "PL_fastball_hint" "ライブファイア。パネルをハックし、味方を蘇生できる"
+ "PL_fastball_desc" "ライブファイア。パネルをハックし、味方を蘇生できる。"
+ "PL_fastball_hint" "ライブファイア。パネルをハックし、味方を蘇生できる。"
"PL_fastball_abbr" "FB"
"FASTBALL_PANEL_CAPTURED" "%s1 がパネル%s2を制圧した"
"SCOREBOARD_FASTBALL_HACKS" "制圧パネル"
@@ -265,10 +265,10 @@
"player_bleedout_aiBleedingPlayerMissChance" "ダウン時のAI命中率"
// coop stuff
- "PL_sp_coop" "(UNFINISHED) Singleplayer Coop"
- "PL_sp_coop_lobby" "Singleplayer Coop Lobby"
- "PL_sp_coop_desc" "Play through the singleplayer campaign with friends"
- "PL_sp_coop_hint" "Play through the singleplayer campaign with friends"
+ "PL_sp_coop" "(未完成) シングルプレイヤー 協力モード"
+ "PL_sp_coop_lobby" "シングルプレイヤー 協力モード ロビー"
+ "PL_sp_coop_desc" "シングルプレイヤーのキャンペーンモードをフレンドと一緒にプレイできる"
+ "PL_sp_coop_hint" "シングルプレイヤーのキャンペーンモードをフレンドと一緒にプレイできる"
"PL_sp_coop_abbr" "SP"
"SP_TRAINING" "パイロット・ガントレット"
@@ -340,13 +340,70 @@
"NO_GAMESERVER_RESPONSE" "ゲームサーバーに接続できません\n(Couldn't reach game server)"
"BAD_GAMESERVER_RESPONSE" "ゲームサーバーが不明なレスポンスを返しました\n(Game server gave an invalid response)"
"UNAUTHORIZED_GAMESERVER" "ゲームサーバーにそのリクエストを作成する許可がありません\n(Game server is not authorized to make that request)"
- "UNAUTHORIZED_GAME" "StryderはこのアカウントがTitanfall 2を所持しているかどうかを確認できませんでした\nStryder couldn't confirm that this account owns Titanfall 2"
+ "UNAUTHORIZED_GAME" "StryderはこのアカウントがTitanfall 2を所持しているかどうかを確認できませんでした\n(Stryder couldn't confirm that this account owns Titanfall 2)"
"UNAUTHORIZED_PWD" "パスワードが間違っています\n(Wrong password)"
"STRYDER_RESPONSE" "Stryderからのレスポンスの処理に失敗しました\n(Couldn't parse stryder response)"
"PLAYER_NOT_FOUND" "プレイヤーのアカウントが見つかりません\n(Couldn't find player account)"
- "INVALID_MASTERSERVER_TOKEN" "マスターサーバーのトークンが不明か期限切れです\n(Invalid or expired masterserver token)"
+ "INVALID_MASTERSERVER_TOKEN" "マスターサーバーのトークンが不明か期限切れです。EAアプリの再起動をお試しください。\n(Invalid or expired masterserver token, try restarting EA App.)"
"JSON_PARSE_ERROR" "JSONレスポンスの処理に失敗しました\n(Error parsing json response)"
"UNSUPPORTED_VERSION" "現在使用しているバージョンはサポートされていません\n(The version you are using is no longer supported)"
+ "player_force_respawn" "強制リスポーン"
+ "SHOW_ONLY_REQUIRED" "必須のModのみ"
+ "Y_BUTTON_TOGGLE_PROGRESSION" "%[Y_BUTTON|]% 進行システム切り替え"
+ "TOGGLE_PROGRESSION" "進行システム切り替え"
+ "AUTHENTICATION_FAILED_HEADER" "認証に失敗"
+ "DOWNLOADING_MOD_TITLE" "Modをダウンロード中"
+ "PROGRESSION_TOGGLE_ENABLED_BODY" "タイタン、武器、勢力、スキンなどが全てアンロックされ、いつでも使えるようになる。\n\nこの設定は、マルチプレイヤーロビーでいつでも変更可能だ。"
+ "PROGRESSION_TOGGLE_DISABLED_HEADER" "進行システムを有効にしますか?"
+ "PROGRESSION_TOGGLE_ENABLED_HEADER" "進行システムを無効にしますか?"
+ "PROGRESSION_DISABLED_HEADER" "進行システムが無効になりました!"
+ "PROGRESSION_ENABLED_HEADER" "進行システムが有効になりました!"
+ "PROGRESSION_DISABLED_BODY" "^CCCC0000進行システムが無効化された。^\n\nタイタン、武器、勢力、スキンなどが全てアンロックされ、いつでも使えるようになる。\n\nこの設定は、マルチプレイヤーロビーでいつでも変更可能だ。"
+ "SHOULD_RETURN_TO_LOBBY" "マッチ終了後にロビーへ戻る"
+ "REPLACEMENT_WEAPON" "武器の置き換え"
+ "TACTICAL_REPLACEMENT" "戦術の置き換え"
+ "DISALLOWED_TACTICALS" "戦術の禁止"
+ "DISALLOWED_WEAPONS" "武器の禁止"
+ "ONLY_HOST_CAN_START_MATCH" "マッチを開始できるのはホストのみ"
+ "ONLY_HOST_MATCH_SETTINGS" "プライベートマッチの設定を変更できるのはホストのみ"
+ "AUTHENTICATION_FAILED_HELP" "ヘルプ"
+ "MATCH_COUNTDOWN_LENGTH" "プライベートマッチのカウントダウン時間"
+ "ARE_YOU_SURE" "よろしいですか?"
+ "MOD_SETTINGS_SERVER" "サーバー"
+ "MOD_SETTINGS_RESET" "リセット"
+ "MOD_SETTINGS_RESET_ALL" "全てリセット"
+ "NO_RESULTS" "リザルトなし。"
+ "MOD_SETTINGS" "Modの設定"
+ "NORTHSTAR_BASE_SETTINGS" "Northstarの基本設定"
+ "NO_DISK_SPACE_AVAILABLE" "ディスクに十分な領域がありません。"
+ "FAILED_DOWNLOADING" "Modのダウンロードに失敗"
+ "FAILED_READING_ARCHIVE" "Modアーカイブの読込中にエラーが発生しました。"
+ "DOWNLOADING_MOD_TITLE_W_PROGRESS" "Modをダウンロード中 (%s1%)"
+ "DOWNLOADING_MOD_TEXT" "ダウンロード中 %s1 v%s2..."
+ "DOWNLOADING_MOD_TEXT_W_PROGRESS" "ダウンロード中 %s1 v%s2...\n(%s3/%s4 MB)"
+ "MOD_FETCHING_FAILED" "ThunderstoreからModアーカイブをダウンロードできませんでした。"
+ "EXTRACTING_MOD_TITLE" "Modを抽出中 (%s1%)"
+ "EXTRACTING_MOD_TEXT" "抽出中 %s1 v%s2...\n(%s3/%s4 MB)"
+ "FAILED_WRITING_TO_DISK" "Modファイルをファイルシステムに抽出する際にエラーが発生しました。"
+ "MOD_FETCHING_FAILED_GENERAL" "Modの抽出に失敗。詳細はログを確認してください。"
+ "MOD_CORRUPTED" "ダウンロードしたアーカイブのチェックサムが検証済み署名と一致しませんでした。"
+ "CHECKSUMING_TITLE" "Modをチェックサム中"
+ "CHECKSUMING_TEXT" "コンテンツを検証中 %s1 v%s2..."
+ "MOD_DL_DISABLED" "(自動Modダウンロードは無効です)"
+ "PROGRESSION_TOGGLE_DISABLED_BODY" "タイタン、武器、勢力、スキンなどのアンロックにレベル上げやメリットが必要になる。\n\nこの設定は、マルチプレイヤーロビーでいつでも変更可能だ。\n\n^CC000000警告: 現在装備しているアイテムがロック中の場合、装備状況は初期化されます!"
+ "PROGRESSION_ENABLED_BODY" "^CCCC0000進行システムが有効化された。^\n\nタイタン、武器、勢力、スキンなどのアンロックにレベル上げやメリットが必要になる。\n\nこの設定は、マルチプレイヤーロビーでいつでも変更可能だ。"
+ "LOG_UNKNOWN_CLIENTCOMMANDS" "不明なクライアントコマンドを記録する"
+ "SHOW_ONLY_NOT_REQUIRED" "任意のModのみ"
+ "AUTHENTICATION_FAILED_ERROR_CODE" "エラーコード: ^DB6F2C00%s1^"
+ "AUTHENTICATION_FAILED_BODY" "Atlasの認証に失敗しました!\n(Failed to authenticate with Atlas!)"
+ "MISSING_MOD" "Mod消失 \"%s1\" v%s2"
+ "MOD_REQUIRED_WARNING" " :このModはサーバーに参加した際に読み込まる(または読み込まれない)ことがあります"
+ "WILL_RESET_ALL_SETTINGS" "カテゴリー内の全設定をリセットしようとしています。\n\nこの操作は取り消せません。"
+ "WILL_RESET_SETTING" "%s1 の設定を初期値に戻そうとしています。\n\nこの操作は取り消せません。"
+ "NO_MODS" "設定が利用できません!Modのインストールはこちらから: ^5588FF00northstar.thunderstore.io^0"
+ "PROGRESSION_ANNOUNCEMENT_BODY" "^CCCC0000進行システムが開放されました!^\n\nNorthstarはバニラの進行システム、つまり武器、スキン、タイタン等をレベルアップやチャレンジ達成で解除する機能をサポートしています。\n\nこの進行システムは、ロビー画面下のボタンから有効にできます。\n\nこの設定はいつでも変更可能です。"
+ "MOD_NOT_VERIFIED" "(Modが検証されていないため、自動ダウンロードできませんでした)"
+ "WRONG_MOD_VERSION" "サーバーはMod\"%s1\"v%s2 を要求していますが、あなたのModバージョンは%s3です"
// Translation done by Zetryox and CYakigasi
}
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_polish.txt b/Northstar.Client/mod/resource/northstar_client_localisation_polish.txt
new file mode 100644
index 000000000..9b570cebe
--- /dev/null
+++ b/Northstar.Client/mod/resource/northstar_client_localisation_polish.txt
@@ -0,0 +1,394 @@
+"lang"
+{
+ "Language" "polish"
+ "Tokens"
+ {
+ // This file needs to be encoded as UTF-16 LE
+
+ "MENU_LAUNCH_NORTHSTAR" "Uruchom Northstar"
+ "MENU_TITLE_MODS" "Mody"
+ "RELOAD_MODS" "Przeładuj Mody"
+ "WARNING" "Ostrzeżenie"
+ "CORE_MOD_DISABLE_WARNING" "Wyłączenie kluczowych Modów może zepsuć Northstar!"
+ "DISABLE" "Wyłącz"
+
+ "DIALOG_TITLE_INSTALLED_NORTHSTAR" "Dzięki za instalację Northstar!"
+ "AUTHENTICATION_AGREEMENT_DIALOG_TEXT" "Dla poprawnego działania Northstar potrzebne jest uwierzytelnienie z głównym serwerem. Potrzebne będzie wysłanie twojego tokena Origin do głównego serwera, nie będzie on przechowywany ani użyty do żadnych innych celów.
+Naciśnij Tak jeżeli wyrażasz zgodę. Wybór może zostać zmieniony w menu Modów."
+ "BACK_AUTHENTICATION_AGREEMENT" "Zgoda na uwierzytelnianie"
+ "AUTHENTICATION_AGREEMENT" "Zgoda na uwierzytelnianie"
+ "AUTHENTICATION_AGREEMENT_RESTART" "Potrzebne jest ponowne odpalenie Titanfall 2 by zmiany dały efekt."
+
+ "DIALOG_AUTHENTICATING_MASTERSERVER" "Uwierzytelnianie z głównym serwerem."
+ "AUTHENTICATIONAGREEMENT_NO" "Zdecydowałeś się nie uwierzytelniać się z Northstar. Zgodę można wyrazić w menu z modami."
+
+ "MENU_TITLE_SERVER_BROWSER" "Wyszukiwarka serwerów"
+ "NS_SERVERBROWSER_NOSERVERS" "Nie znaleziono serwerów"
+ "NS_SERVERBROWSER_UNKNOWNMODE" "Nieznany tryb"
+ "NS_SERVERBROWSER_WAITINGFORSERVERS" "Oczekiwanie na serwery..."
+ "NS_SERVERBROWSER_CONNECTIONFAILED" "Połączenie nieudane!"
+ "REFRESH_SERVERS" "Odśwież"
+
+ "MENU_TITLE_CONNECT_PASSWORD" "Dołącz z hasłem"
+ "MENU_CONNECT_MENU_CONNECT" "Dołącz"
+
+ "PRIVATE_MATCH_PAGE_PREV" "Poprzednia strona"
+ "PRIVATE_MATCH_PAGE_NEXT" "Następna strona"
+
+ "MENU_MATCH_SETTINGS" "Ustawienia meczu"
+ "MENU_MATCH_SETTINGS_SUBMENU" "%s1 Ustawienia trybu"
+
+ "PRIVATE_MATCH_SINGLEPLAYER_LEVEL" "%s1 (Tryb Jednoosobowy)"
+
+ // fra hint for private match menu, because fra only has PL_fra_desc in vanilla
+ "PL_fra_hint" "Każdy na każdego. Zbierz 3 baterie, aby otrzymać Tytana."
+
+ // mode settings
+ "MODE_SETTING_CATEGORY_PILOT" "Pilot"
+ "MODE_SETTING_CATEGORY_TITAN" "Tytan"
+ "MODE_SETTING_CATEGORY_RIFF" "Modyfikatory rozgrywki"
+ "MODE_SETTING_CATEGORY_MATCH" "Mecz"
+
+ "classic_mp" "Klasyczny tryb wieloosobowy"
+ "run_epilogue" "Epilog"
+ "scorelimit" "Limit wyniku"
+ "roundscorelimit" "Limit wyniku (na rundę)"
+ "timelimit" "Limit Czasu"
+ "roundtimelimit" "Limit Czasu (na rundę)"
+ "respawnprotection" "Ochrona po odrodzeniu"
+
+ "pilot_health_multiplier" "Mnożnik życia"
+ "respawn_delay" "Opóźnienie odrodzeń"
+ "boosts_enabled" "Wzmocnienia"
+ "earn_meter_pilot_overdrive" "Doładowanie wskaźnika Tytana/wzmocnienia"
+ "earn_meter_pilot_multiplier" "Mnożnik wsk. Tytana/wzm."
+
+ "earn_meter_titan_multiplier" "Mnożnik wskaźnika rdzenia"
+ "aegis_upgrades" "POSTĘPY DOTYCZĄCE RANGI EGIDY"
+ "infinite_doomed_state" "Stan tytana spisany na straty jest nieskończony"
+ "titan_shield_regen" "Regeneracja tarczy Tytanów"
+
+ "riff_floorislava" "Deadly ground"
+ "featured_mode_all_holopilot" "The Great Bamboozle"
+ "featured_mode_all_grapple" "Attack on Titanfall"
+ "featured_mode_all_phase" "The Otherside"
+ "featured_mode_all_ticks" "Spicy"
+ "featured_mode_tactikill" "Tactikill"
+ "featured_mode_amped_tacticals" "Amped Tacticals"
+ "featured_mode_rocket_arena" "Rocket Arena"
+ "featured_mode_shotguns_snipers" "Armed and Dangerous"
+ "iron_rules" "Iron Titan Rules"
+
+ "cp_amped_capture_points" "Obrona umocnień"
+ "coliseum_loadouts_enabled" "Uzbrojenie Koloseum"
+
+ "aitdm_archer_grunts" "Piechurzy z łucznikami"
+
+ // northstar.custom localisation is just deciding not to work, so putting it here for now
+ "PL_sbox" "Piaskownica"
+ "PL_sbox_lobby" "Piaskownica Lobby"
+ "PL_sbox_desc" "jak gmod tylko gorsze"
+ "PL_sbox_abbr" "Piaskownica"
+ "GAMEMODE_SBOX" "Piaskownica"
+
+ "PL_gg" "Gun Game"
+ "PL_gg_lobby" "Gun Game"
+ "PL_gg_desc" "Zdobądź zabójstwo każdą bronią by wygrać."
+ "PL_gg_hint" "Zdobądź zabójstwo każdą bronią by wygrać."
+ "PL_gg_abbr" "GG"
+ "GAMEMODE_GG" "Gun Game"
+ "gg_kill_reward" "Procentowa nagroda za zabójstwo"
+ "gg_assist_reward" "Procentowa nagroda za asystę"
+ "gg_execution_reward" "Procentowa nagroda za egzekucję"
+
+ "PL_tt" "Titan Tag"
+ "PL_tt_lobby" "Titan Tag Lobby"
+ "PL_tt_desc" "Zdobywaj punkty będąc w Tytanie. Zniszcz Tytana by otrzymać własnego."
+ "PL_tt_hint" "Zdobywaj punkty będąc w Tytanie. Zniszcz Tytana by otrzymać własnego."
+ "PL_tt_abbr" "TT"
+ "GAMEMODE_TT" "Titan Tag"
+
+ "PL_chamber" "One in the Chamber"
+ "PL_chamber_lobby" "One in the Chamber Lobby"
+ "PL_chamber_desc" "Jeden strzał, Jedno zabójstwo. Zdobądź kolejny pocisk w magazynku zabijając przeciwnika."
+ "PL_chamber_hint" "Jeden strzał, Jedno zabójstwo. Zdobądź kolejny pocisk w magazynku zabijając przeciwnika."
+ "PL_chamber_abbr" "CHAMBER"
+ "GAMEMODE_CHAMBER" "One in the Chamber Lobby"
+
+ "PL_hidden" "The Hidden"
+ "PL_hidden_lobby" "The Hidden Lobby"
+ "PL_hidden_desc" "Jeden gracz jest niewidzialny i poluję na resztę."
+ "PL_hidden_hint" "Jeden gracz jest niewidzialny i poluję na resztę."
+ "PL_hidden_abbr" "HIDDEN"
+ "GAMEMODE_HIDDEN" "The Hidden"
+ "HIDDEN_YOU_ARE_HIDDEN" "Jesteś The Hidden!"
+ "HIDDEN_KILL_SURVIVORS" "Zabij wszystkich ocaleńców."
+ "HIDDEN_FIRST_HIDDEN" "%s1 jest The Hidden."
+
+ "PL_sns" "Sticks and Stones"
+ "PL_sns_lobby" "Sticks and Stones Lobby"
+ "PL_sns_desc" "Każdy na każdego. Zabij przeciwnika ostrzem pulsacyjnym lub wykonaj egzekucję by zresetować jego wynik"
+ "PL_sns_abbr" "SNS"
+ "GAMEMODE_SNS" "Sticks and Stones"
+ "SCOREBOARD_BANKRUPTS" "Zabójstwa resetujące"
+ "SNS_LEADER_BANKRUPT" "Wynik Lidera został zresetowany!"
+ "SNS_LEADER_BANKRUPT_SUB" "Wynik %s1 został zresetowany przez %s2"
+ "SNS_BANKRUPT" "Reset!"
+ "SNS_BANKRUPT_SUB" "Twój wynik został zresetowany przez %s1"
+ "sns_wme_kill_value" "Wartość zabójstwa przy użyciu Elitarny skrzydłowy"
+ "sns_softball_kill_value" "Wartość zabójstwa przy użyciu Softball"
+ "sns_offhand_kill_value" "Wartość zabójstwa przy użyciu Offhand "
+ "sns_reset_kill_value" "Wartość zabójstwa przy użyciu Ostrza pulsacyjnego/egzekucji"
+ "sns_melee_kill_value" "Wartość zabójstwa wręcz"
+ "sns_reset_pulse_blade_cooldown_on_pulse_blade_kill" "Zabójstwa regenerują zdolność taktyczną - Ostrze pulsacyjne"
+ "sns_softball_enabled" "Softball Włączony"
+
+ "PL_inf" "Infekcja"
+ "PL_inf_lobby" "Infekcja Lobby"
+ "PL_inf_desc" "Przeżyj zarazę. Ocaleńcy zostają zainfekowani gdy zabici."
+ "PL_inf_hint" "Przeżyj zarazę. Ocaleńcy zostają zainfekowani gdy zabici.."
+ "PL_inf_abbr" "INF"
+ "GAMEMODE_INF" "Infekcja"
+ "INFECTION_YOU_ARE_INFECTED" "Zostałeś zainfekowany!"
+ "INFECTION_KILL_SURVIVORS" "Zainfekuj pozostałych ocaleńców."
+ "INFECTION_FIRST_INFECTED" "%s1 jest pierwszym zainfekowanym."
+ "INFECTION_LAST_SURVIVOR" "%s1 jest ostatnim ocaleńcem!"
+ "INFECTION_KILL_LAST_SURVIVOR" "Zainfekuj ich przed końcem czasu!"
+ "INFECTION_YOU_ARE_LAST_SURVIVOR" "Jesteś ostatnim ocaleńcem!"
+ "INFECTION_SURVIVE_LAST_SURVIVOR" "Przeżyj..."
+
+ "PL_tffa" "Każdy na każdego Tytany"
+ "PL_tffa_lobby" "Każdy na każdego Tytany"
+ "PL_tffa_desc" "Każdy na każdego, zniszcz tytany przeciwników."
+ "PL_tffa_hint" "Każdy na każdego, zniszcz tytany przeciwników."
+ "PL_tffa_abbr" "TFFA"
+ "GAMEMODE_TFFA" "Każdy na każdego Tytany"
+
+ "PL_hs" "Chowany"
+ "PL_hs_lobby" "Chowany Lobby"
+ "PL_hs_desc" "Szukający szukają chowających się."
+ "PL_hs_hint" "Szukający szukają chowających się."
+ "PL_hs_abbr" "HS"
+ "GAMEMODE_hs" "Chowany"
+ "HIDEANDSEEK_YOU_ARE_SEEKER" "JESTEŚ SZUKAJĄCYM"
+ "HIDEANDSEEK_SEEKER_DESC" "Znajdź chowających się i przyłóż im wręcz.\nOdrodzisz się w %s1 sekund"
+ "HIDEANDSEEK_YOU_ARE_HIDER" "JESTEŚ CHOWAJĄCYM SIĘ"
+ "HIDEANDSEEK_HIDER_DESC" "Schowaj się."
+ "HIDEANDSEEK_SEEKERS_INCOMING" "Szukający nadchodzi"
+ "HIDEANDSEEK_DONT_GET_FOUND" "Nie daj się znaleźć!"
+ "HIDEANDSEEK_GET_LAST_HIDER" "%s1 JEST OSTATNIM CHOWAJĄCYM SIĘ"
+ "HIDEANDSEEK_YOU_ARE_LAST_HIDER" "JESTEŚ OSTATNIM CHOWAJĄCYM SIĘ"
+ "HIDEANDSEEK_GOT_STIM" "Masz Stymulant! Nie daj się złapać!"
+ "hideandseek_balance_teams" "Automatyczny balans Chowający się/Szukający"
+ "hideandseek_hiding_time" "Czas na ukrycie się dla Chowających się"
+
+ // these are defined in r1_english but titan war is a shit name so i'm changing it to another one that was referenced in development
+ "GAMEMODE_fw" "Wojna Kresów"
+ "PL_fw" "Wojna Kresów"
+ "PL_fw_lobby" "Wojna Kresów Lobby"
+ "PL_fw_desc" "Zniszcz Zbieracz przeciwnika i ochroń swój"
+ "PL_fw_abbr" "FW"
+
+ "GAMEMODE_kr" "Seria Zabójstw"
+ "PL_kr" "Seria Zabójstw"
+ "PL_kr_lobby" "Seria Zabójstw Lobby"
+ "PL_kr_desc" "Zabijaj przeciwników by ustanowić rekord serii i wygrać. Zbierz flagę by ją aktywować."
+ "PL_kr_hint" "Zabijaj przeciwników by ustanowić rekord serii i wygrać. Zbierz flagę by ją aktywować."
+ "PL_kr_abbr" "KR"
+ "SCOREBOARD_KR_RECORD" "Rekord Zabójstw"
+ "KR_NEW_RACER" "%s1 aktywował Serię"
+ "KR_YOU_ARE_NEW_RACER" "Aktywowałeś Serię"
+ "KR_YOU_SET_NEW_RECORD" "Ustaw nowy rekord zabójstw!"
+ "KR_FLAG_INCOMING" "Niedługo pojawi się flaga"
+ "KR_COLLECT_FLAG" "Zdobądź ją by aktywować Serię!"
+ "KR_ENEMY_KILLRACE_OVER" "Seria %s1 się skończyła"
+ "KR_YOUR_KILLRACE_OVER" "Twoja Seria się skończyła"
+ "KR_YOUR_KILLRACE_SCORE" "Zdobyłeś %s1 zabójstw."
+
+ "GAMEMODE_fastball" "Fastball"
+ "PL_fastball" "Fastball"
+ "PL_fastball_lobby" "Fastball Lobby"
+ "PL_fastball_desc" "Śmierć pernamentna. Hackuj panele kotrolne by wygrywać rundy i odradzać swoich osoby z swojej drużyny."
+ "PL_fastball_hint" "Śmierć pernamentna. Hackuj panele kotrolne by wygrywać rundy i odradzać swoich osoby z swojej drużyny."
+ "PL_fastball_abbr" "FB"
+ "FASTBALL_PANEL_CAPTURED" "%s1 przęjeli panel %s2"
+ "SCOREBOARD_FASTBALL_HACKS" "Przejęte panele"
+
+ "GAMEMODE_ctf_comp" "Kompetetywne - Walka o flagę"
+
+ // mode settings
+ "MODE_SETTING_CATEGORY_PROMODE" "Tryb pro"
+ "MODE_SETTING_CATEGORY_BLEEDOUT" "Krwawienie Pilotów"
+
+ "custom_air_accel_pilot" "Air Acceleration"
+ "no_pilot_collision" "Kolidowanie Pilotów"
+ "promode_enable" "Bronie trybu pro"
+ "fp_embark_enabled" "Pierwszoosobowe wsiadanie/egzekucje"
+ "classic_rodeo" "Klasyczne Rodeo"
+ "oob_timer_enabled" "Licznik czasu poza granicami mapy"
+ "riff_instagib" "Instagib Mode"
+ "player_force_respawn" "Wymuszone odrodzenie"
+
+ "riff_player_bleedout" "Krwawienie Pilotów"
+ "player_bleedout_forceHolster" "Schowaj bronie gdy powalonym"
+ "player_bleedout_forceDeathOnTeamBleedout" "Umrzyj przy wykrawieniu się drużyny"
+ "player_bleedout_bleedoutTime" "Czas do wykrwawienia się"
+ "player_bleedout_firstAidTime" "Czas potrzebny do udzielenia pierwszej pomocy"
+ "player_bleedout_firstAidTimeSelf" "Czas potrzebny do samo-reanimacji"
+ "player_bleedout_firstAidHealPercent" "Procent życia po otrzymaniu pierwszej pomocy"
+ "player_bleedout_aiBleedingPlayerMissChance" "Szanasa na nietrafienie przez powalone SI"
+
+ // coop stuff
+ "PL_sp_coop" "(NIE UKOŃCZONE) Kampania w trybie Kooperacji"
+ "PL_sp_coop_lobby" "Kampania w trybie Kooperacji Lobby"
+ "PL_sp_coop_desc" "Zagraj kampanie z przyjaciółmi"
+ "PL_sp_coop_hint" "Zagraj kampanie z przyjaciółmi"
+ "PL_sp_coop_abbr" "SP"
+
+ "SP_TRAINING" "Tor przeszkód"
+ "SP_TRAINING_CLASSIC_DESC" "Symulacja kapitana Lastimosy."
+
+ "SP_CRASHSITE" "BT-7274"
+ "SP_CRASHSITE_CLASSIC_DESC" "Jack Cooper spotyka BT-7274."
+
+ "SP_SEWERS1" "Krew i rdza"
+ "SP_SEWERS1_CLASSIC_DESC" "Cooper i BT ruszają spotkać się z majorem Andersonem."
+
+ "SP_BOOMTOWN_START" "W paszczę otchłani"
+ "SP_BOOMTOWN_START_CLASSIC_DESC" "Przeprawa podziemnym skrótem przynosi nieoczekiwane skutki."
+
+ "SP_HUB_TIMESHIFT" "Skutek i przyczyna"
+ "SP_HUB_TIMESHIFT_CLASSIC_DESC" "W miejscu, w którym przebywa major Anderson zaobserwowano osobliwe zjawisko."
+
+ "SP_BEACON" "Nadajnik"
+ "SP_BEACON_CLASSIC_DESC" "Cooper i BT podejmują próbę nawiązania kontaktu z pozostałymi siłami by przekazać im plany IMC."
+
+ "SP_TDAY" "Próba ognia"
+ "SP_TDAY_CLASSIC_DESC" "Zdolności pilotażu Coopera zostają wystawione na próbę w ostatecznym starciu o Arkę."
+
+ "SP_S2S" "Arka"
+ "SP_S2S_CLASSIC_DESC" "Cooper i BT ruszają statkiem w pościg za Arką."
+
+ "SP_SKYWAY_V1" "Zakrzywiacz"
+ "SP_SKYWAY_V1_CLASSIC_DESC" "Cooper i BT zostają pojmani przez Kubena Bliska."
+
+ // Better.Serverbrowser
+ "SERVERS_COLUMN" "Serwery"
+ "PLAYERS_COLUMN" "Gracze"
+ "MAP_COLUMN" "Mapa"
+ "GAMEMODE_COLUMN" "Tryb gry"
+ "REGION_COLUMN" "Region"
+ "SEARCHBAR_LABEL" "Wyszukiwanie:"
+ "MAP_FILTER" "Mapa"
+ "GAMEMODE_FILTER" "Tryb gry"
+ "HIDE_FULL_FILTER" "Ukryj Pełne Serwery"
+ "HIDE_EMPTY_FILTER" "Ukryj Puste Serwery"
+ "HIDE_PROT_FILTER" "Ukryj Chronione Hasłem Serwery"
+ "SERVER_DESCRIPTION" "Opis"
+ "SERVER_MODS" "Mody"
+ "CLEAR_FILTERS" "WYCZYŚĆ"
+ "JOIN_BUTTON" "DOŁĄCZ"
+
+ "SWITCH_YES" "Tak"
+ "SWITCH_NO" "Nie"
+ "SWITCH_ANY" "Dowolnie"
+
+ "CONNECTING" "Łączenie..."
+ "INGAME_PLAYERS" "Gracze: ^6BA6C400%s1"
+ "TOTAL_SERVERS" "Serwery: ^C46C6C00%s1"
+
+ // Mods menu
+ "SHOW" "Pokaż"
+ "SHOW_ALL" "Wszystko"
+ "SHOW_ONLY_ENABLED" "Tylko Włączone"
+ "SHOW_ONLY_DISABLED" "Tylko Wyłączone"
+ "SHOW_ONLY_NOT_REQUIRED" "Tylko Mody Opcjonalne"
+ "SHOW_ONLY_REQUIRED" "Tylko Mody Wymagane"
+ "MOD_REQUIRED_WARNING" " : Ten mod może zostać wyłączony po dołączeniu na serwer"
+
+ // Maps menu
+ "HIDE_LOCKED" "Ukryj zablokowane"
+
+ // In-game chat
+ "HUD_CHAT_WHISPER_PREFIX" "[SZEPT]"
+ "HUD_CHAT_SERVER_PREFIX" "[SERWER]"
+
+ "NO_GAMESERVER_RESPONSE" "Nie udało się osiągnąć serwera"
+ "BAD_GAMESERVER_RESPONSE" "Serwer dał nieprawidłową odpowiedź"
+ "UNAUTHORIZED_GAMESERVER" "Serwer nie ma autoryzacji by przedstawić takie zapytanie"
+ "UNAUTHORIZED_GAME" "Stryder nie może potwierdzić że to konto ma Titanfall 2"
+ "UNAUTHORIZED_PWD" "Nieprawidłowe hasło"
+ "STRYDER_RESPONSE" "Nieprawidłowa odpowiedź od Stryder"
+ "PLAYER_NOT_FOUND" "Nie udało się znaleźć konta gracza"
+ "INVALID_MASTERSERVER_TOKEN" "Token głównego serwera jest nieprawidłowy lub wygasł, spróbuj odpalić ponownie aplikację EA"
+ "JSON_PARSE_ERROR" "Wystąpił błąd przy sprawdzaniu pliku json"
+ "UNSUPPORTED_VERSION" "Wersja której używasz nie jest już wspierana"
+
+ "AUTHENTICATION_FAILED_HEADER" "Uwierzytelnianie się nie powiodło"
+ "AUTHENTICATION_FAILED_BODY" "Uwierzytelnianie z Atlas się nie powiodło!"
+ "AUTHENTICATION_FAILED_ERROR_CODE" "Kod błędu: ^DB6F2C00%s1^"
+ "AUTHENTICATION_FAILED_HELP" "Pomoc"
+
+ // Mod Settings
+ "MOD_SETTINGS" "Ustawienia Modów"
+ "NORTHSTAR_BASE_SETTINGS" "Główne ustawienia Northstar"
+ "ONLY_HOST_MATCH_SETTINGS" "Tylko Host może zmienić ustawienia meczu prywatnego"
+ "ONLY_HOST_CAN_START_MATCH" "Tylko Host może rozpocząć mecz"
+ "MATCH_COUNTDOWN_LENGTH" "Długość odliczana do rozpoczęcia meczu prywatnego"
+ "LOG_UNKNOWN_CLIENTCOMMANDS" "Loguj nieznane komendy klienta"
+ "DISALLOWED_TACTICALS" "Zabroniona umiejętność taktyczna"
+ "TACTICAL_REPLACEMENT" "Zastępująca umiejętność taktyczna"
+ "DISALLOWED_WEAPONS" "Zabroniona broń"
+ "REPLACEMENT_WEAPON" "Zastępująca broń"
+ "SHOULD_RETURN_TO_LOBBY" "Powróć do lobby po zakończeniu meczu"
+ "ARE_YOU_SURE" "Jesteś pewien?"
+ "WILL_RESET_ALL_SETTINGS" "Zresetujesz WSZYSTKIE ustawienia tej kategorii.\n\nAkcja jest nieodwracalna."
+ "WILL_RESET_SETTING" "Zresetujesz %s1 do startowej wartości.\n\nAkcja jest nieodwracalna."
+ "MOD_SETTINGS_SERVER" "Serwer"
+ "MOD_SETTINGS_RESET" "Zresetuj"
+ "MOD_SETTINGS_RESET_ALL" "Zresetuj WSZYSTKO"
+ "NO_RESULTS" "Brak rezultatu."
+ "NO_MODS" "Brak dostępnych ustawień, zainstaluj więcej modów w ^5588FF00northstar.thunderstore.io^0."
+
+ // Toggleable progression
+ "TOGGLE_PROGRESSION" "Przełącz Progresję"
+ "Y_BUTTON_TOGGLE_PROGRESSION" "%[Y_BUTTON|]% Przełącz Progresję"
+
+ "PROGRESSION_TOGGLE_ENABLED_HEADER" "Wyłączyć Progresję?"
+ "PROGRESSION_TOGGLE_ENABLED_BODY" "Tytany, Bronie, Fakcje, Skórki, itd. zostaną wszytskie odblokowane.\n\nDecyzję możesz zmienić kiedykolwiek w Lobby trybu wieloosobowego."
+
+ "PROGRESSION_TOGGLE_DISABLED_HEADER" "Włączyć Progresję?"
+ "PROGRESSION_TOGGLE_DISABLED_BODY" "Tytany, Bronie, Fakcje, Skórki, itd. będą musiały być odblokowane poprzez zwiększanie swojego poziomu lub zakupione.\n\nDecyzję możesz zmienić kiedykolwiek w Lobby trybu wieloosobowego.\n\n^CC000000Ostrzeżenie: jeżeli masz wyekwipowane uzbrojenie której nie jest odblokowane zostenie ono zresetowane!"
+
+ "PROGRESSION_ENABLED_HEADER" "Progresja została Włączona!"
+ "PROGRESSION_ENABLED_BODY" "^CCCC0000Progresja została Włączona.^\n\nTytany, Bronie, Fakcje, Skórki, itd. będą musiały być odblokowane poprzez zwiększanie swojego poziomu lub zakupione.\n\nDecyzję możesz zmienić kiedykolwiek w Lobby trybu wieloosobowego."
+
+ "PROGRESSION_DISABLED_HEADER" "Progresja została Wyłączona!"
+ "PROGRESSION_DISABLED_BODY" "^CCCC0000Progresja została Wyłączona.^\n\nTytany, Bronie, Fakcje, Skórki, itd. zostaną wszytskie odblokowane.\n\nDecyzję możesz zmienić kiedykolwiek w Lobby trybu wieloosobowego."
+
+ "PROGRESSION_ANNOUNCEMENT_BODY" "^CCCC0000Jest już możliwość włączenia Progresjii!^\n\nNorthstar teraz wspiera klasyczną Progresję, Dzięki niej możesz wybrać odblokowawanie Broni, Tytanów, Skórek, itd. poprzez zwiększanie swojego poziomu lub ukańczanie wyzwań.\n\nProgresja może zostać włączona poprzez przycisk na dole ekranu Lobby.\n\nDecyzję możesz zmienić kiedykolwiek."
+
+ // Mod downloading
+ "MISSING_MOD" "Brakujący Mod \"%s1\" v%s2"
+ "WRONG_MOD_VERSION" "Serwer ma Moda \"%s1\" v%s2 podczas gdy ty masz v%s3"
+ "MOD_NOT_VERIFIED" "(Mod nie został zweryfikowany i nie mógł być pobrany automatycznie)"
+ "MOD_DL_DISABLED" "(Automatyczne pobieranie modów jest wyłączone)"
+ "MANIFESTO_FETCHING_TITLE" "Przygotowywanie do pobierania Moda"
+ "MANIFESTO_FETCHING_TEXT" "Zdobywanie listy zweryfikowanych modów..."
+ "DOWNLOADING_MOD_TITLE" "Pobieranie"
+ "DOWNLOADING_MOD_TITLE_W_PROGRESS" "Pobieranie (%s1%)"
+ "DOWNLOADING_MOD_TEXT" "Pobieranie %s1 v%s2..."
+ "DOWNLOADING_MOD_TEXT_W_PROGRESS" "Pobieranie %s1 v%s2...\n(%s3/%s4 MB)"
+ "CHECKSUMING_TITLE" "Sprawdzanie wartości kontrolnej Moda"
+ "CHECKSUMING_TEXT" "Weryfikowanie zawartości %s1 v%s2..."
+ "EXTRACTING_MOD_TITLE" "Rozpakowywanie Moda (%s1%)"
+ "EXTRACTING_MOD_TEXT" "Rozpakowywanie %s1 v%s2...\n(%s3/%s4 MB)"
+ "FAILED_DOWNLOADING" "Pobieranie Moda się nie powiodło"
+ "FAILED_READING_ARCHIVE" "Wystąpił błąd podczas czytania archiwum Moda."
+ "FAILED_WRITING_TO_DISK" "Wystąpił błąd podczas rozpakowywania Moda."
+ "MOD_FETCHING_FAILED" "Pobieranie archiwum Moda się nie powiodło."
+ "MOD_CORRUPTED" "Wartość kontrolna pobranego archiwum/moda nie zgadza się z zweryfikowaną wartośćią kontrolną."
+ "NO_DISK_SPACE_AVAILABLE" "Nie ma wystarczająco miejsca na dysku."
+ "MOD_FETCHING_FAILED_GENERAL" "Rozpakowywanie Moda się nie powiodło. Sprawdź Logi po więcej detali."
+ }
+}
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_portuguese.txt b/Northstar.Client/mod/resource/northstar_client_localisation_portuguese.txt
index b6364db4e..75a5faad6 100644
--- a/Northstar.Client/mod/resource/northstar_client_localisation_portuguese.txt
+++ b/Northstar.Client/mod/resource/northstar_client_localisation_portuguese.txt
@@ -326,7 +326,7 @@ Clique em Sim se você concorda. Esta escolha pode ser alterada a qualquer momen
"UNAUTHORIZED_GAMESERVER" "Servidor da partida não está autorizado a fazer tal requisição"
"UNAUTHORIZED_PWD" "Senha inválida"
"PLAYER_NOT_FOUND" "Não foi possível encontrar conta do jogador"
- "INVALID_MASTERSERVER_TOKEN" "Token do servidor mestre inválido ou vencido"
+ "INVALID_MASTERSERVER_TOKEN" "Token do servidor mestre inválido ou vencido, tente reiniciar o EA App."
"JSON_PARSE_ERROR" "Erro ao ler a resposta json"
"SHOW_ONLY_REQUIRED" "Somente Mods Mandatórios"
"UNAUTHORIZED_GAME" "Stryder não pode confirmar que esta conta possui Titanfall 2"
@@ -338,5 +338,43 @@ Clique em Sim se você concorda. Esta escolha pode ser alterada a qualquer momen
"SHOULD_RETURN_TO_LOBBY" "Retornar ao saguão após término de Partida"
"WILL_RESET_SETTING" "Isso irá resetar a configuração %s1 ao valor padrão.\n\nAção não reversível."
"aitdm_archer_grunts" "Soldados com Archer"
+ "player_force_respawn" "Resurgimento Forçado"
+ "PROGRESSION_ENABLED_HEADER" "Progressão Habilitada!"
+ "PROGRESSION_ENABLED_BODY" "^CCCC0000Progressão foi habilitada.^\n\nTitãs, Armas, Facções, Cosméticos, etc. serão destravados ao ganhar níveis, ou comprados com Méritos.\n\nIsso pode ser alterado a qualquer momento no menu de multijogador."
+ "PROGRESSION_DISABLED_HEADER" "Progressão Desabilitada!"
+ "TOGGLE_PROGRESSION" "Habilitar Progressão"
+ "PROGRESSION_TOGGLE_ENABLED_HEADER" "Desabilitar Progressão?"
+ "PROGRESSION_TOGGLE_ENABLED_BODY" "Titãs, Armas, Facções, Cosméticos etc. serão todos destravados e usáveis a qualquer momento.\n\nIsso pode ser alterado a qualquer hora no menu de multijogador."
+ "PROGRESSION_TOGGLE_DISABLED_HEADER" "Habilitar Progressão?"
+ "PROGRESSION_TOGGLE_DISABLED_BODY" "Titãs, Armas, Facções, Cosméticos, etc. terão que ser destravados ao ganhar níveis, ou comprados com Méritos.\n\nIsso pode ser alterado a qualquer momento no menu de multijogador.\n\n^CC000000Aviso: Se você atualmente tem qualquer item equipado que não foi préviamente destravado, ele será resetado!"
+ "Y_BUTTON_TOGGLE_PROGRESSION" "%[Y_BUTTON|]% Alternar Progressão"
+ "DOWNLOADING_MOD_TITLE_W_PROGRESS" "Baixando mod (%s1%)"
+ "DOWNLOADING_MOD_TEXT" "Baixando %s1 v%s2..."
+ "MISSING_MOD" "Mod em falta \"%s1\" v%s2"
+ "MOD_NOT_VERIFIED" "(o mod não é verificado, e não pode ser baixado automaticamente)"
+ "MOD_DL_DISABLED" "(download automático de mod está desabilitado)"
+ "DOWNLOADING_MOD_TITLE" "Baixando mod"
+ "DOWNLOADING_MOD_TEXT_W_PROGRESS" "Baixando %s1 v%s2...\n(%s3/%s4 MB)"
+ "CHECKSUMING_TITLE" "Verificando o mod"
+ "CHECKSUMING_TEXT" "Verificando conteúdo de %s1 v%s2..."
+ "EXTRACTING_MOD_TITLE" "Extraíndo mod (%s1%)"
+ "MOD_REQUIRED_WARNING" " Este mod pode ser desativado quando entrar em um servidor"
+ "AUTHENTICATION_FAILED_HEADER" "Autenticação Falhou"
+ "AUTHENTICATION_FAILED_BODY" "Falha ao autenticar com Atlas!"
+ "AUTHENTICATION_FAILED_ERROR_CODE" "Código de Erro: ^DB6F2C00%s1^"
+ "AUTHENTICATION_FAILED_HELP" "Ajuda"
+ "EXTRACTING_MOD_TEXT" "Extraíndo %s1 v%s2...\n(%s3/%s4 MB)"
+ "FAILED_DOWNLOADING" "Falha ao baixar mod"
+ "FAILED_READING_ARCHIVE" "Um erro ocorreu ao ler o aqruivo do mod."
+ "MOD_FETCHING_FAILED" "O arquivo do mod não pode ser baixado da Thunderstore."
+ "MOD_CORRUPTED" "O aquivo baixado não condiz com a assinatura de verificação registrada."
+ "NO_DISK_SPACE_AVAILABLE" "Não há espaço suficiente no disco."
+ "PROGRESSION_DISABLED_BODY" "^CCCC0000Progressão foi desabilitada.^\n\nTitãs, Facções, Armas, Cosméticos, etc. estarão disponíveis para uso a qualquer momento.\n\nIsso pode ser alterado a qualquer hora no menu de multijogador."
+ "PROGRESSION_ANNOUNCEMENT_BODY" "^CCCC0000Progressão agora pode ser habilitada!^\n\nNorthstar agora suporta a progressão original, o que significa que você pode optar por destravar Armas, Cosméticos, Titãs e outros através do ganho de níveis e completando os desafios.\n\nVocê pode habilitar a progressão usando o botão no rodapé do menu de multijogador.\n\nIsso pode ser alterado a qualquer momento."
+ "WRONG_MOD_VERSION" "O servidor tem o mod \"%s1\" v%s2 enquanto você possui v%s3"
+ "FAILED_WRITING_TO_DISK" "Um erro ocorreu enquanto se extraía o conteúdo do mod para o sistema."
+ "MOD_FETCHING_FAILED_GENERAL" "Extração do mod falhou. Verifique os logs para mais detalhes."
+ "MANIFESTO_FETCHING_TEXT" "Retornando a lista de mods verificados..."
+ "MANIFESTO_FETCHING_TITLE" "Preparando o download do mod"
}
}
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_russian.txt b/Northstar.Client/mod/resource/northstar_client_localisation_russian.txt
index cf410ff29..91a0dedee 100644
--- a/Northstar.Client/mod/resource/northstar_client_localisation_russian.txt
+++ b/Northstar.Client/mod/resource/northstar_client_localisation_russian.txt
@@ -250,7 +250,7 @@
"UNAUTHORIZED_PWD" "Неправильный пароль"
"STRYDER_RESPONSE" "Не удалось разобрать ответ Stryder"
"PLAYER_NOT_FOUND" "Не удалось найти аккаунт игрока"
- "INVALID_MASTERSERVER_TOKEN" "Некорректный или истёкший токен главного сервера"
+ "INVALID_MASTERSERVER_TOKEN" "Некорректный или истёкший токен главного сервера. Перезапустите EA App, чтобы обновить токен."
"JSON_PARSE_ERROR" "Ошибка разбора json-ответа"
"UNSUPPORTED_VERSION" "Используемая вами версия больше не поддерживается"
"DISABLE" "Выключить"
@@ -345,5 +345,27 @@
"AUTHENTICATION_FAILED_ERROR_CODE" "Код ошибки: ^DB6F2C00%s1^"
"AUTHENTICATION_FAILED_HELP" "Справка"
"AUTHENTICATION_FAILED_HEADER" "Ошибка аутентификации"
+ "MISSING_MOD" "Отсутствует мод \"%s1\" v%s2"
+ "MOD_NOT_VERIFIED" "(мод не был загружен автоматически, т.к. он не проверенный)"
+ "MOD_DL_DISABLED" "(автоматическая загрузка модов не включена)"
+ "DOWNLOADING_MOD_TITLE" "Загрузка мода"
+ "DOWNLOADING_MOD_TEXT" "Загружаем %s1 v%s2..."
+ "DOWNLOADING_MOD_TEXT_W_PROGRESS" "Загружаем %s1 v%s2...\n(%s3/%s4 МБ)"
+ "DOWNLOADING_MOD_TITLE_W_PROGRESS" "Загрузка мода (%s1%)"
+ "CHECKSUMING_TITLE" "Проверка целостности"
+ "MOD_REQUIRED_WARNING" " : Этот мод может автоматически включиться / отключиться при подключении к серверу"
+ "WRONG_MOD_VERSION" "Версии мода \"%s1\" не совпадают. На сервере: v%s2. У вас: v%s3"
+ "CHECKSUMING_TEXT" "Проверяем целостность %s1 v%s2..."
+ "EXTRACTING_MOD_TITLE" "Распаковка мода (%s1%)"
+ "EXTRACTING_MOD_TEXT" "Распаковываем %s1 v%s2...\n(%s3/%s4 МБ)"
+ "FAILED_DOWNLOADING" "Ошибка загрузки мода"
+ "FAILED_READING_ARCHIVE" "Ошибка чтения архива мода."
+ "FAILED_WRITING_TO_DISK" "Ошибка распаковки файлов мода."
+ "MOD_FETCHING_FAILED" "Ошибка скачивания мода с Thunderstore."
+ "MOD_CORRUPTED" "Файл архива мода повреждён: контрольная сумма не совпадает."
+ "NO_DISK_SPACE_AVAILABLE" "Недостаточно места на диске."
+ "MOD_FETCHING_FAILED_GENERAL" "Ошибка распаковки мода. Проверьте файл лога, чтобы узнать подробности."
+ "MANIFESTO_FETCHING_TEXT" "Скачиваем список проверенных модов..."
+ "MANIFESTO_FETCHING_TITLE" "Начало загрузки модов"
}
}
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_spanish.txt b/Northstar.Client/mod/resource/northstar_client_localisation_spanish.txt
index 3d9ae4c6c..d4172232e 100644
--- a/Northstar.Client/mod/resource/northstar_client_localisation_spanish.txt
+++ b/Northstar.Client/mod/resource/northstar_client_localisation_spanish.txt
@@ -352,5 +352,6 @@ Presiona Sí al estar de acuerdo. Esta opcion se puede cambiar en el menú de mo
"WILL_RESET_SETTING" "Ésta acción reiniciará %s1 a su valor por defecto.\n\nNo se podrá revertir el proceso."
"PROGRESSION_ANNOUNCEMENT_BODY" "^CCCC0000El progreso puede ser habilitado desde ahora^\n\nNorthstar ahora es compatible con el progreso normal del juego, esto significa que puedes elegir desbloquear Armas, Aspectos, Titanes y otros a través de desafíos y subiendo de nivel.\n\nPuedes habilitar el progreso normal del juego en la opción ubicada al final de la pantalla de la sala de espera.\n\nÉsta opción puede ser cambiada en cualquier momento."
"player_force_respawn" "Reaparición Forzada"
+ "MOD_REQUIRED_WARNING" " : Esta modificacion puede ser desactivada al entrar a un servidor"
}
}
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_tchinese.txt b/Northstar.Client/mod/resource/northstar_client_localisation_tchinese.txt
index 500f8a969..fd649cdb6 100644
--- a/Northstar.Client/mod/resource/northstar_client_localisation_tchinese.txt
+++ b/Northstar.Client/mod/resource/northstar_client_localisation_tchinese.txt
@@ -320,7 +320,7 @@
"UNAUTHORIZED_PWD" "密碼錯誤"
"STRYDER_RESPONSE" "無法讀取Stryder回應"
"PLAYER_NOT_FOUND" "找不到玩家賬戶"
- "INVALID_MASTERSERVER_TOKEN" "主伺服器token過期或無效"
+ "INVALID_MASTERSERVER_TOKEN" "主伺服器token過期或無效。"
"JSON_PARSE_ERROR" "讀取json回應時發生錯誤"
"UNSUPPORTED_VERSION" "您的遊戲版本過低"
"NORTHSTAR_BASE_SETTINGS" "北极星基础设置"
@@ -356,5 +356,31 @@
"PROGRESSION_DISABLED_BODY" "^CCCC0000個人進度已停用^\n\n泰坦,武器,陣營,皮膚及所有其他一切需要解鎖的物品將隨時可以進行解鎖並使用。\n\n您可以隨時在多人大廳中更改此項。"
"PROGRESSION_ENABLED_BODY" "^CCCC0000個人進度已啟用^\n\n泰坦,武器, 陣營,皮膚以及其他一切需要解鎖的物品將通過升級或是使用點數購買來進行解鎖。.\n\n您可以隨時在多人大廳中更改此項。"
"PROGRESSION_ANNOUNCEMENT_BODY" "^CCCC0000現在可以隨時開啟個人進度!^\n\nNorthstar 現在支持類似官服的進度系統, 这意味着你可以选择通过升级和完成挑战来解锁武器、皮肤、泰坦等。\n\n您可以通過多人大廳底部的“遊戲進度”按鈕來進行開啟。\n\n您可以隨時在多人大廳中更改此項。"
+ "AUTHENTICATION_FAILED_HEADER" "認證失敗"
+ "AUTHENTICATION_FAILED_BODY" "無法與Atlas驗證!"
+ "AUTHENTICATION_FAILED_ERROR_CODE" "錯誤代碼:^DB6F2C00%s1^"
+ "AUTHENTICATION_FAILED_HELP" "幫助"
+ "MISSING_MOD" "缺少Mod \"%s1\" v%s2"
+ "MOD_NOT_VERIFIED" "(Mod未經驗證并且無法自動下載)"
+ "MOD_REQUIRED_WARNING" " : 加入一個服務器時,這個Mod可能加載或不加載"
+ "DOWNLOADING_MOD_TEXT" "正在下載Mod %s1 %s2..."
+ "DOWNLOADING_MOD_TEXT_W_PROGRESS" "正在下載Mod %s1 %s2...\n(%s3/%s4 MB)"
+ "CHECKSUMING_TITLE" "正在校驗Mod"
+ "WRONG_MOD_VERSION" "服務器的 \"%s1\" Mod版本為 %s2 而你的版本是 %s3"
+ "MOD_DL_DISABLED" "(Mod自動下載已禁用)"
+ "DOWNLOADING_MOD_TITLE" "下載Mod中"
+ "DOWNLOADING_MOD_TITLE_W_PROGRESS" "下載Mod中(%s1%)"
+ "CHECKSUMING_TEXT" "驗證内容 %s1 %s2……"
+ "EXTRACTING_MOD_TITLE" "解壓Mod中 (%s1%)"
+ "EXTRACTING_MOD_TEXT" "解壓Mod中 %s1 %s2...\n(%s3/%s4 MB)"
+ "FAILED_DOWNLOADING" "下載Mod失敗"
+ "FAILED_READING_ARCHIVE" "在讀取Mod檔案時發生錯誤。"
+ "FAILED_WRITING_TO_DISK" "在解壓Mod文件到文件系統時發生錯誤。"
+ "MOD_FETCHING_FAILED" "從Thunderstore下載Mod檔案失敗。"
+ "MOD_CORRUPTED" "已下載檔案的校驗和無法與驗證簽名匹配。"
+ "NO_DISK_SPACE_AVAILABLE" "你的存儲設備沒有足夠空間。"
+ "MOD_FETCHING_FAILED_GENERAL" "Mod解壓失敗。在日志中查看更多詳情。"
+ "MANIFESTO_FETCHING_TITLE" "設置Mod下載"
+ "MANIFESTO_FETCHING_TEXT" "獲取已驗證Mod列表……"
}
}
diff --git a/Northstar.Client/mod/resource/ui/menus/mode_select.menu b/Northstar.Client/mod/resource/ui/menus/mode_select.menu
new file mode 100644
index 000000000..f8afcf0ff
--- /dev/null
+++ b/Northstar.Client/mod/resource/ui/menus/mode_select.menu
@@ -0,0 +1,343 @@
+resource/ui/menus/mode_select.menu
+{
+ menu
+ {
+ ControlName Frame
+ xpos 0
+ ypos 0
+ zpos 3
+ wide f0
+ tall f0
+ autoResize 0
+ pinCorner 0
+ visible 1
+ enabled 1
+ PaintBackgroundType 0
+ infocus_bgcolor_override "0 0 0 0"
+ outoffocus_bgcolor_override "0 0 0 0"
+
+ MenuCommon
+ {
+ ControlName CNestedPanel
+ xpos 0
+ ypos 0
+ wide f0
+ tall f0
+ visible 1
+ controlSettingsFile "resource/ui/menus/panels/menu_common.res"
+ }
+
+ MatchmakingStatus
+ {
+ ControlName CNestedPanel
+ xpos 0
+ ypos 0
+ wide f0
+ tall f0
+ visible 1
+ controlSettingsFile "resource/ui/menus/panels/matchmaking_status.res"
+ }
+
+ NextModeImageFrame
+ {
+ ControlName RuiPanel
+ xpos 800
+ ypos 160
+ wide 860
+ tall 418
+ labelText ""
+ visible 1
+ bgcolor_override "0 0 0 0"
+ paintbackground 1
+ rui "ui/basic_border_box.rpak"
+ }
+
+ NextModeImage
+ {
+ ControlName RuiPanel
+ pin_to_sibling NextModeImageFrame
+ pin_corner_to_sibling BOTTOM
+ pin_to_sibling_corner BOTTOM
+// xpos -12
+ ypos -12
+ wide 480
+ tall 240
+ visible 1
+ scaleImage 1
+ rui "ui/basic_menu_image.rpak"
+ zpos 2
+ }
+
+ ModeIconImage
+ {
+ ControlName RuiPanel
+ pin_to_sibling NextModeImage
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner TOP_LEFT
+ xpos 0
+ ypos -16
+ wide 72
+ tall 72
+ visible 1
+ scaleImage 1
+ rui "ui/basic_image_add.rpak"
+ zpos 2
+ }
+
+ NextModeName
+ {
+ ControlName Label
+ pin_to_sibling NextModeImageFrame
+ pin_corner_to_sibling TOP
+ pin_to_sibling_corner TOP
+ ypos -20
+ wide 840
+ auto_tall_tocontents 1
+ visible 1
+ labelText "Foo"
+ textAlignment center
+ centerWrap 1
+ font Default_43_DropShadow
+ allcaps 1
+ fgcolor_override "255 255 255 255"
+ }
+
+ NextModeDesc
+ {
+ ControlName Label
+ pin_to_sibling NextModeName
+ pin_corner_to_sibling TOP
+ pin_to_sibling_corner BOTTOM
+ xpos 0
+ ypos 0
+ wide 840
+ wrap 1
+ auto_tall_tocontents 1
+ visible 1
+ labelText "Bar"
+ textAlignment center
+ centerWrap 1
+ font Default_27
+ allcaps 0
+ fgcolor_override "255 255 255 255"
+ }
+
+ MenuTitle
+ {
+ ControlName Label
+ InheritProperties MenuTitle
+ labelText "#SELECT_GAME_MODE"
+ }
+
+ ButtonRowAnchor
+ {
+ ControlName Label
+ labelText ""
+
+ xpos 96
+ ypos 160
+ }
+
+ BtnMode1
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 0
+ navUp BtnMode15
+ navDown BtnMode2
+
+ pin_to_sibling ButtonRowAnchor
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner TOP_LEFT
+ }
+ BtnMode2
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 1
+ pin_to_sibling BtnMode1
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode1
+ navDown BtnMode3
+ }
+ BtnMode3
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 2
+ pin_to_sibling BtnMode2
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode2
+ navDown BtnMode4
+ }
+ BtnMode4
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 3
+ pin_to_sibling BtnMode3
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ //ypos 11
+ navUp BtnMode3
+ navDown BtnMode5
+ }
+ BtnMode5
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 4
+ pin_to_sibling BtnMode4
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode4
+ navDown BtnMode6
+ }
+ BtnMode6
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 5
+ pin_to_sibling BtnMode5
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode5
+ navDown BtnMode7
+ }
+ BtnMode7
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 6
+ pin_to_sibling BtnMode6
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode6
+ navDown BtnMode8
+ }
+ BtnMode8
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 7
+ pin_to_sibling BtnMode7
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode7
+ navDown BtnMode9
+ }
+ BtnMode9
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 8
+ pin_to_sibling BtnMode8
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode8
+ navDown BtnMode10
+ }
+ BtnMode10
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 9
+ pin_to_sibling BtnMode9
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode9
+ navDown BtnMode11
+ }
+ BtnMode11
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 10
+ pin_to_sibling BtnMode10
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode10
+ navDown BtnMode12
+ }
+ BtnMode12
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 11
+ pin_to_sibling BtnMode11
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode11
+ navDown BtnMode13
+ }
+ BtnMode13
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 12
+ pin_to_sibling BtnMode12
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode12
+ navDown BtnMode14
+ }
+ BtnMode14
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 13
+ pin_to_sibling BtnMode13
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode13
+ navDown BtnMode15
+ }
+ BtnMode15
+ {
+ ControlName RuiButton
+ InheritProperties RuiSmallButton
+ classname ModeButton
+ scriptID 14
+ pin_to_sibling BtnMode14
+ pin_corner_to_sibling TOP_LEFT
+ pin_to_sibling_corner BOTTOM_LEFT
+ navUp BtnMode14
+ navDown BtnMode1
+ }
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ButtonTooltip
+ {
+ ControlName CNestedPanel
+ InheritProperties ButtonTooltip
+ }
+
+ FooterButtons
+ {
+ ControlName CNestedPanel
+ xpos 0
+ ypos r119
+ wide f0
+ tall 36
+ visible 1
+ controlSettingsFile "resource/ui/menus/panels/footer_buttons.res"
+ }
+ }
+}
diff --git a/Northstar.Client/mod/scripts/vscripts/cl_northstar_client_init.nut b/Northstar.Client/mod/scripts/vscripts/cl_northstar_client_init.nut
index 3560fd562..9e683a869 100644
--- a/Northstar.Client/mod/scripts/vscripts/cl_northstar_client_init.nut
+++ b/Northstar.Client/mod/scripts/vscripts/cl_northstar_client_init.nut
@@ -20,6 +20,9 @@ global struct GameStateStruct {
int otherHighestScore
int maxScore
float timeEnd
+ int serverGameState
+ int fd_waveNumber
+ int fd_totalWaves
}
global struct UIPresenceStruct {
diff --git a/Northstar.Client/mod/scripts/vscripts/presence/cl_presence.nut b/Northstar.Client/mod/scripts/vscripts/presence/cl_presence.nut
index f17216fbc..142c94bad 100644
--- a/Northstar.Client/mod/scripts/vscripts/presence/cl_presence.nut
+++ b/Northstar.Client/mod/scripts/vscripts/presence/cl_presence.nut
@@ -30,6 +30,19 @@ GameStateStruct function DiscordRPC_GenerateGameState( GameStateStruct gs )
if ( IsValid( GetLocalClientPlayer() ) )
gs.ownScore = GameRules_GetTeamScore( GetLocalClientPlayer().GetTeam() )
+ if ( GameRules_GetGameMode() == FD )
+ {
+ gs.playlist = "fd" // So it returns only one thing to the plugin side instead of the 5 separate difficulties FD have
+ if ( GetGlobalNetInt( "FD_waveState" ) == WAVE_STATE_INCOMING || GetGlobalNetInt( "FD_waveState" ) == WAVE_STATE_IN_PROGRESS )
+ {
+ gs.fd_waveNumber = GetGlobalNetInt( "FD_currentWave" ) + 1
+ gs.fd_totalWaves = GetGlobalNetInt( "FD_totalWaves" )
+ }
+ else
+ gs.fd_waveNumber = -1 // Tells plugin it's on Wave Break
+ }
+
+ gs.serverGameState = GetGameState() == -1 ? 0 : GetGameState()
gs.otherHighestScore = gs.ownScore == highestScore ? secondHighest : highestScore
gs.maxScore = IsRoundBased() ? GetCurrentPlaylistVarInt( "roundscorelimit", 0 ) : GetCurrentPlaylistVarInt( "scorelimit", 0 )
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_connect_password.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_connect_password.nut
index 1e10aa45f..b89e665b8 100644
--- a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_connect_password.nut
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_connect_password.nut
@@ -56,6 +56,6 @@ void function ConnectWithPassword( var button )
if ( GetTopNonDialogMenu() == file.menu )
{
TriggerConnectToServerCallbacks()
- thread ThreadedAuthAndConnectToServer( Hud_GetUTF8Text( Hud_GetChild( file.menu, "EnterPasswordBox" ) ) )
+ thread ThreadedAuthAndConnectToServer( Hud_GetUTF8Text( Hud_GetChild( file.menu, "EnterPasswordBox" ) ), true )
}
} \ No newline at end of file
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_moddownload.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_moddownload.nut
index 4d299362d..4968714c7 100644
--- a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_moddownload.nut
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_moddownload.nut
@@ -1,8 +1,10 @@
global function DownloadMod
global function DisplayModDownloadErrorDialog
+global function FetchVerifiedModsManifesto
global enum eModInstallStatus
{
+ MANIFESTO_FETCHING,
DOWNLOADING,
CHECKSUMING,
EXTRACTING,
@@ -18,6 +20,35 @@ global enum eModInstallStatus
const int MB = 1024*1000;
+
+void function FetchVerifiedModsManifesto()
+{
+ print("Start fetching verified mods manifesto from the Internet")
+
+ // Fetching UI
+ DialogData dialogData
+ dialogData.header = Localize( "#MANIFESTO_FETCHING_TITLE" )
+ dialogData.message = Localize( "#MANIFESTO_FETCHING_TEXT" )
+ dialogData.showSpinner = true;
+
+ // Prevent user from closing dialog
+ dialogData.forceChoice = true;
+ OpenDialog( dialogData )
+
+ // Do the actual fetching
+ NSFetchVerifiedModsManifesto()
+
+ ModInstallState state = NSGetModInstallState()
+ while ( state.status == eModInstallStatus.MANIFESTO_FETCHING )
+ {
+ state = NSGetModInstallState()
+ WaitFrame()
+ }
+
+ // Close dialog when manifesto has been received
+ CloseActiveMenu()
+}
+
bool function DownloadMod( RequiredModInfo mod )
{
// Downloading mod UI
@@ -58,6 +89,10 @@ void function UpdateModDownloadDialog( RequiredModInfo mod, ModInstallState stat
{
switch ( state.status )
{
+ case eModInstallStatus.MANIFESTO_FETCHING:
+ Hud_SetText( header, Localize( "#MANIFESTO_FETCHING_TITLE" ) )
+ Hud_SetText( body, Localize( "#MANIFESTO_FETCHING_TEXT" ) )
+ break
case eModInstallStatus.DOWNLOADING:
Hud_SetText( header, Localize( "#DOWNLOADING_MOD_TITLE_W_PROGRESS", string( state.ratio ) ) )
Hud_SetText( body, Localize( "#DOWNLOADING_MOD_TEXT_W_PROGRESS", mod.name, mod.version, floor( state.progress / MB ), floor( state.total / MB ) ) )
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_modmenu.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_modmenu.nut
index 3f643aa3d..f08d69a72 100644
--- a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_modmenu.nut
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_modmenu.nut
@@ -338,6 +338,10 @@ void function RefreshMods()
{
string mod = modNames[i]
+ // Do not display remote mods
+ if ( NSIsModRemote( mod ) )
+ continue
+
if ( searchTerm.len() && mod.tolower().find( searchTerm ) == null )
continue
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut
index 1bc8e405b..cdeb8b3e0 100644
--- a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut
@@ -967,49 +967,93 @@ void function OnServerSelected_Threaded( var button )
bool autoDownloadAllowed = GetConVarBool( "allow_mod_auto_download" )
int downloadedMods = 0;
+ // Check out if there's any server-required mod that is not locally installed
+ array<string> modNames = NSGetModNames()
+ bool uninstalledModFound = false
+ foreach ( requiredModInfo in server.requiredMods )
+ {
+ // Tolerate core mods having different versions
+ if ( requiredModInfo.name.len() > 10 && requiredModInfo.name.slice(0, 10) == "Northstar." )
+ continue
+
+ if ( !modNames.contains( requiredModInfo.name ) )
+ {
+ print( format ( "\"%s\" was not found locally, triggering manifesto fetching.", requiredModInfo.name ) )
+ uninstalledModFound = true
+ break
+ } else if ( NSGetModVersionByModName( requiredModInfo.name ) != requiredModInfo.version ) {
+ print( format ( "\"%s\" was found locally but has version \"%s\" while server requires \"%s\", triggering manifesto fetching.", requiredModInfo.name, NSGetModVersionByModName( requiredModInfo.name ), requiredModInfo.version ) )
+ uninstalledModFound = true
+ break
+ }
+ }
+
+ // If yes, we fetch the verified mods manifesto, to check whether uninstalled
+ // mods can be installed through auto-download
+ if ( uninstalledModFound && autoDownloadAllowed )
+ {
+ print("Auto-download is allowed, checking if missing mods can be installed automatically.")
+ FetchVerifiedModsManifesto()
+ }
+
foreach ( RequiredModInfo mod in server.requiredMods )
{
- if ( !NSGetModNames().contains( mod.name ) )
+ // Tolerate core mods having different versions
+ if ( mod.name.len() > 10 && mod.name.slice(0, 10) == "Northstar." )
+ continue
+
+ if ( !NSGetModNames().contains( mod.name ) || NSGetModVersionByModName( mod.name ) != mod.version )
{
- // Check if mod can be auto-downloaded
- bool modIsVerified = NSIsModDownloadable( mod.name, mod.version )
+ // Auto-download mod
+ if ( autoDownloadAllowed )
+ {
+ bool modIsVerified = NSIsModDownloadable( mod.name, mod.version )
- // Display an error message if not
- if ( !modIsVerified || !autoDownloadAllowed )
+ // Display error message if mod is not verified
+ if ( !modIsVerified )
+ {
+ DialogData dialogData
+ dialogData.header = "#ERROR"
+ dialogData.message = Localize( "#MISSING_MOD", mod.name, mod.version )
+ dialogData.message += "\n" + Localize( "#MOD_NOT_VERIFIED" )
+ dialogData.image = $"ui/menu/common/dialog_error"
+
+ AddDialogButton( dialogData, "#DISMISS" )
+ AddDialogFooter( dialogData, "#A_BUTTON_SELECT" )
+ AddDialogFooter( dialogData, "#B_BUTTON_DISMISS_RUI" )
+
+ OpenDialog( dialogData )
+ return
+ }
+ else
+ {
+ if ( DownloadMod( mod ) )
+ {
+ downloadedMods++
+ }
+ else
+ {
+ DisplayModDownloadErrorDialog( mod.name )
+ return
+ }
+ }
+ }
+
+ // Mod not found, display error message
+ else
{
DialogData dialogData
dialogData.header = "#ERROR"
dialogData.message = Localize( "#MISSING_MOD", mod.name, mod.version )
dialogData.image = $"ui/menu/common/dialog_error"
- // Specify error (only if autoDownloadAllowed is set)
- if ( autoDownloadAllowed )
- {
- dialogData.message += "\n" + Localize( "#MOD_NOT_VERIFIED" )
- }
-
AddDialogButton( dialogData, "#DISMISS" )
-
AddDialogFooter( dialogData, "#A_BUTTON_SELECT" )
AddDialogFooter( dialogData, "#B_BUTTON_DISMISS_RUI" )
OpenDialog( dialogData )
-
return
}
-
- else // Launch download
- {
- if ( DownloadMod( mod ) )
- {
- downloadedMods++
- }
- else
- {
- DisplayModDownloadErrorDialog( mod.name )
- return
- }
- }
}
else
{
@@ -1050,13 +1094,6 @@ void function OnServerSelected_Threaded( var button )
}
}
- // Make Northstar aware new mods have been added
- if ( downloadedMods > 0 )
- {
- print( "Some new mods have been downloaded or enabled, reloading mods." )
- NSReloadMods();
- }
-
if ( server.requiresPassword )
{
OnCloseServerBrowserMenu()
@@ -1065,12 +1102,12 @@ void function OnServerSelected_Threaded( var button )
else
{
TriggerConnectToServerCallbacks()
- thread ThreadedAuthAndConnectToServer()
+ thread ThreadedAuthAndConnectToServer( "", downloadedMods != 0 )
}
}
-void function ThreadedAuthAndConnectToServer( string password = "" )
+void function ThreadedAuthAndConnectToServer( string password = "", bool modsChanged = false )
{
if ( NSIsAuthenticatingWithServer() )
return
@@ -1098,8 +1135,6 @@ void function ThreadedAuthAndConnectToServer( string password = "" )
if ( NSWasAuthSuccessful() )
{
- bool modsChanged = false
-
// disable all RequiredOnClient mods that are not required by the server and are currently enabled
foreach ( string modName in NSGetModNames() )
{
diff --git a/Northstar.Custom/keyvalues/playlists_v2.txt b/Northstar.Custom/keyvalues/playlists_v2.txt
index c22e2174d..d60a24e5c 100644
--- a/Northstar.Custom/keyvalues/playlists_v2.txt
+++ b/Northstar.Custom/keyvalues/playlists_v2.txt
@@ -395,7 +395,6 @@ playlists
mp_colony02 1
mp_glitch 1
mp_lf_stacks 1
- mp_lf_stacks 1
mp_lf_deck 1
mp_lf_meadow 1
mp_lf_traffic 1
@@ -449,7 +448,6 @@ playlists
mp_colony02 1
mp_glitch 1
mp_lf_stacks 1
- mp_lf_stacks 1
mp_lf_deck 1
mp_lf_meadow 1
mp_lf_traffic 1
@@ -498,7 +496,6 @@ playlists
mp_colony02 1
mp_glitch 1
mp_lf_stacks 1
- mp_lf_stacks 1
mp_lf_deck 1
mp_lf_meadow 1
mp_lf_traffic 1
@@ -626,35 +623,27 @@ playlists
{
maps
{
- mp_complex3 1
- mp_drydock 1
- mp_glitch 1
- mp_homestead 2
- mp_eden 1
mp_forwardbase_kodai 1
- mp_black_water_canal 1
- mp_glitch 1
- mp_angel_city 1
- mp_colony02 1
- mp_relic02 1
mp_grave 1
mp_homestead 1
- mp_drydock 1
- mp_glitch 1
mp_thaw 1
- mp_eden 2
mp_black_water_canal 1
- mp_glitch 1
- mp_relic02 1
- mp_wargames 1
- mp_rise 1
+ mp_eden 1
+ mp_drydock 1
mp_crashsite3 1
+ mp_complex3 1
+ mp_angel_city 1
+ mp_colony02 1
+ mp_glitch 1
mp_lf_stacks 1
mp_lf_deck 1
mp_lf_meadow 1
mp_lf_traffic 1
mp_lf_township 1
mp_lf_uma 1
+ mp_relic02 1
+ mp_wargames 1
+ mp_rise 1
}
}
}
@@ -681,35 +670,27 @@ playlists
{
maps
{
- mp_complex3 1
- mp_drydock 1
- mp_glitch 1
- mp_homestead 2
- mp_eden 1
mp_forwardbase_kodai 1
- mp_black_water_canal 1
- mp_glitch 1
- mp_angel_city 1
- mp_colony02 1
- mp_relic02 1
mp_grave 1
mp_homestead 1
- mp_drydock 1
- mp_glitch 1
mp_thaw 1
- mp_eden 2
mp_black_water_canal 1
- mp_glitch 1
- mp_relic02 1
- mp_wargames 1
- mp_rise 1
+ mp_eden 1
+ mp_drydock 1
mp_crashsite3 1
+ mp_complex3 1
+ mp_angel_city 1
+ mp_colony02 1
+ mp_glitch 1
mp_lf_stacks 1
mp_lf_deck 1
mp_lf_meadow 1
mp_lf_traffic 1
mp_lf_township 1
mp_lf_uma 1
+ mp_relic02 1
+ mp_wargames 1
+ mp_rise 1
}
}
}
@@ -747,7 +728,6 @@ playlists
mp_colony02 1
mp_glitch 1
mp_lf_stacks 1
- mp_lf_stacks 1
mp_lf_deck 1
mp_lf_meadow 1
mp_lf_traffic 1
@@ -795,7 +775,6 @@ playlists
mp_colony02 1
mp_glitch 1
mp_lf_stacks 1
- mp_lf_stacks 1
mp_lf_deck 1
mp_lf_meadow 1
mp_lf_traffic 1
@@ -829,18 +808,18 @@ playlists
{
maps
{
- mp_forwardbase_kodai 1
- mp_grave 1
- mp_homestead 1
- mp_thaw 1
- mp_black_water_canal 1
- mp_eden 1
- mp_drydock 1
- mp_crashsite3 1
- mp_complex3 1
- mp_angel_city 1
- mp_colony02 1
- mp_glitch 1
+ mp_forwardbase_kodai 1
+ mp_grave 1
+ mp_homestead 1
+ mp_thaw 1
+ mp_black_water_canal 1
+ mp_eden 1
+ mp_drydock 1
+ mp_crashsite3 1
+ mp_complex3 1
+ mp_angel_city 1
+ mp_colony02 1
+ mp_glitch 1
mp_relic02 1
mp_wargames 1
mp_rise 1
diff --git a/Northstar.Custom/mod.json b/Northstar.Custom/mod.json
index 399311e43..69432f498 100644
--- a/Northstar.Custom/mod.json
+++ b/Northstar.Custom/mod.json
@@ -441,6 +441,19 @@
"RunOn": "CLIENT || SERVER || UI"
},
{
+ "Path": "_testing.nut",
+ "RunOn": "CLIENT || SERVER || UI",
+ "ClientCallback": {
+ "Before": "Testing_Init"
+ },
+ "ServerCallback": {
+ "Before": "Testing_Init"
+ },
+ "UICallback": {
+ "Before": "Testing_Init"
+ }
+ },
+ {
"Path": "_event_models.gnut",
"RunOn": "SERVER && LOBBY",
"ServerCallback": {
diff --git a/Northstar.Custom/mod/models/weapons/shotgun_doublebarrel/ptpov_shotgun_doublebarrel.mdl b/Northstar.Custom/mod/models/weapons/shotgun_doublebarrel/ptpov_shotgun_doublebarrel.mdl
index 2fcc34390..82b58becc 100644
--- a/Northstar.Custom/mod/models/weapons/shotgun_doublebarrel/ptpov_shotgun_doublebarrel.mdl
+++ b/Northstar.Custom/mod/models/weapons/shotgun_doublebarrel/ptpov_shotgun_doublebarrel.mdl
Binary files differ
diff --git a/Northstar.Custom/mod/models/weapons/shotgun_doublebarrel/w_shotgun_doublebarrel.mdl b/Northstar.Custom/mod/models/weapons/shotgun_doublebarrel/w_shotgun_doublebarrel.mdl
index 137d985d6..9e6603745 100644
--- a/Northstar.Custom/mod/models/weapons/shotgun_doublebarrel/w_shotgun_doublebarrel.mdl
+++ b/Northstar.Custom/mod/models/weapons/shotgun_doublebarrel/w_shotgun_doublebarrel.mdl
Binary files differ
diff --git a/Northstar.Custom/mod/scripts/vscripts/_testing.nut b/Northstar.Custom/mod/scripts/vscripts/_testing.nut
new file mode 100644
index 000000000..15bcf18ba
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/_testing.nut
@@ -0,0 +1,302 @@
+global function Testing_Init
+global function RunAllTests
+global function RunAllTests_SaveToFile
+global function RunTestsByCategory
+global function RunTestByCategoryAndName
+
+global function AddTest
+
+struct TestInfo
+{
+ string testName
+ var functionref() callback
+ // whether the test completed successfully
+ // if this is true, actualResult is valid
+ bool completed
+ // var not string because then i can just set it to an exception
+ // which print can then handle
+ var error
+ // whether the test is considered successful
+ var expectedResult
+ var actualResult
+ bool passed
+}
+
+struct {
+ table< string, array< TestInfo > > tests = {}
+} file
+
+void function Testing_Init()
+{
+ // tests for the testing functions :)
+ //AddTest( "Example Tests", "example succeeding test", ExampleTest_ReturnsTrue, true )
+ //AddTest( "Example Tests", "example failing test", ExampleTest_ReturnsFalse, true )
+ //AddTest( "Example Tests", "example erroring test", ExampleTest_ThrowsError, true )
+ //AddTest( "Example Tests", "example test with args", var function() {
+ // return ExampleTest_HasArgs_ReturnsNonVar( 2, 3 )
+ //}, 6 )
+}
+
+int function ExampleTest_HasArgs_ReturnsNonVar( int first, int second )
+{
+ return first * second
+}
+
+var function ExampleTest_ReturnsFalse()
+{
+ return false
+}
+
+var function ExampleTest_ReturnsTrue()
+{
+ return true
+}
+
+var function ExampleTest_ThrowsError()
+{
+ throw "Example exception"
+ return null
+}
+
+void function RunAllTests_SaveToFile()
+{
+ RunAllTests()
+
+ #if UI
+ string fileName = "ns-unit-tests-UI.json"
+ #elseif CLIENT
+ string fileName = "ns-unit-tests-CLIENT.json"
+ #elseif SERVER
+ string fileName = "ns-unit-tests-SERVER.json"
+ #endif
+
+ // cant encode structs so have to reconstruct a table manually from the structs
+ table out = {}
+ foreach ( category, tests in file.tests )
+ {
+ array categoryResults = []
+ foreach ( test in tests )
+ {
+ table testTable = {}
+ testTable[ "name" ] <- test.testName
+ testTable[ "completed" ] <- test.completed
+ testTable[ "passed" ] <- test.passed
+ if ( !test.completed )
+ testTable[ "error" ] <- test.error
+ else if ( !test.passed )
+ {
+ testTable[ "expectedResult" ] <- test.expectedResult
+ testTable[ "actualResult" ] <- test.actualResult
+ }
+
+ categoryResults.append( testTable )
+ }
+ out[ category ] <- categoryResults
+ }
+
+ NSSaveJSONFile( fileName, out )
+}
+
+void function RunAllTests()
+{
+ printt( "Running all tests!" )
+
+ foreach ( category, categoryTests in file.tests )
+ {
+ foreach ( test in categoryTests )
+ {
+ RunTest( test )
+ }
+ }
+
+ PrintAllTestResults()
+}
+
+void function RunTestsByCategory( string category )
+{
+ if ( !( category in file.tests ) )
+ {
+ printt( format( "Category '%s' has no tests registered", category ) )
+ return
+ }
+
+ foreach ( categoryTest in file.tests[ category ] )
+ {
+ RunTest( categoryTest )
+ }
+}
+
+void function RunTestByCategoryAndName( string category, string testName )
+{
+ // find test
+ if ( !( category in file.tests ) )
+ {
+ printt( format( "Category '%s' has no tests registered", category ) )
+ return
+ }
+
+ TestInfo ornull foundTest = null
+ foreach ( categoryTest in file.tests[ category ] )
+ {
+ if ( categoryTest.testName == testName )
+ {
+ foundTest = categoryTest
+ break
+ }
+ }
+
+ if ( !foundTest )
+ {
+ printt( format( "Category '%s' does not contain a test with name '%s'", category, testName ) )
+ return
+ }
+
+ expect TestInfo( foundTest )
+
+ printt( "Running test!" )
+ // run test
+ RunTest( foundTest )
+ // print result
+ PrintTestResult( foundTest )
+}
+
+void function RunTest( TestInfo test )
+{
+ test.completed = false
+ test.passed = false
+ test.actualResult = null
+ test.error = ""
+
+ try
+ {
+ test.actualResult = test.callback()
+ test.completed = true
+ test.passed = test.actualResult == test.expectedResult
+ }
+ catch ( exception )
+ {
+ test.completed = false
+ test.error = exception
+ }
+}
+
+void function PrintAllTestResults()
+{
+ int totalSucceeded = 0
+ int totalFailed = 0
+ int totalErrored = 0
+
+ foreach ( category, categoryTests in file.tests )
+ {
+ int categorySucceeded = 0
+ int categoryFailed = 0
+ int categoryErrored = 0
+
+ printt( format( "Results for category: '%s'", category ) )
+ foreach ( test in categoryTests )
+ {
+ if ( test.completed )
+ {
+ if ( test.passed )
+ {
+ printt( "\t", test.testName, "- Passed!" )
+ categorySucceeded++
+ }
+ else
+ {
+ printt( "\t", test.testName, "- Failed!" )
+ printt( "\t\tExpected:", test.expectedResult )
+ printt( "\t\tActual: ", test.actualResult )
+ categoryFailed++
+ }
+ }
+ else
+ {
+ printt( "\t", test.testName, "- Errored!" )
+ printt( "\t\tError:", test.error )
+ categoryErrored++
+ }
+ }
+
+ printt( "Succeeded:", categorySucceeded, "Failed:", categoryFailed, "Errored:", categoryErrored )
+
+ totalSucceeded += categorySucceeded
+ totalFailed += categoryFailed
+ totalErrored += categoryErrored
+ }
+
+ printt( "TOTAL SUCCEEDED:", totalSucceeded, "TOTAL FAILED:", totalFailed, "TOTAL ERRORED:", totalErrored )
+}
+
+void function PrintCategoryResults( string category )
+{
+ int categorySucceeded = 0
+ int categoryFailed = 0
+ int categoryErrored = 0
+
+ printt( format( "Results for category: '%s'", category ) )
+ foreach ( test in file.tests[ category ] )
+ {
+ if ( test.completed )
+ {
+ if ( test.passed )
+ {
+ printt( "\t", test.testName, "- Passed!" )
+ categorySucceeded++
+ }
+ else
+ {
+ printt( "\t", test.testName, "- Failed!" )
+ printt( "\t\tExpected:", test.expectedResult )
+ printt( "\t\tActual: ", test.actualResult )
+ categoryFailed++
+ }
+ }
+ else
+ {
+ printt( "\t", test.testName, "- Errored!" )
+ printt( "\t\tError:", test.error )
+ categoryErrored++
+ }
+ }
+
+ printt( "Succeeded:", categorySucceeded, "Failed:", categoryFailed, "Errored:", categoryErrored )
+}
+
+void function PrintTestResult( TestInfo test )
+{
+ string resultString = test.testName
+
+ if ( test.completed )
+ {
+ if ( test.passed )
+ resultString += " - Passed!"
+ else
+ {
+ resultString += " - Failed!"
+ resultString += "\n\tExpected: " + test.expectedResult
+ resultString += "\n\tActual: " + test.actualResult
+ }
+ }
+ else
+ {
+ resultString += " - Not completed!"
+ resultString += "\n\tError: " + test.error
+ }
+
+ printt( resultString )
+}
+
+void function AddTest( string testCategory, string testName, var functionref() testFunc, var expectedResult )
+{
+ TestInfo newTest
+ newTest.testName = testName
+ newTest.callback = testFunc
+ newTest.expectedResult = expectedResult
+
+ // create the test category if it doesn't exist
+ if ( !( testCategory in file.tests ) )
+ file.tests[ testCategory ] <- [ newTest ]
+ else
+ file.tests[ testCategory ].append( newTest )
+}
diff --git a/Northstar.Custom/mod/scripts/vscripts/earn_meter/cl_earn_meter.gnut b/Northstar.Custom/mod/scripts/vscripts/earn_meter/cl_earn_meter.gnut
index 16908362c..3971d2bec 100644
--- a/Northstar.Custom/mod/scripts/vscripts/earn_meter/cl_earn_meter.gnut
+++ b/Northstar.Custom/mod/scripts/vscripts/earn_meter/cl_earn_meter.gnut
@@ -335,7 +335,11 @@ void function EarnMeter_Update()
break
entity soul = player.GetTitanSoul()
- entity core = player.GetOffhandWeapons()[3]
+ entity core = player.GetOffhandWeapon( OFFHAND_EQUIPMENT )
+
+ if ( !IsValid( core ) )
+ break
+
string coreName = core.GetWeaponClassName()
array<string> coreMods = core.GetMods()
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_inf.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_inf.gnut
index fef4c8b6c..02f0799a1 100644
--- a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_inf.gnut
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_inf.gnut
@@ -45,7 +45,16 @@ void function SelectFirstInfectedDelayed()
wait 10.0 + RandomFloat( 5.0 )
array<entity> players = GetPlayerArray()
- entity infected = players[ RandomInt( players.len() ) ]
+
+ // End game if server empty on selecting infected
+ if ( !players.len() )
+ {
+ printt( "Couldn't select first infected: player array was empty" )
+ SetWinner( INFECTION_TEAM_SURVIVOR )
+ return
+ }
+
+ entity infected = players.getrandom()
InfectPlayer( infected )
RespawnInfected( infected )
diff --git a/Northstar.Custom/mod/scripts/vscripts/sh_damage_types.nut b/Northstar.Custom/mod/scripts/vscripts/sh_damage_types.nut
index 4987ee015..bae0116ef 100644
--- a/Northstar.Custom/mod/scripts/vscripts/sh_damage_types.nut
+++ b/Northstar.Custom/mod/scripts/vscripts/sh_damage_types.nut
@@ -727,7 +727,7 @@ bool function RegisterWeaponDamageSourceInternal( int id, string newVal, string
damageSourceID[ newVal ] <- id
file.damageSourceIDToString[ id ] <- newVal
file.damageSourceIDToName[ id ] <- stringVal
- file.customDamageSourceIDList.extend( [ id.tostring(), newVal, StringReplace( stringVal, " ", MESSAGE_SPACE_PADDING ) ] )
+ file.customDamageSourceIDList.extend( [ id.tostring(), newVal, StringReplace( stringVal, " ", MESSAGE_SPACE_PADDING, true ) ] )
return true
}
@@ -773,6 +773,6 @@ void function ReceiveNewDamageSourceIDs( array<string> args )
{
// IDs are inserted to the custom list in triplets, so we can trust these indices exist and the loop will end properly
for ( int i = 0; i < args.len(); i += 3 )
- RegisterWeaponDamageSourceInternal( args[ i ].tointeger(), args[ i + 1 ], StringReplace( args[ i + 2 ], MESSAGE_SPACE_PADDING, " " ) )
+ RegisterWeaponDamageSourceInternal( args[ i ].tointeger(), args[ i + 1 ], StringReplace( args[ i + 2 ], MESSAGE_SPACE_PADDING, " ", true ) )
}
#endif
diff --git a/Northstar.Custom/mod/scripts/vscripts/weapons/_arc_cannon.nut b/Northstar.Custom/mod/scripts/vscripts/weapons/_arc_cannon.nut
index defb1a56f..e198f7265 100644
--- a/Northstar.Custom/mod/scripts/vscripts/weapons/_arc_cannon.nut
+++ b/Northstar.Custom/mod/scripts/vscripts/weapons/_arc_cannon.nut
@@ -85,28 +85,30 @@ global const ARC_CANNON_BEAM_EFFECT_MOD = $"wpn_arc_cannon_beam_mod"
global const ARC_CANNON_FX_TABLE = "exp_arc_cannon"
global const ArcCannonTargetClassnames = {
- [ "npc_drone" ] = true,
- [ "npc_dropship" ] = true,
- [ "npc_marvin" ] = true,
- [ "npc_prowler" ] = true,
- [ "npc_soldier" ] = true,
- [ "npc_soldier_heavy" ] = true,
- [ "npc_soldier_shield" ] = true,
- [ "npc_spectre" ] = true,
- [ "npc_stalker" ] = true,
- [ "npc_super_spectre" ] = true,
- [ "npc_titan" ] = true,
- [ "npc_turret_floor" ] = true,
- [ "npc_turret_mega" ] = true,
- [ "npc_turret_sentry" ] = true,
- [ "npc_frag_drone" ] = true,
- [ "player" ] = true,
- [ "prop_dynamic" ] = true,
- [ "prop_script" ] = true,
- [ "grenade_frag" ] = true,
- [ "rpg_missile" ] = true,
- [ "script_mover" ] = true,
- [ "turret" ] = true,
+ [ "npc_drone" ] = true,
+ [ "npc_dropship" ] = true,
+ [ "npc_gunship" ] = true,
+ [ "npc_marvin" ] = true,
+ [ "npc_prowler" ] = true,
+ [ "npc_soldier" ] = true,
+ [ "npc_soldier_heavy" ] = true,
+ [ "npc_soldier_shield" ] = true,
+ [ "npc_pilot_elite" ] = true,
+ [ "npc_spectre" ] = true,
+ [ "npc_stalker" ] = true,
+ [ "npc_super_spectre" ] = true,
+ [ "npc_titan" ] = true,
+ [ "npc_turret_floor" ] = true,
+ [ "npc_turret_mega" ] = true,
+ [ "npc_turret_sentry" ] = true,
+ [ "npc_frag_drone" ] = true,
+ [ "player" ] = true,
+ [ "prop_dynamic" ] = true,
+ [ "prop_script" ] = true,
+ [ "grenade_frag" ] = true,
+ [ "rpg_missile" ] = true,
+ [ "script_mover" ] = true,
+ [ "turret" ] = true,
}
struct {
diff --git a/Northstar.Custom/paks/mp_weapon_shotgun_doublebarrel.rpak b/Northstar.Custom/paks/mp_weapon_shotgun_doublebarrel.rpak
index 3a714d3b6..440f16aef 100644
--- a/Northstar.Custom/paks/mp_weapon_shotgun_doublebarrel.rpak
+++ b/Northstar.Custom/paks/mp_weapon_shotgun_doublebarrel.rpak
Binary files differ
diff --git a/Northstar.Custom/paks/mp_weapon_shotgun_doublebarrel.starpak b/Northstar.Custom/paks/mp_weapon_shotgun_doublebarrel.starpak
index 3bd344679..93e59c892 100644
--- a/Northstar.Custom/paks/mp_weapon_shotgun_doublebarrel.starpak
+++ b/Northstar.Custom/paks/mp_weapon_shotgun_doublebarrel.starpak
Binary files differ
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_items.nut b/Northstar.CustomServers/mod/scripts/vscripts/_items.nut
index a23a68b0c..96623086c 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_items.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_items.nut
@@ -10094,7 +10094,7 @@ void function InitUnlockAsEntitlement( string itemRef, string parentRef, int ent
unlock = file.entitlementUnlocks[fullRef]
}
- unlock.entitlementIds.append( entitlementId )
+ unlock.entitlementIds.append( 1 ) // Using `1` here instead of the huge DLC check I did previously. Having the `1` seems to keep all paid cosmetics unlocked with progression enabled.
}
array<int> function GetEntitlementIds( string itemRef, string parentRef = "" )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_loadouts_mp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_loadouts_mp.gnut
index 103efe6f6..bd289d4c2 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_loadouts_mp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_loadouts_mp.gnut
@@ -54,8 +54,6 @@ void function SvLoadoutsMP_Init()
AddClientCommandCallback( "InGameMPMenuClosed", ClientCommandCallback_InGameMPMenuClosed )
AddClientCommandCallback( "LoadoutMenuClosed", ClientCommandCallback_LoadoutMenuClosed )
}
-
- AddCallback_OnPlayerKilled( DestroyDroppedWeapon )
}
void function SetLoadoutGracePeriodEnabled( bool enabled )
@@ -70,20 +68,10 @@ void function SetAllowLoadoutChangeFunc( bool functionref( entity ) func )
void function SetWeaponDropsEnabled( bool enabled )
{
- file.weaponDropsEnabled = enabled
-}
-
-void function DestroyDroppedWeapon( entity victim, entity attacker, var damageInfo )
-{
- if ( !file.weaponDropsEnabled && IsValid( victim.GetActiveWeapon() ) )
- thread DelayDestroyDroppedWeapon( victim.GetActiveWeapon() )
-}
-
-void function DelayDestroyDroppedWeapon( entity weapon )
-{
- WaitEndFrame()
- if ( IsValid( weapon ) )
- weapon.Destroy()
+ if( enabled )
+ FlagSet( "WeaponDropsAllowed" )
+ else
+ FlagClear( "WeaponDropsAllowed" )
}
void function AddCallback_OnTryGetTitanLoadout( TryGetTitanLoadoutCallbackType callback )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut
index 2b95d1a8c..97d993e65 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut
@@ -16,7 +16,10 @@ void function SetupPlayerPreviousXPValues( entity player )
player.SetPersistentVar( "previousFactionXP[" + xpFaction + "]", FactionGetXP( player, xpFaction ) )
foreach ( string xpTitan in shTitanXP.titanClasses )
+ {
player.SetPersistentVar( "previousTitanXP[" + xpTitan + "]", TitanGetXP( player, xpTitan ) )
+ player.SetPersistentVar( "fdPreviousTitanXP[" + xpTitan + "]", FD_TitanGetXP( player, xpTitan ) )
+ }
foreach ( string xpWeapon in shWeaponXP.weaponClassNames )
player.SetPersistentVar( GetItemPersistenceStruct( xpWeapon ) + ".previousWeaponXP", WeaponGetXP( player, xpWeapon ) )
@@ -35,6 +38,14 @@ void function HandleXPGainForScoreEvent( entity player, ScoreEvent event )
int weaponXp = ScoreEvent_GetXPValueWeapon( event )
int titanXp = ScoreEvent_GetXPValueTitan( event )
int factionXp = ScoreEvent_GetXPValueFaction( event )
+
+ if ( player.GetPlayerNetInt( "xpMultiplier" ) > 0 || GetCurrentPlaylistVarInt( "double_xp_enabled", 0 ) == 1 )
+ {
+ xpValue *= 2
+ weaponXp *= 2
+ titanXp *= 2
+ factionXp *= 2
+ }
entity weapon = player.GetActiveWeapon()
if ( IsValid( weapon ) && ShouldTrackXPForWeapon( weapon.GetWeaponClassName() ) && weaponXp != 0 )
@@ -63,5 +74,10 @@ void function AddXP( entity player, int amount )
int newXp = player.GetPersistentVarAsInt( "xp" )
int newLevel = GetLevelForXP( newXp )
if ( newLevel != oldLevel )
+ {
Remote_CallFunction_NonReplay( player, "ServerCallback_PlayerLeveledUp", player.GetPersistentVarAsInt( "gen" ), newLevel )
+
+ if( ProgressionEnabledForPlayer( player ) )
+ AwardRandomItemsForPlayerLevels( player, player.GetPersistentVarAsInt( "gen" ), newLevel )
+ }
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut b/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut
index af074689c..760daef0b 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut
@@ -246,6 +246,9 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
SetTeamActiveObjective( evacTeam, "EG_DropshipExtract", Time() + arrivalTime, file.evacIcon )
SetTeamActiveObjective( GetOtherTeam( evacTeam ), "EG_StopExtract", Time() + arrivalTime, file.evacIcon )
+ foreach( entity player in GetPlayerArrayOfTeam( evacTeam ) ) //Show the Evac Match Goal for players of the team that lost the match
+ Remote_CallFunction_UI( player, "SCB_SetEvacMeritState", 0 )
+
// would've liked to use cd_dropship_rescue_side_start length here, but can't since this is done before dropship spawn, can't
wait arrivalTime - 4.33333
@@ -280,6 +283,9 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
{
SetTeamActiveObjective( evacTeam, "EG_DropshipExtractDropshipDestroyed" )
SetTeamActiveObjective( GetOtherTeam( evacTeam ), "EG_DropshipExtractDropshipDestroyed" )
+
+ foreach( entity player in GetPlayerArrayOfTeam( evacTeam ) )
+ SetPlayerChallengeEvacState( player, 0 )
}
foreach ( entity player in dropship.s.evacSlots )
@@ -402,7 +408,10 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
if ( !PlayerInDropship( player, dropship ) )
{
if ( player.GetTeam() == dropship.GetTeam() )
+ {
SetPlayerActiveObjective( player, "EG_DropshipExtractFailedEscape" )
+ SetPlayerChallengeEvacState( player, 2 )
+ }
continue
}
@@ -417,6 +426,7 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
Remote_CallFunction_NonReplay( player, "ServerCallback_DisableHudForEvac" )
Remote_CallFunction_NonReplay( player, "ServerCallback_SetClassicSkyScale", dropship.GetEncodedEHandle(), 0.7 )
Remote_CallFunction_NonReplay( player, "ServerCallback_SetMapSettings", 4.0, false, 0.4, 0.125 )
+ SetPlayerChallengeEvacState( player, 1 )
// display player [Evacuated] in killfeed
foreach ( entity otherPlayer in GetPlayerArray() )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/faction_xp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/faction_xp.gnut
index 6555c875b..5aee11044 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/faction_xp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/faction_xp.gnut
@@ -4,10 +4,19 @@ void function AddFactionXP( entity player, int amount )
{
string faction = GetFactionChoice( player )
int oldLevel = FactionGetLevel( player, faction )
+ int FactionXPMatch = player.GetPersistentVarAsInt( "xp_match[" + XP_TYPE.FACTION_LEVELED + "]" )
+
// increment xp
player.SetPersistentVar( "factionXP[" + faction + "]", min( FactionGetXP( player, faction ) + amount, FactionGetMaxXP( faction ) ) )
// note: no notif for faction level up
if ( FactionGetLevel( player, faction ) != oldLevel )
+ {
AddPlayerScore( player, "FactionLevelUp" )
+ IncrementPlayerChallengeFactionLeveledUp( player )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.FACTION_LEVELED + "]", FactionXPMatch + 1 )
+
+ if( ProgressionEnabledForPlayer( player ) )
+ AwardRandomItemsForFactionLevels( player, faction, oldLevel, FactionGetLevel( player, faction ) )
+ }
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
index f47ee90f9..cacb54cf1 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
@@ -60,6 +60,7 @@ void function GamemodeAITdm_Init()
}
ScoreEvent_SetupEarnMeterValuesForMixedModes()
+ SetupGenericTDMChallenge()
}
// add settings
@@ -434,9 +435,12 @@ void function SquadHandler( array<entity> guys )
// Setup AI, first assault point
foreach ( guy in guys )
{
- guy.EnableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE | NPC_ALLOW_HAND_SIGNALS | NPC_ALLOW_FLEE )
- guy.AssaultPoint( point )
- guy.AssaultSetGoalRadius( 1600 ) // 1600 is minimum for npc_stalker, works fine for others
+ if ( IsAlive( guy ) )
+ {
+ guy.EnableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE | NPC_ALLOW_HAND_SIGNALS | NPC_ALLOW_FLEE )
+ guy.AssaultPoint( point )
+ guy.AssaultSetGoalRadius( 1600 ) // 1600 is minimum for npc_stalker, works fine for others
+ }
//thread AITdm_CleanupBoredNPCThread( guy )
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut
index 93a3aa168..9cf0021d7 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut
@@ -178,6 +178,12 @@ void function AT_PlayerTitleThink( entity player )
{
// Set player money count
player.SetTitle( "$" + string( AT_GetPlayerBonusPoints( player ) ) )
+
+ if( AT_GetPlayerBonusPoints( player ) >= 600 && !HasPlayerCompletedMeritScore( player ) ) //Challenge is: "Earn $600."
+ {
+ AddPlayerScore( player, "ChallengeATAssault" )
+ SetPlayerChallengeMeritScore( player )
+ }
}
else if ( GetGameState() >= eGameState.WinnerDetermined )
{
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut
index c5765300f..d8b0c9bdd 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut
@@ -29,6 +29,8 @@ struct {
array<HardpointStruct> hardpoints
array<CP_PlayerStruct> players
+ table<entity,int> playerAssaultPoints
+ table<entity,int> playerDefensePoints
} file
void function GamemodeCP_Init()
@@ -112,11 +114,13 @@ void function GamemodeCP_OnPlayerKilled(entity victim, entity attacker, var dama
{
AddPlayerScore( attacker , "HardpointDefense", victim )
attacker.AddToPlayerGameStat(PGS_DEFENSE_SCORE,POINTVALUE_HARDPOINT_DEFENSE)
+ UpdatePlayerScoreForChallenge(attacker,0,POINTVALUE_HARDPOINT_DEFENSE)
}
else if((victimCP.hardpoint.GetTeam()==victim.GetTeam())||(GetHardpointCappingTeam(victimCP)==victim.GetTeam()))
{
AddPlayerScore( attacker, "HardpointAssault", victim )
attacker.AddToPlayerGameStat(PGS_ASSAULT_SCORE,POINTVALUE_HARDPOINT_ASSAULT)
+ UpdatePlayerScoreForChallenge(attacker,POINTVALUE_HARDPOINT_ASSAULT,0)
}
}
}
@@ -127,10 +131,12 @@ void function GamemodeCP_OnPlayerKilled(entity victim, entity attacker, var dama
{
AddPlayerScore( attacker , "HardpointSnipe", victim )
attacker.AddToPlayerGameStat(PGS_ASSAULT_SCORE,POINTVALUE_HARDPOINT_SNIPE)
+ UpdatePlayerScoreForChallenge(attacker,POINTVALUE_HARDPOINT_SNIPE,0)
}
else{
AddPlayerScore( attacker , "HardpointSiege", victim )
attacker.AddToPlayerGameStat(PGS_ASSAULT_SCORE,POINTVALUE_HARDPOINT_SIEGE)
+ UpdatePlayerScoreForChallenge(attacker,POINTVALUE_HARDPOINT_SIEGE,0)
}
}
else if(attackerCP.hardpoint!=null)//Perimeter Defense
@@ -138,6 +144,7 @@ void function GamemodeCP_OnPlayerKilled(entity victim, entity attacker, var dama
if(attackerCP.hardpoint.GetTeam()==attacker.GetTeam())
AddPlayerScore( attacker , "HardpointPerimeterDefense", victim)
attacker.AddToPlayerGameStat(PGS_DEFENSE_SCORE,POINTVALUE_HARDPOINT_PERIMETER_DEFENSE)
+ UpdatePlayerScoreForChallenge(attacker,0,POINTVALUE_HARDPOINT_PERIMETER_DEFENSE)
}
foreach(CP_PlayerStruct player in file.players) //Reset Victim Holdtime Counter
@@ -308,6 +315,7 @@ void function CapturePointForTeam(HardpointStruct hardpoint, int Team)
if(player.IsPlayer()){
AddPlayerScore(player,"ControlPointCapture")
player.AddToPlayerGameStat(PGS_ASSAULT_SCORE,POINTVALUE_HARDPOINT_CAPTURE)
+ UpdatePlayerScoreForChallenge(player,POINTVALUE_HARDPOINT_CAPTURE,0)
}
}
}
@@ -319,12 +327,17 @@ void function GamemodeCP_InitPlayer(entity player)
playerStruct.timeOnPoints = [0.0,0.0,0.0]
playerStruct.isOnHardpoint = false
file.players.append(playerStruct)
+ file.playerAssaultPoints[player] <- 0
+ file.playerDefensePoints[player] <- 0
thread PlayerThink(playerStruct)
}
void function GamemodeCP_RemovePlayer(entity player)
{
-
+ if(player in file.playerAssaultPoints)
+ delete file.playerAssaultPoints[player]
+ if(player in file.playerDefensePoints)
+ delete file.playerDefensePoints[player]
foreach(index,CP_PlayerStruct playerStruct in file.players)
{
if(playerStruct.player==player)
@@ -376,11 +389,13 @@ void function PlayerThink(CP_PlayerStruct player)
{
AddPlayerScore(player.player,"ControlPointAmpedHold")
player.player.AddToPlayerGameStat( PGS_DEFENSE_SCORE, POINTVALUE_HARDPOINT_AMPED_HOLD )
+ UpdatePlayerScoreForChallenge(player.player,0,POINTVALUE_HARDPOINT_AMPED_HOLD)
}
else
{
AddPlayerScore(player.player,"ControlPointHold")
player.player.AddToPlayerGameStat( PGS_DEFENSE_SCORE, POINTVALUE_HARDPOINT_HOLD )
+ UpdatePlayerScoreForChallenge(player.player,0,POINTVALUE_HARDPOINT_HOLD)
}
}
break
@@ -574,6 +589,7 @@ void function HardpointThink( HardpointStruct hardpoint )
{
AddPlayerScore(player,"ControlPointAmped")
player.AddToPlayerGameStat(PGS_DEFENSE_SCORE,POINTVALUE_HARDPOINT_AMPED)
+ UpdatePlayerScoreForChallenge(player,0,POINTVALUE_HARDPOINT_AMPED)
}
}
}
@@ -714,3 +730,26 @@ string function GetHardpointGroup(entity hardpoint) //Hardpoint Entity B on Home
return string(hardpoint.kv.hardpointGroup)
}
+
+void function UpdatePlayerScoreForChallenge(entity player,int assaultpoints = 0,int defensepoints = 0)
+{
+ if(player in file.playerAssaultPoints)
+ {
+ file.playerAssaultPoints[player] += assaultpoints
+ if( file.playerAssaultPoints[player] >= 1000 && !HasPlayerCompletedMeritScore(player) )
+ {
+ AddPlayerScore(player,"ChallengeCPAssault")
+ SetPlayerChallengeMeritScore(player)
+ }
+ }
+
+ if(player in file.playerDefensePoints)
+ {
+ file.playerDefensePoints[player] += defensepoints
+ if( file.playerDefensePoints[player] >= 500 && !HasPlayerCompletedMeritScore(player) )
+ {
+ AddPlayerScore(player,"ChallengeCPDefense")
+ SetPlayerChallengeMeritScore(player)
+ }
+ }
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
index 9b05c3d42..85b80d744 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
@@ -1,5 +1,4 @@
untyped
-// this needs a refactor lol
global function CaptureTheFlag_Init
global function RateSpawnpoints_CTF
@@ -12,16 +11,31 @@ const array<string> SWAP_FLAG_MAPS = [
struct {
entity imcFlagSpawn
entity imcFlag
- entity imcFlagReturnTrigger
entity militiaFlagSpawn
entity militiaFlag
- entity militiaFlagReturnTrigger
array<entity> imcCaptureAssistList
array<entity> militiaCaptureAssistList
} file
+
+
+
+
+
+
+
+
+
+/*
+ ██████ █████ ██████ ████████ ██ ██ ██████ ███████ ████████ ██ ██ ███████ ███████ ██ █████ ██████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██ ███████ ██████ ██ ██ ██ ██████ █████ ██ ███████ █████ █████ ██ ███████ ██ ███
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ██████ ██ ██ ██ ██ ██████ ██ ██ ███████ ██ ██ ██ ███████ ██ ███████ ██ ██ ██████
+*/
+
void function CaptureTheFlag_Init()
{
PrecacheModel( CTF_FLAG_MODEL )
@@ -30,30 +44,31 @@ void function CaptureTheFlag_Init()
PrecacheParticleSystem( FLAG_FX_ENEMY )
CaptureTheFlagShared_Init()
+
SetSwitchSidesBased( true )
SetSuddenDeathBased( true )
+
SetShouldUseRoundWinningKillReplay( true )
- SetRoundWinningKillReplayKillClasses( false, false ) // make these fully manual
+ SetRoundWinningKillReplayKillClasses( false, false )
AddCallback_OnClientConnected( CTFInitPlayer )
-
+ AddCallback_OnClientDisconnected( CTFPlayerDisconnected )
+
AddCallback_GameStateEnter( eGameState.Prematch, CreateFlags )
AddCallback_GameStateEnter( eGameState.Epilogue, RemoveFlags )
+ AddCallback_GameStateEnter( eGameState.Playing, OnPlaying )
+
AddCallback_OnTouchHealthKit( "item_flag", OnFlagCollected )
+
AddCallback_OnPlayerKilled( OnPlayerKilled )
AddCallback_OnPilotBecomesTitan( DropFlagForBecomingTitan )
- SetSpawnZoneRatingFunc( DecideSpawnZone_CTF )
AddSpawnpointValidationRule( VerifyCTFSpawnpoint )
- RegisterSignal( "FlagReturnEnded" )
RegisterSignal( "ResetDropTimeout" )
- // setup stuff for the functions in sh_gamemode_ctf
- // don't really like using level for stuff but just how it be
level.teamFlags <- {}
- // setup score event earnmeter values
ScoreEvent_SetEarnMeterValues( "KillPilot", 0.05, 0.20 )
ScoreEvent_SetEarnMeterValues( "Headshot", 0.0, 0.02 )
ScoreEvent_SetEarnMeterValues( "FirstStrike", 0.0, 0.05 )
@@ -67,61 +82,21 @@ void function CaptureTheFlag_Init()
ScoreEvent_SetEarnMeterValues( "FlagReturn", 0.0, 0.20 )
}
-void function RateSpawnpoints_CTF( int checkClass, array<entity> spawnpoints, int team, entity player )
-{
- RateSpawnpoints_SpawnZones( checkClass, spawnpoints, team, player )
-}
-bool function VerifyCTFSpawnpoint( entity spawnpoint, int team )
-{
- // ensure spawnpoints aren't too close to enemy base
-
- if ( HasSwitchedSides() )
- team = GetOtherTeam( team )
-
- array<entity> startSpawns = SpawnPoints_GetPilotStart( team )
- array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( team ) )
-
- vector averageFriendlySpawns
- vector averageEnemySpawns
-
- foreach ( entity spawn in startSpawns )
- averageFriendlySpawns += spawn.GetOrigin()
-
- averageFriendlySpawns /= startSpawns.len()
-
- foreach ( entity spawn in enemyStartSpawns )
- averageEnemySpawns += spawn.GetOrigin()
-
- averageEnemySpawns /= startSpawns.len()
-
- return Distance2D( spawnpoint.GetOrigin(), averageEnemySpawns ) / Distance2D( averageFriendlySpawns, averageEnemySpawns ) > 0.35
-}
-void function CTFInitPlayer( entity player )
-{
- if ( !IsValid( file.imcFlagSpawn ) )
- return
-
- vector imcSpawn = file.imcFlagSpawn.GetOrigin()
- Remote_CallFunction_NonReplay( player, "ServerCallback_SetFlagHomeOrigin", TEAM_IMC, imcSpawn.x, imcSpawn.y, imcSpawn.z )
-
- vector militiaSpawn = file.militiaFlagSpawn.GetOrigin()
- Remote_CallFunction_NonReplay( player, "ServerCallback_SetFlagHomeOrigin", TEAM_MILITIA, militiaSpawn.x, militiaSpawn.y, militiaSpawn.z )
-}
-void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
-{
- if ( !IsValid( GetFlagForTeam( GetOtherTeam( victim.GetTeam() ) ) ) ) // getting a crash idk
- return
- if ( GetFlagForTeam( GetOtherTeam( victim.GetTeam() ) ).GetParent() == victim )
- {
- if ( victim != attacker && attacker.IsPlayer() )
- AddPlayerScore( attacker, "FlagCarrierKill", victim )
-
- DropFlag( victim )
- }
-}
+
+
+
+
+
+/*
+███████ ██████ █████ ██ ██ ███ ██ ██ ██████ ██████ ██ ██████
+██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██
+███████ ██████ ███████ ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██
+ ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+███████ ██ ██ ██ ███ ███ ██ ████ ███████ ██████ ██████ ██ ██████
+*/
void function CreateFlags()
{
@@ -129,25 +104,20 @@ void function CreateFlags()
{
file.imcFlagSpawn.Destroy()
file.imcFlag.Destroy()
- file.imcFlagReturnTrigger.Destroy()
-
+ }
+ if ( IsValid( file.militiaFlagSpawn ) )
+ {
file.militiaFlagSpawn.Destroy()
file.militiaFlag.Destroy()
- file.militiaFlagReturnTrigger.Destroy()
}
foreach ( entity spawn in GetEntArrayByClass_Expensive( "info_spawnpoint_flag" ) )
{
- // on some maps flags are on the opposite side from what they should be
- // likely this is because respawn uses distance checks from spawns to check this in official
- // but i don't like doing that so just using a list of maps to swap them on lol
bool switchedSides = HasSwitchedSides() == 1
-
- // i dont know why this works and whatever we had before didn't, but yeah
+
bool shouldSwap = switchedSides
- if (!shouldSwap && SWAP_FLAG_MAPS.contains( GetMapName() ))
+ if ( !shouldSwap && SWAP_FLAG_MAPS.contains( GetMapName() ) )
shouldSwap = !shouldSwap
-
int flagTeam = spawn.GetTeam()
if ( shouldSwap )
@@ -155,48 +125,33 @@ void function CreateFlags()
flagTeam = GetOtherTeam( flagTeam )
SetTeam( spawn, flagTeam )
}
-
- // create flag base
+
entity base = CreatePropDynamic( CTF_FLAG_BASE_MODEL, spawn.GetOrigin(), spawn.GetAngles(), 0 )
SetTeam( base, spawn.GetTeam() )
svGlobal.flagSpawnPoints[ flagTeam ] = base
- // create flag
entity flag = CreateEntity( "item_flag" )
flag.SetValueForModelKey( CTF_FLAG_MODEL )
SetTeam( flag, flagTeam )
flag.MarkAsNonMovingAttachment()
- flag.Minimap_AlwaysShow( TEAM_IMC, null ) // show flag icon on minimap
+ flag.Minimap_AlwaysShow( TEAM_IMC, null )
flag.Minimap_AlwaysShow( TEAM_MILITIA, null )
flag.Minimap_SetAlignUpright( true )
DispatchSpawn( flag )
flag.SetModel( CTF_FLAG_MODEL )
- flag.SetOrigin( spawn.GetOrigin() + < 0, 0, base.GetBoundingMaxs().z * 2 > ) // ensure flag doesn't spawn clipped into geometry
+ flag.SetOrigin( spawn.GetOrigin() + < 0, 0, base.GetBoundingMaxs().z * 2 > )
flag.SetVelocity( < 0, 0, 1 > )
flag.s.canTake <- true
- flag.s.playersReturning <- []
level.teamFlags[ flag.GetTeam() ] <- flag
-
- entity returnTrigger = CreateEntity( "trigger_cylinder" )
- SetTeam( returnTrigger, flagTeam )
- returnTrigger.SetRadius( CTF_GetFlagReturnRadius() )
- returnTrigger.SetAboveHeight( CTF_GetFlagReturnRadius() )
- returnTrigger.SetBelowHeight( CTF_GetFlagReturnRadius() )
-
- returnTrigger.SetEnterCallback( OnPlayerEntersFlagReturnTrigger )
- returnTrigger.SetLeaveCallback( OnPlayerExitsFlagReturnTrigger )
- DispatchSpawn( returnTrigger )
-
- thread TrackFlagReturnTrigger( flag, returnTrigger )
+ thread FlagProximityTracker( flag )
if ( flagTeam == TEAM_IMC )
{
file.imcFlagSpawn = base
file.imcFlag = flag
- file.imcFlagReturnTrigger = returnTrigger
SetGlobalNetEnt( "imcFlag", file.imcFlag )
SetGlobalNetEnt( "imcFlagHome", file.imcFlagSpawn )
@@ -205,76 +160,185 @@ void function CreateFlags()
{
file.militiaFlagSpawn = base
file.militiaFlag = flag
- file.militiaFlagReturnTrigger = returnTrigger
SetGlobalNetEnt( "milFlag", file.militiaFlag )
SetGlobalNetEnt( "milFlagHome", file.militiaFlagSpawn )
}
}
- // reset the flag states, prevents issues where flag is home but doesnt think it's home when halftime goes
SetFlagStateForTeam( TEAM_MILITIA, eFlagState.None )
SetFlagStateForTeam( TEAM_IMC, eFlagState.None )
-
- foreach ( entity player in GetPlayerArray() )
- CTFInitPlayer( player )
}
void function RemoveFlags()
{
- // destroy all the flag related things
if ( IsValid( file.imcFlagSpawn ) )
{
+ PlayFX( $"P_phase_shift_main", file.imcFlagSpawn.GetOrigin() )
file.imcFlagSpawn.Destroy()
file.imcFlag.Destroy()
- file.imcFlagReturnTrigger.Destroy()
}
+
if ( IsValid( file.militiaFlagSpawn ) )
{
+ PlayFX( $"P_phase_shift_main", file.militiaFlagSpawn.GetOrigin() )
file.militiaFlagSpawn.Destroy()
file.militiaFlag.Destroy()
- file.militiaFlagReturnTrigger.Destroy()
}
-
- // unsure if this is needed, since the flags are destroyed? idk
+
SetFlagStateForTeam( TEAM_MILITIA, eFlagState.None )
SetFlagStateForTeam( TEAM_IMC, eFlagState.None )
}
-void function TrackFlagReturnTrigger( entity flag, entity returnTrigger )
+void function RateSpawnpoints_CTF( int checkClass, array<entity> spawnpoints, int team, entity player )
{
- // this is a bit of a hack, it seems parenting the return trigger to the flag actually sets the pickup radius of the flag to be the same as the trigger
- // this isn't wanted since only pickups should use that additional radius
- flag.EndSignal( "OnDestroy" )
+ vector allyFlagSpot
+ vector enemyFlagSpot
+ foreach ( entity spawn in GetEntArrayByClass_Expensive( "info_spawnpoint_flag" ) )
+ {
+ if( spawn.GetTeam() == team )
+ allyFlagSpot = spawn.GetOrigin()
+ else
+ enemyFlagSpot = spawn.GetOrigin()
+ }
+
+ foreach ( entity spawn in spawnpoints )
+ {
+ float rating = 0.0
+ float allyFlagDistance = Distance2D( spawn.GetOrigin(), allyFlagSpot )
+ float enemyFlagDistance = Distance2D( spawn.GetOrigin(), enemyFlagSpot )
+
+ if( enemyFlagDistance > allyFlagDistance )
+ {
+ rating += spawn.NearbyAllyScore( team, "ai" )
+ rating += spawn.NearbyAllyScore( team, "titan" )
+ rating += spawn.NearbyAllyScore( team, "pilot" )
+
+ rating += spawn.NearbyEnemyScore( team, "ai" )
+ rating += spawn.NearbyEnemyScore( team, "titan" )
+ rating += spawn.NearbyEnemyScore( team, "pilot" )
+
+ rating = rating / allyFlagDistance
+ }
+
+ if ( spawn == player.p.lastSpawnPoint )
+ rating += GetConVarFloat( "spawnpoint_last_spawn_rating" )
- while ( true )
+ spawn.CalculateRating( checkClass, team, rating, rating * 0.25 )
+ }
+}
+
+bool function VerifyCTFSpawnpoint( entity spawnpoint, int team )
+{
+ vector allyFlagSpot
+ vector enemyFlagSpot
+ foreach ( entity spawn in GetEntArrayByClass_Expensive( "info_spawnpoint_flag" ) )
{
- returnTrigger.SetOrigin( flag.GetOrigin() )
- WaitFrame()
+ if( spawn.GetTeam() == team )
+ allyFlagSpot = spawn.GetOrigin()
+ else
+ enemyFlagSpot = spawn.GetOrigin()
}
+
+ if( Distance2D( spawnpoint.GetOrigin(), allyFlagSpot ) > Distance2D( spawnpoint.GetOrigin(), enemyFlagSpot ) )
+ return false
+
+ return true
}
-void function SetFlagStateForTeam( int team, int state )
+
+
+
+
+
+
+
+
+/*
+██████ ██ █████ ██ ██ ███████ ██████ ██ ██████ ██████ ██ ██████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██████ ██ ███████ ████ █████ ██████ ██ ██ ██ ██ ███ ██ ██
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██ ███████ ██ ██ ██ ███████ ██ ██ ███████ ██████ ██████ ██ ██████
+*/
+
+void function OnPlaying()
{
- if ( state == eFlagState.Away ) // we tell the client the flag is the player carrying it if they're carrying it
- SetGlobalNetEnt( team == TEAM_IMC ? "imcFlag" : "milFlag", ( team == TEAM_IMC ? file.imcFlag : file.militiaFlag ).GetParent() )
- else
- SetGlobalNetEnt( team == TEAM_IMC ? "imcFlag" : "milFlag", team == TEAM_IMC ? file.imcFlag : file.militiaFlag )
+ foreach ( entity player in GetPlayerArray() )
+ CTFInitPlayer( player )
+}
- SetGlobalNetInt( team == TEAM_IMC ? "imcFlagState" : "milFlagState", state )
+void function CTFInitPlayer( entity player )
+{
+ if ( !GamePlaying() )
+ return
+
+ if ( IsValid( file.imcFlagSpawn ) )
+ {
+ vector imcSpawn = file.imcFlagSpawn.GetOrigin()
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SetFlagHomeOrigin", TEAM_IMC, imcSpawn.x, imcSpawn.y, imcSpawn.z )
+ }
+
+ if ( IsValid( file.militiaFlagSpawn ) )
+ {
+ vector militiaSpawn = file.militiaFlagSpawn.GetOrigin()
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SetFlagHomeOrigin", TEAM_MILITIA, militiaSpawn.x, militiaSpawn.y, militiaSpawn.z )
+ }
+}
+
+void function CTFPlayerDisconnected( entity player )
+{
+ // This has no validity checks on the player because the disconnection callback happens in the exact last frame the player entity still exists
+ if( !GamePlaying() )
+ return
+
+ if ( PlayerHasEnemyFlag( player ) )
+ DropFlag( player, false )
}
+void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( !IsValid( GetFlagForTeam( GetOtherTeam( victim.GetTeam() ) ) ) ) // getting a crash idk
+ return
+
+ if ( PlayerHasEnemyFlag( victim ) )
+ {
+ if ( victim != attacker && attacker.IsPlayer() )
+ AddPlayerScore( attacker, "FlagCarrierKill", victim )
+
+ DropFlag( victim )
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+/*
+███████ ██ █████ ██████ ██ ██████ ██████ ██ ██████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+█████ ██ ███████ ██ ███ ██ ██ ██ ██ ███ ██ ██
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██ ███████ ██ ██ ██████ ███████ ██████ ██████ ██ ██████
+*/
+
bool function OnFlagCollected( entity player, entity flag )
{
if ( !IsAlive( player ) || flag.GetParent() != null || player.IsTitan() || player.IsPhaseShifted() )
return false
if ( player.GetTeam() != flag.GetTeam() && flag.s.canTake )
- GiveFlag( player, flag ) // pickup enemy flag
+ GiveFlag( player, flag )
else if ( player.GetTeam() == flag.GetTeam() && IsFlagHome( flag ) && PlayerHasEnemyFlag( player ) )
- CaptureFlag( player, GetFlagForTeam( GetOtherTeam( flag.GetTeam() ) ) ) // cap the flag
+ CaptureFlag( player, GetFlagForTeam( GetOtherTeam( flag.GetTeam() ) ) )
- return false // don't wanna delete the flag entity
+ return false // Don't delete the flag
}
void function GiveFlag( entity player, entity flag )
@@ -283,7 +347,8 @@ void function GiveFlag( entity player, entity flag )
flag.Signal( "ResetDropTimeout" )
flag.SetParent( player, "FLAG" )
- thread DropFlagIfPhased( player, flag )
+ if ( GetCurrentPlaylistVarInt( "phase_shift_drop_flag", 0 ) == 1 )
+ thread DropFlagIfPhased( player, flag )
// do notifications
MessageToPlayer( player, eEventNotifications.YouHaveTheEnemyFlag )
@@ -301,103 +366,10 @@ void function GiveFlag( entity player, entity flag )
SetFlagStateForTeam( flag.GetTeam(), eFlagState.Away ) // used for held
}
-void function DropFlagIfPhased( entity player, entity flag )
-{
- player.EndSignal( "StartPhaseShift" )
- player.EndSignal( "OnDestroy" )
-
- OnThreadEnd( function() : ( player )
- {
- if (GetGameState() == eGameState.Playing || GetGameState() == eGameState.SuddenDeath)
- DropFlag( player, true )
- })
- // the IsValid check is purely to prevent a crash due to a destroyed flag (epilogue)
- while( IsValid(flag) && flag.GetParent() == player )
- WaitFrame()
-}
-
-void function DropFlagForBecomingTitan( entity pilot, entity titan )
-{
- DropFlag( pilot, true )
-}
-
-void function DropFlag( entity player, bool realDrop = true )
-{
- entity flag = GetFlagForTeam( GetOtherTeam( player.GetTeam() ) )
-
- if ( flag.GetParent() != player )
- return
-
- print( player + " dropped the flag!" )
-
- flag.ClearParent()
- flag.SetAngles( < 0, 0, 0 > )
- flag.SetVelocity( < 0, 0, 0 > )
-
- if ( realDrop )
- {
- // start drop timeout countdown
- thread TrackFlagDropTimeout( flag )
-
- // add to capture assists
- if ( player.GetTeam() == TEAM_IMC )
- file.imcCaptureAssistList.append( player )
- else
- file.militiaCaptureAssistList.append( player )
-
- // do notifications
- MessageToPlayer( player, eEventNotifications.YouDroppedTheEnemyFlag )
- EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_FlagDrop" )
-
- MessageToTeam( player.GetTeam(), eEventNotifications.PlayerDroppedEnemyFlag, player, player )
- // todo need a sound here maybe
- MessageToTeam( GetOtherTeam( player.GetTeam() ), eEventNotifications.PlayerDroppedFriendlyFlag, player, player )
- // todo need a sound here maybe
- }
-
- SetFlagStateForTeam( flag.GetTeam(), eFlagState.Home ) // used for return prompt
-}
-
-void function TrackFlagDropTimeout( entity flag )
-{
- flag.EndSignal( "ResetDropTimeout" )
-
- wait CTF_GetDropTimeout()
-
- ResetFlag( flag )
-}
-
-void function ResetFlag( entity flag )
-{
- // prevents crash when flag is reset after it's been destroyed due to epilogue
- if (!IsValid(flag))
- return
- // ensure we can't pickup the flag after it's been dropped but before it's been reset
- flag.s.canTake = false
-
- if ( flag.GetParent() != null )
- DropFlag( flag.GetParent(), false )
-
- entity spawn
- if ( flag.GetTeam() == TEAM_IMC )
- spawn = file.imcFlagSpawn
- else
- spawn = file.militiaFlagSpawn
-
- flag.SetOrigin( spawn.GetOrigin() + < 0, 0, spawn.GetBoundingMaxs().z + 1 > )
-
- // we can take it again now
- flag.s.canTake = true
-
- SetFlagStateForTeam( flag.GetTeam(), eFlagState.None ) // used for home
-
- flag.Signal( "ResetDropTimeout" )
-}
-
void function CaptureFlag( entity player, entity flag )
{
// can only capture flags during normal play or sudden death
- if (GetGameState() != eGameState.Playing && GetGameState() != eGameState.SuddenDeath)
+ if ( GetGameState() != eGameState.Playing && GetGameState() != eGameState.SuddenDeath )
{
printt( player + " tried to capture the flag, but the game state was " + GetGameState() + " not " + eGameState.Playing + " or " + eGameState.SuddenDeath)
return
@@ -421,8 +393,18 @@ void function CaptureFlag( entity player, entity flag )
assistList = file.militiaCaptureAssistList
foreach( entity assistPlayer in assistList )
- if ( player != assistPlayer )
- AddPlayerScore( assistPlayer, "FlagCaptureAssist", player )
+ {
+ if ( IsValidPlayer( assistPlayer ) )
+ {
+ if ( player != assistPlayer )
+ AddPlayerScore( assistPlayer, "FlagCaptureAssist", player )
+ if( !HasPlayerCompletedMeritScore( assistPlayer ) )
+ {
+ AddPlayerScore( assistPlayer, "ChallengeCTFCapAssist" )
+ SetPlayerChallengeMeritScore( assistPlayer )
+ }
+ }
+ }
assistList.clear()
@@ -430,84 +412,207 @@ void function CaptureFlag( entity player, entity flag )
MessageToPlayer( player, eEventNotifications.YouCapturedTheEnemyFlag )
EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_PlayerScore" )
+ if( !HasPlayerCompletedMeritScore( player ) )
+ SetPlayerChallengeMeritScore( player )
+
MessageToTeam( team, eEventNotifications.PlayerCapturedEnemyFlag, player, player )
EmitSoundOnEntityToTeamExceptPlayer( flag, "UI_CTF_3P_TeamScore", player.GetTeam(), player )
MessageToTeam( GetOtherTeam( team ), eEventNotifications.PlayerCapturedFriendlyFlag, player, player )
EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyScores", flag.GetTeam() )
- if ( GameRules_GetTeamScore( team ) == GameMode_GetRoundScoreLimit( GAMETYPE ) - 1 )
+ if ( GameRules_GetTeamScore( team ) == GetScoreLimit_FromPlaylist() - 1 )
{
PlayFactionDialogueToTeam( "ctf_notifyWin1more", team )
PlayFactionDialogueToTeam( "ctf_notifyLose1more", GetOtherTeam( team ) )
+ foreach( entity otherPlayer in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_CTF_PlayMatchNearEndMusic" )
}
}
-void function OnPlayerEntersFlagReturnTrigger( entity trigger, entity player )
+void function DropFlag( entity player, bool realDrop = true )
{
- entity flag
- if ( trigger.GetTeam() == TEAM_IMC )
- flag = file.imcFlag
- else
- flag = file.militiaFlag
-
- if( !IsValid( flag ) || !IsValid( player ) )
- return
+ entity flag = GetFlagForTeam( GetOtherTeam( player.GetTeam() ) )
- if ( !player.IsPlayer() || player.IsTitan() || player.GetTeam() != flag.GetTeam() || IsFlagHome( flag ) || flag.GetParent() != null )
+ if( !IsValid( flag ) || flag.GetParent() != player )
return
- thread TryReturnFlag( player, flag )
+ print( player + " dropped the flag!" )
+
+ flag.ClearParent()
+ flag.SetAngles( < 0, 0, 0 > )
+ flag.SetVelocity( < 0, 0, 0 > )
+
+ if ( realDrop )
+ {
+ if ( player.GetTeam() == TEAM_IMC && !file.imcCaptureAssistList.contains( player ) )
+ file.imcCaptureAssistList.append( player )
+
+ else if( !file.militiaCaptureAssistList.contains( player ) )
+ file.militiaCaptureAssistList.append( player )
+
+ MessageToPlayer( player, eEventNotifications.YouDroppedTheEnemyFlag )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_FlagDrop" )
+
+ MessageToTeam( player.GetTeam(), eEventNotifications.PlayerDroppedEnemyFlag, player, player )
+ MessageToTeam( GetOtherTeam( player.GetTeam() ), eEventNotifications.PlayerDroppedFriendlyFlag, player, player )
+ }
+
+ thread TrackFlagDropTimeout( flag )
+ SetFlagStateForTeam( flag.GetTeam(), eFlagState.Home )
}
-void function OnPlayerExitsFlagReturnTrigger( entity trigger, entity player )
+void function ResetFlag( entity flag )
{
- entity flag
- if ( trigger.GetTeam() == TEAM_IMC )
- flag = file.imcFlag
+ flag.s.canTake = false
+
+ if ( flag.GetParent() != null )
+ DropFlag( flag.GetParent(), false )
+
+ entity flagBase
+ if ( flag.GetTeam() == TEAM_IMC )
+ flagBase = file.imcFlagSpawn
else
- flag = file.militiaFlag
+ flagBase = file.militiaFlagSpawn
- if ( !player.IsPlayer() || player.IsTitan() || player.GetTeam() != flag.GetTeam() || IsFlagHome( flag ) || flag.GetParent() != null )
- return
+ flag.SetOrigin( flagBase.GetOrigin() + < 0, 0, flagBase.GetBoundingMaxs().z + 1 > )
+
+ flag.s.canTake = true
- player.Signal( "FlagReturnEnded" )
-}
+ SetFlagStateForTeam( flag.GetTeam(), eFlagState.None )
+
+ flag.Signal( "ResetDropTimeout" )
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check proximity for flag returns
+// Input : flag - The flag entity
+//-----------------------------------------------------------------------------
+void function FlagProximityTracker( entity flag )
+{
+ flag.EndSignal( "OnDestroy" )
+
+ array < entity > playerInsidePerimeter
+ while( true )
+ {
+ if( !playerInsidePerimeter.len() )
+ ArrayRemoveDead( playerInsidePerimeter )
+
+ foreach ( player in GetPlayerArrayOfTeam_Alive( flag.GetTeam() ) )
+ {
+ if ( Distance( player.GetOrigin(), flag.GetOrigin() ) < CTF_GetFlagReturnRadius() )
+ {
+ if ( player.IsTitan() || player.GetTeam() != flag.GetTeam() || IsFlagHome( flag ) || flag.GetParent() != null )
+ continue
+
+ if( playerInsidePerimeter.contains( player ) )
+ continue
+
+ playerInsidePerimeter.append( player )
+ thread TryReturnFlag( player, flag )
+ }
+ else
+ {
+ if( playerInsidePerimeter.contains( player ) )
+ {
+ player.Signal( "CTF_LeftReturnTriggerArea" ) // Cut the progress if outside range
+ playerInsidePerimeter.removebyvalue( player )
+ }
+ }
+ }
+
+ WaitFrame()
+ }
+}
void function TryReturnFlag( entity player, entity flag )
{
- // start return progress bar
Remote_CallFunction_NonReplay( player, "ServerCallback_CTF_StartReturnFlagProgressBar", Time() + CTF_GetFlagReturnTime() )
EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_FlagReturnMeter" )
- OnThreadEnd( function() : ( player )
+ OnThreadEnd( function() : ( flag, player )
{
- // cleanup
- Remote_CallFunction_NonReplay( player, "ServerCallback_CTF_StopReturnFlagProgressBar" )
- StopSoundOnEntity( player, "UI_CTF_1P_FlagReturnMeter" )
+ if ( IsValidPlayer( player ) )
+ {
+ Remote_CallFunction_NonReplay( player, "ServerCallback_CTF_StopReturnFlagProgressBar" )
+ StopSoundOnEntity( player, "UI_CTF_1P_FlagReturnMeter" )
+ }
})
- player.EndSignal( "FlagReturnEnded" )
- flag.EndSignal( "FlagReturnEnded" ) // avoid multiple players to return one flag at once
+ flag.EndSignal( "CTF_ReturnedFlag" )
+ flag.EndSignal( "OnDestroy" )
+
+ player.EndSignal( "CTF_LeftReturnTriggerArea" )
player.EndSignal( "OnDeath" )
+ player.EndSignal( "OnDestroy" )
wait CTF_GetFlagReturnTime()
- // flag return succeeded
- // return flag
ResetFlag( flag )
- flag.Signal( "FlagReturnEnded" )
-
- // do notifications for return
- MessageToPlayer( player, eEventNotifications.YouReturnedFriendlyFlag )
- AddPlayerScore( player, "FlagReturn", player )
- player.AddToPlayerGameStat( PGS_DEFENSE_SCORE, 1 )
MessageToTeam( flag.GetTeam(), eEventNotifications.PlayerReturnedFriendlyFlag, null, player )
EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_TeamReturnsFlag", flag.GetTeam() )
PlayFactionDialogueToTeam( "ctf_flagReturnedFriendly", flag.GetTeam() )
+ MessageToPlayer( player, eEventNotifications.YouReturnedFriendlyFlag )
+ AddPlayerScore( player, "FlagReturn", player )
+ player.AddToPlayerGameStat( PGS_DEFENSE_SCORE, 1 )
+
+ if ( !HasPlayerCompletedMeritScore( player ) )
+ {
+ AddPlayerScore( player, "ChallengeCTFRetAssist" )
+ SetPlayerChallengeMeritScore( player )
+ }
+
MessageToTeam( GetOtherTeam( flag.GetTeam() ), eEventNotifications.PlayerReturnedEnemyFlag, null, player )
EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyReturnsFlag", GetOtherTeam( flag.GetTeam() ) )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_ReturnsFlag" )
PlayFactionDialogueToTeam( "ctf_flagReturnedEnemy", GetOtherTeam( flag.GetTeam() ) )
-} \ No newline at end of file
+
+ flag.Signal( "CTF_ReturnedFlag" )
+}
+
+void function SetFlagStateForTeam( int team, int state )
+{
+ if ( state == eFlagState.Away ) // we tell the client the flag is the player carrying it if they're carrying it
+ SetGlobalNetEnt( team == TEAM_IMC ? "imcFlag" : "milFlag", ( team == TEAM_IMC ? file.imcFlag : file.militiaFlag ).GetParent() )
+ else
+ SetGlobalNetEnt( team == TEAM_IMC ? "imcFlag" : "milFlag", team == TEAM_IMC ? file.imcFlag : file.militiaFlag )
+
+ SetGlobalNetInt( team == TEAM_IMC ? "imcFlagState" : "milFlagState", state )
+}
+
+void function DropFlagIfPhased( entity player, entity flag )
+{
+ player.EndSignal( "StartPhaseShift" )
+ player.EndSignal( "OnDestroy" )
+ flag.EndSignal( "OnDestroy" )
+
+ OnThreadEnd( function() : ( player )
+ {
+ if ( IsValidPlayer( player ) )
+ {
+ if ( GetGameState() == eGameState.Playing || GetGameState() == eGameState.SuddenDeath )
+ DropFlag( player, true )
+ }
+ })
+
+ while( flag.GetParent() == player )
+ WaitFrame()
+}
+
+void function DropFlagForBecomingTitan( entity pilot, entity titan )
+{
+ DropFlag( pilot, true )
+}
+
+void function TrackFlagDropTimeout( entity flag )
+{
+ flag.EndSignal( "CTF_ReturnedFlag" )
+ flag.EndSignal( "ResetDropTimeout" )
+ flag.EndSignal( "OnDestroy" )
+
+ wait CTF_GetDropTimeout()
+
+ ResetFlag( flag )
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut
index 9d8f84b5c..6d0fd3c7b 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut
@@ -17,6 +17,7 @@ void function GamemodeFRA_Init()
ScoreEvent_SetEarnMeterValues( "PilotBatteryPickup", 0.0, 0.34 )
EarnMeterMP_SetPassiveMeterGainEnabled( false )
PilotBattery_SetMaxCount( 3 )
+ SetupGenericFFAChallenge()
AddCallback_OnPlayerKilled( FRARemoveEarnMeter )
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut
index 31c85a573..8999231d3 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut
@@ -8,6 +8,8 @@ struct {
float lastDamageInfoTime
bool shouldDoHighlights
+
+ table< entity, int > pilotstreak
} file
void function GamemodeLts_Init()
@@ -34,6 +36,37 @@ void function GamemodeLts_Init()
ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() )
ClassicMP_ForceDisableEpilogue( true )
AddCallback_GameStateEnter( eGameState.Playing, WaitForThirtySecondsLeft )
+
+ AddCallback_OnClientConnected( SetupPlayerLTSChallenges ) //Just to make up the Match Goals tracking
+ AddCallback_OnClientDisconnected( RemovePlayerLTSChallenges ) //Safety removal of data to prevent crashes
+ AddCallback_OnPlayerKilled( LTSChallengeForPlayerKilled )
+}
+
+void function SetupPlayerLTSChallenges( entity player )
+{
+ file.pilotstreak[ player ] <- 0
+}
+
+void function RemovePlayerLTSChallenges( entity player )
+{
+ if( player in file.pilotstreak )
+ delete file.pilotstreak[ player ]
+}
+
+void function LTSChallengeForPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim == attacker || !attacker.IsPlayer() || GetGameState() != eGameState.Playing )
+ return
+
+ if ( victim.IsPlayer() && attacker in file.pilotstreak )
+ {
+ file.pilotstreak[attacker]++
+ if( file.pilotstreak[attacker] >= 2 && !HasPlayerCompletedMeritScore( attacker ) )
+ {
+ AddPlayerScore( attacker, "ChallengeLTS" )
+ SetPlayerChallengeMeritScore( attacker )
+ }
+ }
}
void function WaitForThirtySecondsLeft()
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut
index 659dbb7a3..768bbde11 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut
@@ -180,6 +180,12 @@ void function MarkPlayers( entity imcMark, entity militiaMark )
entity livingMark = GetMarked( GetOtherTeam( deadMark.GetTeam() ) )
livingMark.SetPlayerGameStat( PGS_DEFENSE_SCORE, livingMark.GetPlayerGameStat( PGS_DEFENSE_SCORE ) + 1 )
+ if( !HasPlayerCompletedMeritScore( livingMark ) )
+ {
+ AddPlayerScore( livingMark, "ChallengeMFD" )
+ SetPlayerChallengeMeritScore( livingMark )
+ }
+
// thread this so we don't kill our own thread
thread AddTeamScore( livingMark.GetTeam(), 1 )
}
@@ -188,10 +194,22 @@ void function UpdateMarksForKill( entity victim, entity attacker, var damageInfo
{
if ( victim == GetMarked( victim.GetTeam() ) )
{
- MessageToAll( eEventNotifications.MarkedForDeathKill, null, victim, attacker.GetEncodedEHandle() )
+ // handle suicides. Not sure what the actual message is that vanilla shows for this
+ // but this will prevent crashing for now
+ bool isSuicide = IsSuicide( victim, attacker, DamageInfo_GetDamageSourceIdentifier( damageInfo ) )
+ entity actualAttacker = isSuicide ? victim : attacker
+
+ MessageToAll( eEventNotifications.MarkedForDeathKill, null, victim, actualAttacker.GetEncodedEHandle() )
svGlobal.levelEnt.Signal( "MarkKilled", { mark = victim } )
- if ( attacker.IsPlayer() )
+ if ( !isSuicide && attacker.IsPlayer() )
+ {
attacker.SetPlayerGameStat( PGS_ASSAULT_SCORE, attacker.GetPlayerGameStat( PGS_ASSAULT_SCORE ) + 1 )
+ if( !HasPlayerCompletedMeritScore( attacker ) )
+ {
+ AddPlayerScore( attacker, "ChallengeMFD" )
+ SetPlayerChallengeMeritScore( attacker )
+ }
+ }
}
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut
index 57355ad8b..fb84cc82b 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut
@@ -19,12 +19,7 @@ void function GamemodePs_Init()
AddCallback_OnPlayerKilled( GiveScoreForPlayerKill )
ScoreEvent_SetupEarnMeterValuesForMixedModes()
SetTimeoutWinnerDecisionFunc( CheckScoreForDraw )
-
- // spawnzone stuff
- SetShouldCreateMinimapSpawnZones( true )
-
- //AddCallback_OnPlayerKilled( CheckSpawnzoneSuspiciousDeaths )
- //AddSpawnCallbackEditorClass( "trigger_multiple", "trigger_mp_spawn_zone", SpawnzoneTriggerInit )
+ SetupGenericFFAChallenge()
file.militiaPreviousSpawnZones = [ null, null, null ]
file.imcPreviousSpawnZones = [ null, null, null ]
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
index cb277b004..4617476eb 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
@@ -18,6 +18,7 @@ void function GamemodeSpeedball_Init()
Riff_ForceTitanAvailability( eTitanAvailability.Never )
Riff_ForceSetEliminationMode( eEliminationMode.Pilots )
ScoreEvent_SetupEarnMeterValuesForMixedModes()
+ SetupGenericFFAChallenge()
AddSpawnCallbackEditorClass( "script_ref", "info_speedball_flag", CreateFlag )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut
index 5c0e6feca..61ede2d44 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut
@@ -6,6 +6,7 @@ void function GamemodeTdm_Init()
AddCallback_OnPlayerKilled( GiveScoreForPlayerKill )
ScoreEvent_SetupEarnMeterValuesForMixedModes()
SetTimeoutWinnerDecisionFunc( CheckScoreForDraw )
+ SetupGenericTDMChallenge()
}
void function GiveScoreForPlayerKill( entity victim, entity attacker, var damageInfo )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut
index 6b30a3990..3ba843945 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut
@@ -2,6 +2,11 @@ global function GamemodeTTDM_Init
const float TTDMIntroLength = 15.0
+struct
+{
+ table< entity, int > challengeCount
+} file
+
void function GamemodeTTDM_Init()
{
Riff_ForceSetSpawnAsTitan( eSpawnAsTitan.Always )
@@ -14,6 +19,8 @@ void function GamemodeTTDM_Init()
ClassicMP_ForceDisableEpilogue( true )
SetTimeoutWinnerDecisionFunc( CheckScoreForDraw )
+ AddCallback_OnClientConnected( SetupPlayerTTDMChallenges ) //Just to make up the Match Goals tracking
+ AddCallback_OnClientDisconnected( RemovePlayerTTDMChallenges ) //Safety removal of data to prevent crashes
AddCallback_OnPlayerKilled( AddTeamScoreForPlayerKilled ) // dont have to track autotitan kills since you cant leave your titan in this mode
// probably needs scoreevent earnmeter values
@@ -56,6 +63,17 @@ void function TTDMIntroShowIntermissionCam( entity player )
thread PlayerWatchesTTDMIntroIntermissionCam( player )
}
+void function SetupPlayerTTDMChallenges( entity player )
+{
+ file.challengeCount[ player ] <- 0
+}
+
+void function RemovePlayerTTDMChallenges( entity player )
+{
+ if( player in file.challengeCount )
+ delete file.challengeCount[ player ]
+}
+
void function PlayerWatchesTTDMIntroIntermissionCam( entity player )
{
player.EndSignal( "OnDestroy" )
@@ -79,6 +97,19 @@ void function AddTeamScoreForPlayerKilled( entity victim, entity attacker, var d
if ( victim == attacker || !victim.IsPlayer() || !attacker.IsPlayer() && GetGameState() == eGameState.Playing )
return
+ if( victim in file.challengeCount )
+ file.challengeCount[victim] = 0
+
+ if( attacker in file.challengeCount )
+ {
+ file.challengeCount[attacker]++
+ if( file.challengeCount[attacker] >= 2 && !HasPlayerCompletedMeritScore( attacker ) )
+ {
+ AddPlayerScore( attacker, "ChallengeTTDM" )
+ SetPlayerChallengeMeritScore( attacker )
+ }
+ }
+
AddTeamScore( GetOtherTeam( victim.GetTeam() ), 1 )
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/item_inventory/sv_item_inventory.gnut b/Northstar.CustomServers/mod/scripts/vscripts/item_inventory/sv_item_inventory.gnut
index 60bad8d1f..4e0d65d41 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/item_inventory/sv_item_inventory.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/item_inventory/sv_item_inventory.gnut
@@ -32,7 +32,7 @@ void function PrematchClearInventory() // vanilla behavior
{
foreach( entity player in GetPlayerArray() )
{
- PlayerInventory_TakeAllInventoryItems( player )
+ thread PlayerInventory_TakeAllInventoryItems( player )
}
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
index 9288f75e3..b77a37b2a 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
@@ -43,6 +43,8 @@ void function BaseGametype_Init_MPSP()
AddCallback_OnPlayerKilled( CheckForAutoTitanDeath )
RegisterSignal( "PlayerRespawnStarted" )
RegisterSignal( "KillCamOver" )
+
+ FlagInit( "WeaponDropsAllowed", true )
}
void function SetIntermissionCamera( entity camera )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_challenges.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_challenges.gnut
index 466a50425..016097f20 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_challenges.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_challenges.gnut
@@ -1,6 +1,276 @@
global function InitChallenges
+global function SetPlayerChallengeEvacState //Hooked in _evac.gnut
+global function SetPlayerChallengeMatchWon //Hooked in _score.nut
+global function SetPlayerChallengeMatchComplete //Hooked in _score.nut
+global function SetPlayerChallengeMeritScore //Up to gamemodes to use this directly if needed
+global function IncrementPlayerChallengeTitanLeveledUp //Hooked in titan_xp.gnut
+global function IncrementPlayerChallengeWeaponLeveledUp //Hooked in weapon_xp.gnut
+global function IncrementPlayerChallengeFactionLeveledUp //Hooked in faction_xp.gnut (invisible but necessary for post-summary menu)
+global function RegisterChallenges_OnMatchEnd //Hooked in _gamestate_mp.gnut
+
+global function HasPlayerCompletedMeritScore //Check from gamemodes to not reapply SetPlayerChallengeMeritScore
+global function SetupGenericTDMChallenge //Used by gamemodes which simply adopts the: "Kill 3 Pilots without dying." Challenge
+global function SetupGenericFFAChallenge //Used by gamemodes which simply adopts the: "Kill 5 Pilots." Challenge
+
+struct
+{
+ table< entity, int > playerTotalMeritCount
+ table< entity, bool > playerChallenge
+ table< entity, int > pilotstreak
+ bool isHappyHourActive
+} file
+
+
+
+
+
+
+/*=============================================================================================================
+ __ __ _ _ ____ _ _ _
+ | \/ | __ _ | |_ ___ | |__ / ___|| |__ __ _ | || | ___ _ __ __ _ ___ ___
+ | |\/| | / _` || __|/ __|| '_ \ | | | '_ \ / _` || || | / _ \| '_ \ / _` | / _ \/ __|
+ | | | || (_| || |_| (__ | | | | | |___ | | | || (_| || || || __/| | | || (_| || __/\__ \
+ |_| |_| \__,_| \__|\___||_| |_| \____||_| |_| \__,_||_||_| \___||_| |_| \__, | \___||___/
+ |___/
+=============================================================================================================*/
void function InitChallenges()
{
+#if (UI && CLIENT)
+
+ SCB_SetCompleteMeritState( 4 )
+ SCB_SetEvacMeritState( 4 )
+ SCB_SetMeritCount( 4 )
+ SCB_SetScoreMeritState( 4 )
+ SCB_SetWinMeritState( 4 )
+ SCB_SetWeaponMeritCount( -1 )
+ SCB_SetTitanMeritCount( -1 )
+
+#elseif (SERVER && MP)
+
+ AddCallback_OnClientConnected( SetupPlayerMenuChallenges )
+ AddCallback_OnClientDisconnected( RemovePlayerFromChallengePool )
+
+#endif
+}
+
+void function SetupPlayerMenuChallenges( entity player )
+{
+ file.playerTotalMeritCount[ player ] <- 0
+ file.pilotstreak[ player ] <- 0
+ file.playerChallenge[ player ] <- false
+
+ thread SetupChallenges_Threaded( player )
+}
+void function SetupChallenges_Threaded( entity player )
+{
+ player.EndSignal( "OnDestroy" )
+
+ WaitFrame()
+
+ Remote_CallFunction_UI( player, "SCB_SetCompleteMeritState", 0 )
+ Remote_CallFunction_UI( player, "SCB_SetEvacMeritState", 4 ) //4 tells RUI to hide it
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", 0 )
+ Remote_CallFunction_UI( player, "SCB_SetScoreMeritState", 0 )
+ Remote_CallFunction_UI( player, "SCB_SetWinMeritState", 0 )
+ Remote_CallFunction_UI( player, "SCB_SetWeaponMeritCount", 0 )
+ Remote_CallFunction_UI( player, "SCB_SetTitanMeritCount", 0 )
+}
+
+void function SetupGenericTDMChallenge()
+{
+ AddCallback_OnPlayerKilled( TDMChallenges_OnPlayerKilled )
+}
+
+void function SetupGenericFFAChallenge()
+{
+ AddCallback_OnPlayerKilled( FFAChallenges_OnPlayerKilled )
+}
+
+void function RemovePlayerFromChallengePool( entity player )
+{
+ if( player in file.playerChallenge )
+ delete file.playerChallenge[ player ]
+ if( player in file.playerTotalMeritCount )
+ delete file.playerTotalMeritCount[ player ]
+ if( player in file.pilotstreak )
+ delete file.pilotstreak[ player ]
+}
+
+void function RegisterChallenges_OnMatchEnd()
+{
+ bool eliteWarpaintRNG = false
+
+ if( RandomIntRange( 0, 100 ) <= 30 ) //30% Chance to trigger akin to vanilla, apply always since all players have paid cosmetics unlocked
+ eliteWarpaintRNG = true
+
+ foreach( player in GetPlayerArray() )
+ {
+ player.SetPersistentVar( "isPostGameScoreboardValid", true )
+ player.SetPersistentVar( "isFDPostGameScoreboardValid", false ) //FD itself overrides this right after when match ends
+ SetUIVar( level, "showGameSummary", true )
+
+ if( eliteWarpaintRNG )
+ SetPlayerChallengeSquadLeader( player )
+
+ if( ShouldAwardHappyHourBonus( player ) )
+ {
+ AddPlayerScore( player, "HappyHourBonus" )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.HAPPY_HOUR + "]", 5 ) //The XP Given from Happy Hour Score is 5 merits
+ }
+ }
+}
+
+void function TDMChallenges_OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim == attacker || !attacker.IsPlayer() || GetGameState() != eGameState.Playing )
+ return
+
+ if ( victim.IsPlayer() )
+ {
+ if( victim in file.pilotstreak )
+ file.pilotstreak[victim] = 0
+ if( attacker in file.pilotstreak )
+ {
+ file.pilotstreak[attacker]++
+ if( file.pilotstreak[attacker] >= 3 && !HasPlayerCompletedMeritScore( attacker ) )
+ {
+ AddPlayerScore( attacker, "ChallengeTDM" )
+ SetPlayerChallengeMeritScore( attacker )
+ }
+ }
+ }
+}
+
+void function FFAChallenges_OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim == attacker || !attacker.IsPlayer() || GetGameState() != eGameState.Playing )
+ return
+
+ if ( victim.IsPlayer() && attacker in file.pilotstreak )
+ {
+ file.pilotstreak[attacker]++
+ if( file.pilotstreak[attacker] >= 5 && !HasPlayerCompletedMeritScore( attacker ) )
+ {
+ AddPlayerScore( attacker, "ChallengePVPKillCount" )
+ SetPlayerChallengeMeritScore( attacker )
+ }
+ }
+}
+
+bool function HasPlayerCompletedMeritScore( entity player )
+{
+ Assert( player in file.playerChallenge, player + " is not registered in the challenge pool hooks." )
+ return file.playerChallenge[ player ]
+}
+
+
+
+
+
+
+
+/*=============================================================================================================
+ ____ _ _ _ _
+ / ___| __ _ _ __ ___ ___ _ __ ___ ___ __| | ___ | | | | ___ ___ | | __ ___
+ | | _ / _` || '_ ` _ \ / _ \| '_ ` _ \ / _ \ / _` | / _ \ | |_| | / _ \ / _ \ | |/ // __|
+ | |_| || (_| || | | | | || __/| | | | | || (_) || (_| || __/ | _ || (_) || (_) || < \__ \
+ \____| \__,_||_| |_| |_| \___||_| |_| |_| \___/ \__,_| \___| |_| |_| \___/ \___/ |_|\_\|___/
+
+=============================================================================================================*/
+
+void function SetPlayerChallengeEvacState( entity player, int successEvac = 0 )
+{
+ if( successEvac == 0 ) //Evac Ship destroyed
+ Remote_CallFunction_UI( player, "SCB_SetEvacMeritState", 2 )
+
+ else if( successEvac == 1 ) //Player itself managed to evac
+ {
+ file.playerTotalMeritCount[ player ]++
+ Remote_CallFunction_UI( player, "SCB_SetEvacMeritState", 1 )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.EVAC + "]", 1 )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+ }
+
+ else if( successEvac == 2 ) //Team managed to evac
+ Remote_CallFunction_UI( player, "SCB_SetEvacMeritState", 3 )
+}
+
+void function SetPlayerChallengeMatchWon( entity player, bool playerWon )
+{
+ if( playerWon )
+ {
+ file.playerTotalMeritCount[ player ]++
+ Remote_CallFunction_UI( player, "SCB_SetWinMeritState", 1 )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.MATCH_VICTORY + "]", 1 )
+ player.SetPersistentVar( "matchWin", true )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+ }
+ else
+ Remote_CallFunction_UI( player, "SCB_SetWinMeritState", -1 )
+}
+
+void function SetPlayerChallengeMatchComplete( entity player )
+{
+ file.playerTotalMeritCount[ player ]++
+ Remote_CallFunction_UI( player, "SCB_SetCompleteMeritState", 1 )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.MATCH_COMPLETED + "]", 1 )
+ player.SetPersistentVar( "matchComplete", true )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+}
+
+void function SetPlayerChallengeSquadLeader( entity player )
+{
+ if( !ProgressionEnabledForPlayer( player ) )
+ return
+
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SquadLeaderDoubleXP" )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SquadLeaderBonus", player.GetEncodedEHandle() )
+ player.SetPersistentVar( "matchSquadBonus", true )
+ Player_GiveDoubleXP( player, 1 )
+ foreach( entity teamplayer in GetPlayerArrayOfTeam( player.GetTeam() ) )
+ {
+ if( teamplayer == player )
+ continue
+
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SquadLeaderBonus", teamplayer.GetEncodedEHandle() )
+ }
+}
+
+void function SetPlayerChallengeMeritScore( entity player )
+{
+ if( !HasPlayerCompletedMeritScore( player ) )
+ {
+ file.playerChallenge[ player ] = true
+ file.playerTotalMeritCount[ player ]++
+ Remote_CallFunction_UI( player, "SCB_SetScoreMeritState", 1 )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.SCORE_MILESTONE + "]", 1 )
+ player.SetPersistentVar( "matchScoreEvent", true )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+ }
+}
+
+void function IncrementPlayerChallengeTitanLeveledUp( entity player )
+{
+ player.p.meritData.titanMerits++
+ file.playerTotalMeritCount[ player ]++
+
+ Remote_CallFunction_UI( player, "SCB_SetTitanMeritCount", player.p.meritData.titanMerits++ )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+}
+
+void function IncrementPlayerChallengeWeaponLeveledUp( entity player )
+{
+ player.p.meritData.weaponMerits++
+ file.playerTotalMeritCount[ player ]++
+
+ Remote_CallFunction_UI( player, "SCB_SetWeaponMeritCount", player.p.meritData.weaponMerits )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+}
+
+void function IncrementPlayerChallengeFactionLeveledUp( entity player )
+{
+ file.playerTotalMeritCount[ player ]++
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
index 23ae37a17..c3bdf01c6 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
@@ -26,18 +26,9 @@ const int MAX_DROPSHIP_PLAYERS = 4
global const float DROPSHIP_INTRO_LENGTH = 15.0 // TODO tweak this
-struct IntroDropship
-{
- entity dropship
-
- int playersInDropship
- entity[MAX_DROPSHIP_PLAYERS] players
-}
-
struct {
- // these used to be IntroDropship[2]s but i wanted to be able to use array.getrandom so they have to be actual arrays
- array<IntroDropship> militiaDropships
- array<IntroDropship> imcDropships
+ table< entity, array<entity> > militiaDropships
+ table< entity, array<entity> > imcDropships
float introStartTime
} file
@@ -52,7 +43,12 @@ void function ClassicMP_DefaultDropshipIntro_Setup()
void function DropshipIntro_OnClientConnected( entity player )
{
if ( GetGameState() == eGameState.Prematch )
- thread SpawnPlayerIntoDropship( player )
+ {
+ if( PlayerCanSpawn( player ) )
+ DoRespawnPlayer( player, null )
+
+ PutPlayerInDropship( player )
+ }
}
void function OnPrematchStart()
@@ -62,11 +58,11 @@ void function OnPrematchStart()
print( "starting dropship intro!" )
file.introStartTime = Time()
- // make 2 empty dropship structs per team
- IntroDropship emptyDropship
+ // Clear Dropship arrays of Teams for Match Restarts (i.e Half-Times)
file.militiaDropships.clear()
file.imcDropships.clear()
+ // Try to gather all possible Dropship spawn points for Team
array<entity> validDropshipSpawns
array<entity> dropshipSpawns = GetEntArrayByClass_Expensive( "info_spawnpoint_dropship_start" )
foreach ( entity dropshipSpawn in dropshipSpawns )
@@ -78,47 +74,47 @@ void function OnPrematchStart()
validDropshipSpawns.append( dropshipSpawn )
}
- // if no dropship spawns for this mode, just allow any dropship spawns
+ // Use any spawn point if not enough valid for this Gamemode exists
if ( validDropshipSpawns.len() < 2 )
validDropshipSpawns = dropshipSpawns
// spawn dropships
foreach ( entity dropshipSpawn in validDropshipSpawns )
{
- // todo: possibly make this only spawn dropships if we've got enough players to need them
int createTeam = HasSwitchedSides() ? GetOtherTeam( dropshipSpawn.GetTeam() ) : dropshipSpawn.GetTeam()
- array<IntroDropship> teamDropships = createTeam == TEAM_MILITIA ? file.militiaDropships : file.imcDropships
+ table< entity, array<entity> > teamDropships = createTeam == TEAM_MILITIA ? file.militiaDropships : file.imcDropships
if ( teamDropships.len() >= 2 )
- continue
+ break
- // create entity
entity dropship = CreateDropship( createTeam, dropshipSpawn.GetOrigin(), dropshipSpawn.GetAngles() )
-
- teamDropships.append( clone emptyDropship )
- teamDropships[ teamDropships.len() - 1 ].dropship = dropship
-
AddAnimEvent( dropship, "dropship_warpout", WarpoutEffect )
+
dropship.SetValueForModelKey( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
- dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
+ if ( dropshipSpawn.GetTeam() == TEAM_IMC )
+ dropship.SetValueForModelKey( $"models/vehicle/goblin_dropship/goblin_dropship_hero.mdl" )
DispatchSpawn( dropship )
- // have to do this after dispatch otherwise it won't work for some reason
- // weirdly enough, tf2 actually does use different dropships for imc and militia, despite these concepts not really being a thing for players in tf2
- // probably was just missed by devs, but keeping it in for accuracy
+ dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
if ( dropshipSpawn.GetTeam() == TEAM_IMC )
dropship.SetModel( $"models/vehicle/goblin_dropship/goblin_dropship_hero.mdl" )
- else
- dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
+
+ teamDropships[ dropship ] <- [ null, null, null, null ]
thread PlayAnim( dropship, "dropship_classic_mp_flyin" )
}
+ // Populate Dropships
foreach ( entity player in GetPlayerArray() )
{
if ( !IsPrivateMatchSpectator( player ) )
- thread SpawnPlayerIntoDropship( player )
+ {
+ if( PlayerCanSpawn( player ) )
+ DoRespawnPlayer( player, null )
+
+ PutPlayerInDropship( player )
+ }
else
RespawnPrivateMatchSpectator( player )
}
@@ -128,76 +124,79 @@ void function OnPrematchStart()
void function EndIntroWhenFinished()
{
- wait 15.0
+ wait DROPSHIP_INTRO_LENGTH
ClassicMP_OnIntroFinished()
}
-void function SpawnPlayerIntoDropship( entity player )
+void function PutPlayerInDropship( entity player )
{
- player.EndSignal( "OnDestroy" )
+ //Find the player's dropship and seat
+ table< entity, array<entity> > teamDropships
+ if ( player.GetTeam() == TEAM_MILITIA )
+ teamDropships = file.militiaDropships
+ else
+ teamDropships = file.imcDropships
+
+ entity playerDropship
+ array< int > availableShipSlots
+ array< entity > introDropships
+ int playerDropshipIndex = RandomInt( MAX_DROPSHIP_PLAYERS )
+ foreach( dropship, playerslot in teamDropships )
+ {
+ introDropships.append( dropship )
+ for ( int i = 0; i < MAX_DROPSHIP_PLAYERS; i++ )
+ {
+ if ( !IsValidPlayer( playerslot[i] ) )
+ availableShipSlots.append( i )
+ }
+
+ if( !availableShipSlots.len() )
+ continue
+
+ int slotPick = availableShipSlots.getrandom()
+ playerslot[slotPick] = player
+ playerDropship = dropship
+ playerDropshipIndex = slotPick
+ break
+ }
+
+ if( !IsAlive( playerDropship ) ) //If we're at this point, we have more players than we do dropships, so just pick a random one
+ playerDropship = introDropships.getrandom()
+
+ thread SpawnPlayerIntoDropship( player, playerDropshipIndex, playerDropship )
+}
- if ( IsAlive( player ) )
- player.Die() // kill them so we don't have any issues respawning them later
+void function SpawnPlayerIntoDropship( entity player, int playerDropshipIndex, entity playerDropship )
+{
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "OnDeath" )
- player.s.dropshipIntroIsJumping <- false
- OnThreadEnd( function() : ( player )
+ OnThreadEnd( function() : ( player, playerDropshipIndex, playerDropship )
{
if ( IsValid( player ) )
{
player.ClearParent()
ClearPlayerAnimViewEntity( player )
-
- if ( !player.s.dropshipIntroIsJumping )
- {
- player.MovementEnable()
- player.EnableWeaponViewModel()
- RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
- }
+ }
+ if( IsAlive( playerDropship ) )
+ {
+ if ( playerDropship.GetTeam() == TEAM_MILITIA )
+ file.militiaDropships[ playerDropship ][ playerDropshipIndex ] = null
+ else
+ file.imcDropships[ playerDropship ][ playerDropshipIndex ] = null
}
})
- WaitFrame()
-
- player.EndSignal( "OnDeath" )
-
- // find the player's dropship and seat
- array<IntroDropship> teamDropships
- if ( player.GetTeam() == TEAM_MILITIA )
- teamDropships = file.militiaDropships
- else
- teamDropships = file.imcDropships
-
- IntroDropship playerDropship
- int playerDropshipIndex = -1
- foreach ( IntroDropship dropship in teamDropships )
- for ( int i = 0; i < dropship.players.len(); i++ )
- if ( dropship.players[ i ] == null )
- {
- playerDropship = dropship
- playerDropshipIndex = i
-
- dropship.players[ i ] = player
- break
- }
-
- if ( playerDropship.dropship == null )
- {
- // if we're at this point, we have more players than we do dropships, so just pick a random one
- playerDropship = teamDropships.getrandom()
- playerDropshipIndex = RandomInt( MAX_DROPSHIP_PLAYERS )
- }
-
- // respawn player and holster their weapons so they aren't out
- if ( !IsAlive( player ) )
- player.RespawnPlayer( null )
- HolsterAndDisableWeapons(player)
+ HolsterAndDisableWeapons( player )
player.DisableWeaponViewModel()
+ UnMuteAll( player )
+ StopSoundOnEntity( player, "Duck_For_FrontierDefenseTitanSelectScreen" )
// hide hud and fade screen out from black
AddCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
ScreenFadeFromBlack( player, 0.5, 0.5 )
// faction leaders are done clientside, spawn them here
- Remote_CallFunction_NonReplay( player, "ServerCallback_SpawnFactionCommanderInDropship", playerDropship.dropship.GetEncodedEHandle(), file.introStartTime )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SpawnFactionCommanderInDropship", playerDropship.GetEncodedEHandle(), file.introStartTime )
// do firstperson sequence
FirstPersonSequenceStruct idleSequence
@@ -208,9 +207,7 @@ void function SpawnPlayerIntoDropship( entity player )
idleSequence.viewConeFunction = ViewConeRampFree
idleSequence.hideProxy = true
idleSequence.setInitialTime = Time() - file.introStartTime
- thread FirstPersonSequence( idleSequence, player, playerDropship.dropship )
- WaittillAnimDone( player )
-
+ waitthread FirstPersonSequence( idleSequence, player, playerDropship )
// todo: possibly rework this to actually get the time the idle anim takes and start the starttime of the jump sequence for very late joiners using that
// jump sequence
@@ -218,13 +215,17 @@ void function SpawnPlayerIntoDropship( entity player )
jumpSequence.firstPersonAnim = DROPSHIP_JUMP_ANIMS_POV[ playerDropshipIndex ]
jumpSequence.thirdPersonAnim = DROPSHIP_JUMP_ANIMS[ playerDropshipIndex ]
jumpSequence.attachment = "ORIGIN"
+ jumpSequence.viewConeFunction = ViewConeFree
jumpSequence.setInitialTime = max( 0.0, Time() - ( file.introStartTime + 11.0 ) ) // pretty sure you should do this with GetScriptedAnimEventCycleFrac?
// idk unsure how to use that, all i know is getsequenceduration > the length it actually should be
- thread FirstPersonSequence( jumpSequence, player, playerDropship.dropship )
- WaittillAnimDone( player ) // somehow this is better than just waiting for the blocking FirstPersonSequence call?
+ #if BATTLECHATTER_ENABLED
+ if( playerDropshipIndex == 0 )
+ PlayBattleChatterLine( player, "bc_pIntroChat" )
+ #endif
+
+ waitthread FirstPersonSequence( jumpSequence, player, playerDropship )
- player.s.dropshipIntroIsJumping <- true
thread PlayerJumpsFromDropship( player )
}
@@ -240,20 +241,21 @@ void function PlayerJumpsFromDropship( entity player )
// show weapon viewmodel and hud and let them move again
player.MovementEnable()
player.EnableWeaponViewModel()
- DeployAndEnableWeapons(player)
+ DeployAndEnableWeapons( player )
RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
}
})
-
- // wait for intro timer to be fully done
- wait ( file.introStartTime + DROPSHIP_INTRO_LENGTH ) - Time()
- player.MovementDisable() // disable all movement but let them look around still
- player.ConsumeDoubleJump() // movementdisable doesn't prevent double jumps
// wait for player to hit the ground
- wait 0.1 // assume players will never actually hit ground before this
+ player.ClearParent()
+ WaitFrame()
+ player.SetVelocity( < 0, 0, -100 > ) // Toss players a bit down so it makes a smoother transition when jumping off the Dropship
+ player.MovementDisable() // Disable all movement but let them look around still
+ player.ConsumeDoubleJump() // MovementDisable doesn't prevent double jumps
+ WaitFrame()
while ( !player.IsOnGround() && !player.IsWallRunning() && !player.IsWallHanging() ) // todo this needs tweaking
WaitFrame()
- TryGameModeAnnouncement( player )
+ if ( GetRoundsPlayed() == 0 ) //Intro is announced only for the first round in Vanilla as certain gamemodes have different announcements for rounds restarts
+ TryGameModeAnnouncement( player )
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_codecallbacks.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_codecallbacks.gnut
index ff281d6eb..0d1b42b7e 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_codecallbacks.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_codecallbacks.gnut
@@ -21,6 +21,8 @@ global function SetTitanMeterGainScale
#if MP
global function CodeCallback_OnServerAnimEvent
+global function CodeCallback_WeaponDropped
+global function AddCallback_OnWeaponDropped
#endif
struct AccumulatedDamageData
@@ -43,6 +45,7 @@ struct
]
table<entity, AccumulatedDamageData> playerAccumulatedDamageData
+ array< void functionref( entity ) > weaponDroppedCallbacks
} file
void function CodeCallback_Init()
@@ -1030,4 +1033,26 @@ void function CodeCallback_OnServerAnimEvent( entity ent, string eventName )
PerfEnd( PerfIndexServer.CB_OnServerAnimEvent )
}
+
+void function AddCallback_OnWeaponDropped( void functionref( entity ) callback )
+{
+ file.weaponDroppedCallbacks.append( callback )
+}
+
+void function CodeCallback_WeaponDropped( entity weapon )
+{
+ // shamelessly taken form SP
+ if ( !IsValid( weapon ) )
+ return
+
+ // Might look a bit hacky to put it here, but thats how SP does it
+ if ( !Flag( "WeaponDropsAllowed" ) )
+ {
+ weapon.Destroy()
+ return
+ }
+
+ foreach( callback in file.weaponDroppedCallbacks )
+ callback( weapon )
+}
#endif // #if MP \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
index 4c52a9bfd..0c66f5a96 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
@@ -59,18 +59,21 @@ struct {
bool functionref( entity victim, entity attacker, var damageInfo, bool isRoundEnd ) shouldTryUseProjectileReplayCallback
} file
-void function SetCallback_TryUseProjectileReplay( bool functionref( entity victim, entity attacker, var damageInfo, bool isRoundEnd ) callback )
-{
- file.shouldTryUseProjectileReplayCallback = callback
-}
-bool function ShouldTryUseProjectileReplay( entity victim, entity attacker, var damageInfo, bool isRoundEnd )
-{
- if ( file.shouldTryUseProjectileReplayCallback != null )
- return file.shouldTryUseProjectileReplayCallback( victim, attacker, damageInfo, isRoundEnd )
- // default to true (vanilla behaviour)
- return true
-}
+
+
+
+
+
+
+
+/*
+ ██████ █████ ███ ███ ███████ ███████ ████████ █████ ████████ ███████ ███████
+██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██
+██ ███ ███████ ██ ████ ██ █████ ███████ ██ ███████ ██ █████ ███████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ██████ ██ ██ ██ ██ ███████ ███████ ██ ██ ██ ██ ███████ ███████
+*/
void function PIN_GameStart()
{
@@ -96,10 +99,66 @@ void function PIN_GameStart()
AddCallback_OnPlayerKilled( OnPlayerKilled )
AddDeathCallback( "npc_titan", OnTitanKilled )
+ AddCallback_EntityChangedTeam( "player", OnPlayerChangedTeam )
RegisterSignal( "CleanUpEntitiesForRoundEnd" )
}
+void function GameState_EntitiesDidLoad()
+{
+ if ( GetClassicMPMode() || ClassicMP_ShouldTryIntroAndEpilogueWithoutClassicMP() )
+ ClassicMP_SetupIntro()
+}
+
+void function WaittillGameStateOrHigher( int gameState )
+{
+ while ( GetGameState() < gameState )
+ svGlobal.levelEnt.WaitSignal( "GameStateChanged" )
+}
+
+bool function ShouldTryUseProjectileReplay( entity victim, entity attacker, var damageInfo, bool isRoundEnd )
+{
+ if ( file.shouldTryUseProjectileReplayCallback != null )
+ return file.shouldTryUseProjectileReplayCallback( victim, attacker, damageInfo, isRoundEnd )
+ // default to true (vanilla behaviour)
+ return true
+}
+
+/// This is to move all NPCs that a player owns from one team to the other during a match
+/// Auto-Titans, Turrets, Ticks and Hacked Spectres will all move along together with the player to the new Team
+/// Also possibly prevents mods that spawns other types of NPCs that players can own from breaking when switching (i.e Drones, Hacked Reapers)
+void function OnPlayerChangedTeam( entity player )
+{
+ if ( !player.hasConnected ) // Prevents players who just joined to trigger below code, as server always pre setups their teams
+ return
+
+ if( IsIMCOrMilitiaTeam( player.GetTeam() ) )
+ NotifyClientsOfTeamChange( player, GetOtherTeam( player.GetTeam() ), player.GetTeam() )
+
+ foreach( npc in GetNPCArray() )
+ {
+ entity bossPlayer = npc.GetBossPlayer()
+ if ( IsValidPlayer( bossPlayer ) && bossPlayer == player && IsAlive( npc ) )
+ SetTeam( npc, player.GetTeam() )
+ }
+}
+
+
+
+
+
+
+
+
+
+/*
+ ██████ █████ ███ ███ ███████ ███████ ███████ ████████ ██ ██ ██████
+██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██
+██ ███ ███████ ██ ████ ██ █████ ███████ █████ ██ ██ ██ ██████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ██████ ██ ██ ██ ██ ███████ ███████ ███████ ██ ██████ ██
+*/
+
void function SetGameState( int newState )
{
if ( newState == GetGameState() )
@@ -114,23 +173,166 @@ void function SetGameState( int newState )
callbackFunc()
}
-void function GameState_EntitiesDidLoad()
+void function AddTeamScore( int team, int amount )
{
- if ( GetClassicMPMode() || ClassicMP_ShouldTryIntroAndEpilogueWithoutClassicMP() )
- ClassicMP_SetupIntro()
+ GameRules_SetTeamScore( team, GameRules_GetTeamScore( team ) + amount )
+ GameRules_SetTeamScore2( team, GameRules_GetTeamScore2( team ) + amount )
+
+ int scoreLimit
+ if ( IsRoundBased() )
+ scoreLimit = GameMode_GetRoundScoreLimit( GAMETYPE )
+ else
+ scoreLimit = GameMode_GetScoreLimit( GAMETYPE )
+
+ int score = GameRules_GetTeamScore( team )
+ if ( score >= scoreLimit || GetGameState() == eGameState.SuddenDeath )
+ SetWinner( team )
+ else if ( ( file.switchSidesBased && !file.hasSwitchedSides ) && score >= ( scoreLimit.tofloat() / 2.0 ) )
+ SetGameState( eGameState.SwitchingSides )
}
-void function WaittillGameStateOrHigher( int gameState )
+void function SetWinner( int team, string winningReason = "", string losingReason = "" )
+{
+ SetServerVar( "winningTeam", team )
+
+ file.gameWonThisFrame = true
+ thread UpdateGameWonThisFrameNextFrame()
+
+ if ( winningReason.len() == 0 )
+ file.announceRoundWinnerWinningSubstr = 0
+ else
+ file.announceRoundWinnerWinningSubstr = GetStringID( winningReason )
+
+ if ( losingReason.len() == 0 )
+ file.announceRoundWinnerLosingSubstr = 0
+ else
+ file.announceRoundWinnerLosingSubstr = GetStringID( losingReason )
+
+ if ( GamePlayingOrSuddenDeath() )
+ {
+ if ( IsRoundBased() )
+ {
+ if ( team != TEAM_UNASSIGNED )
+ {
+ GameRules_SetTeamScore( team, GameRules_GetTeamScore( team ) + 1 )
+ GameRules_SetTeamScore2( team, GameRules_GetTeamScore2( team ) + 1 )
+ }
+
+ SetGameState( eGameState.WinnerDetermined )
+ ScoreEvent_RoundComplete( team )
+ }
+ else
+ {
+ SetGameState( eGameState.WinnerDetermined )
+ ScoreEvent_MatchComplete( team )
+
+ array<entity> players = GetPlayerArray()
+ int functionref( entity, entity ) compareFunc = GameMode_GetScoreCompareFunc( GAMETYPE )
+ if ( compareFunc != null )
+ {
+ players.sort( compareFunc )
+ int playerCount = players.len()
+ int currentPlace = 1
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( i >= playerCount )
+ continue
+
+ if ( i > 0 && compareFunc( players[i - 1], players[i] ) != 0 )
+ currentPlace += 1
+
+ switch( currentPlace )
+ {
+ case 1:
+ UpdatePlayerStat( players[i], "game_stats", "mvp" )
+ UpdatePlayerStat( players[i], "game_stats", "mvp_total" )
+ UpdatePlayerStat( players[i], "game_stats", "top3OnTeam" )
+ break
+ case 2:
+ UpdatePlayerStat( players[i], "game_stats", "top3OnTeam" )
+ break
+ case 3:
+ UpdatePlayerStat( players[i], "game_stats", "top3OnTeam" )
+ break
+ }
+ }
+ }
+ }
+ }
+}
+
+void function SetTimeoutWinnerDecisionFunc( int functionref() callback )
{
- while ( GetGameState() < gameState )
- svGlobal.levelEnt.WaitSignal( "GameStateChanged" )
+ file.timeoutWinnerDecisionFunc = callback
+}
+
+void function SetCallback_TryUseProjectileReplay( bool functionref( entity victim, entity attacker, var damageInfo, bool isRoundEnd ) callback )
+{
+ file.shouldTryUseProjectileReplayCallback = callback
+}
+
+void function AddCallback_OnRoundEndCleanup( void functionref() callback )
+{
+ file.roundEndCleanupCallbacks.append( callback )
+}
+
+void function SetShouldUsePickLoadoutScreen( bool shouldUse )
+{
+ file.usePickLoadoutScreen = shouldUse
+}
+
+void function SetSwitchSidesBased( bool switchSides )
+{
+ file.switchSidesBased = switchSides
+}
+
+void function SetSuddenDeathBased( bool suddenDeathBased )
+{
+ file.suddenDeathBased = suddenDeathBased
}
+void function SetTimerBased( bool timerBased )
+{
+ file.timerBased = timerBased
+}
-// logic for individual gamestates:
+void function SetShouldUseRoundWinningKillReplay( bool shouldUse )
+{
+ SetServerVar( "roundWinningKillReplayEnabled", shouldUse )
+}
+void function SetRoundWinningKillReplayKillClasses( bool pilot, bool titan )
+{
+ file.roundWinningKillReplayTrackPilotKills = pilot
+ file.roundWinningKillReplayTrackTitanKills = titan // player kills in titans should get tracked anyway, might be worth renaming this
+}
+
+void function SetRoundWinningKillReplayAttacker( entity attacker, int inflictorEHandle = -1 )
+{
+ file.roundWinningKillReplayTime = Time()
+ file.roundWinningKillReplayHealthFrac = GetHealthFrac( attacker )
+ file.roundWinningKillReplayAttacker = attacker
+ file.roundWinningKillReplayInflictorEHandle = inflictorEHandle == -1 ? attacker.GetEncodedEHandle() : inflictorEHandle
+ file.roundWinningKillReplayTimeOfDeath = Time()
+}
+
+
+
+
+
+
+
+
+
+
+/*
+ ██████ ██ ██ ███████ ████████ ██████ ███ ███ ███████ ████████ █████ ██████ ████████
+██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██ ██
+██ ██ ██ ███████ ██ ██ ██ ██ ████ ██ ███████ ██ ███████ ██████ ██
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ██████ ██████ ███████ ██ ██████ ██ ██ ███████ ██ ██ ██ ██ ██ ██
+*/
-// eGameState.WaitingForCustomStart
void function GameStateEnter_WaitingForCustomStart()
{
// unused in release, comments indicate this was supposed to be used for an e3 demo
@@ -138,7 +340,22 @@ void function GameStateEnter_WaitingForCustomStart()
}
-// eGameState.WaitingForPlayers
+
+
+
+
+
+
+
+
+/*
+██ ██ █████ ██ ████████ ██ ███ ██ ██████ ███████ ██████ ██████ ██████ ██ █████ ██ ██ ███████ ██████ ███████
+██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██ █ ██ ███████ ██ ██ ██ ██ ██ ██ ██ ███ █████ ██ ██ ██████ ██████ ██ ███████ ████ █████ ██████ ███████
+██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ███ ███ ██ ██ ██ ██ ██ ██ ████ ██████ ██ ██████ ██ ██ ██ ███████ ██ ██ ██ ███████ ██ ██ ███████
+*/
+
void function GameStateEnter_WaitingForPlayers()
{
foreach ( entity player in GetPlayerArray() )
@@ -170,7 +387,22 @@ void function WaitingForPlayers_ClientConnected( entity player )
ScreenFadeToBlackForever( player, 0.0 )
}
-// eGameState.PickLoadout
+
+
+
+
+
+
+
+
+/*
+██████ ██ ██████ ██ ██ ██ ██████ █████ ██████ ██████ ██ ██ ████████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██████ ██ ██ █████ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██ ██ ██████ ██ ██ ███████ ██████ ██ ██ ██████ ██████ ██████ ██
+*/
+
void function GameStateEnter_PickLoadout()
{
thread GameStateEnter_PickLoadout_Threaded()
@@ -189,7 +421,22 @@ void function GameStateEnter_PickLoadout_Threaded()
}
-// eGameState.Prematch
+
+
+
+
+
+
+
+
+/*
+██████ ██████ ███████ ███ ███ █████ ████████ ██████ ██ ██
+██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██
+██████ ██████ █████ ██ ████ ██ ███████ ██ ██ ███████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██ ██ ██ ███████ ██ ██ ██ ██ ██ ██████ ██ ██
+*/
+
void function GameStateEnter_Prematch()
{
int timeLimit = GameMode_GetTimeLimit( GAMETYPE ) * 60
@@ -201,6 +448,14 @@ void function GameStateEnter_Prematch()
if ( !GetClassicMPMode() && !ClassicMP_ShouldTryIntroAndEpilogueWithoutClassicMP() )
thread StartGameWithoutClassicMP()
+
+ // Initialise any spectators. Hopefully they are all initialised already in CodeCallback_OnClientConnectionCompleted
+ // (_base_gametype_mp.gnut) but for modes like LTS this doesn't seem to happen late enough to work properly.
+ foreach ( player in GetPlayerArray() )
+ {
+ if ( IsPrivateMatchSpectator( player ) )
+ InitialisePrivateMatchSpectatorPlayer( player )
+ }
}
void function StartGameWithoutClassicMP()
@@ -227,7 +482,22 @@ void function StartGameWithoutClassicMP()
}
-// eGameState.Playing
+
+
+
+
+
+
+
+
+/*
+██████ ██ █████ ██ ██ ██ ███ ██ ██████
+██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██
+██████ ██ ███████ ████ ██ ██ ██ ██ ██ ███
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██ ███████ ██ ██ ██ ██ ██ ████ ██████
+*/
+
void function GameStateEnter_Playing()
{
thread GameStateEnter_Playing_Threaded()
@@ -270,7 +540,22 @@ void function GameStateEnter_Playing_Threaded()
}
-// eGameState.WinnerDetermined
+
+
+
+
+
+
+
+
+/*
+██ ██ ██ ███ ██ ███ ██ ███████ ██████ ██████ ███████ ████████ ███████ ██████ ███ ███ ██ ███ ██ ███████ ██████
+██ ██ ██ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██
+██ █ ██ ██ ██ ██ ██ ██ ██ ██ █████ ██████ ██ ██ █████ ██ █████ ██████ ██ ████ ██ ██ ██ ██ ██ █████ ██ ██
+██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ███ ███ ██ ██ ████ ██ ████ ███████ ██ ██ ██████ ███████ ██ ███████ ██ ██ ██ ██ ██ ██ ████ ███████ ██████
+*/
+
// these are likely innacurate
const float ROUND_END_FADE_KILLREPLAY = 1.0
const float ROUND_END_DELAY_KILLREPLAY = 3.0
@@ -387,6 +672,7 @@ void function GameStateEnter_WinnerDetermined_Threaded()
}
else
{
+ RegisterChallenges_OnMatchEnd()
if ( ClassicMP_ShouldRunEpilogue() )
{
ClassicMP_SetupEpilogue()
@@ -436,7 +722,22 @@ void function PlayerWatchesRoundWinningKillReplay( entity player, float replayLe
}
-// eGameState.SwitchingSides
+
+
+
+
+
+
+
+
+/*
+███████ ██ ██ ██ ████████ ██████ ██ ██ ██ ███ ██ ██████ ███████ ██ ██████ ███████ ███████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██
+███████ ██ █ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ███ ███████ ██ ██ ██ █████ ███████
+ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+███████ ███ ███ ██ ██ ██████ ██ ██ ██ ██ ████ ██████ ███████ ██ ██████ ███████ ███████
+*/
+
void function GameStateEnter_SwitchingSides()
{
thread GameStateEnter_SwitchingSides_Threaded()
@@ -527,7 +828,22 @@ void function PlayerWatchesSwitchingSidesKillReplay( entity player, bool doRepla
}
-// eGameState.SuddenDeath
+
+
+
+
+
+
+
+
+/*
+███████ ██ ██ ██████ ██████ ███████ ███ ██ ██████ ███████ █████ ████████ ██ ██
+██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██
+███████ ██ ██ ██ ██ ██ ██ █████ ██ ██ ██ ██ ██ █████ ███████ ██ ███████
+ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+███████ ██████ ██████ ██████ ███████ ██ ████ ██████ ███████ ██ ██ ██ ██ ██
+*/
+
void function GameStateEnter_SuddenDeath()
{
// disable respawns, suddendeath calling is done on a kill callback
@@ -549,7 +865,22 @@ void function GameStateEnter_SuddenDeath()
}
-// eGameState.Postmatch
+
+
+
+
+
+
+
+
+/*
+██████ ██████ ███████ ████████ ███ ███ █████ ████████ ██████ ██ ██
+██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██
+██████ ██ ██ ███████ ██ ██ ████ ██ ███████ ██ ██ ███████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██ ██████ ███████ ██ ██ ██ ██ ██ ██ ██████ ██ ██
+*/
+
void function GameStateEnter_Postmatch()
{
foreach ( entity player in GetPlayerArray() )
@@ -582,7 +913,21 @@ void function ForceFadeToBlack( entity player )
}
-// shared across multiple gamestates
+
+
+
+
+
+
+
+
+/*
+██ ██ ██ ██ ██ ██████ █████ ██ ██ ██████ █████ ██████ ██ ██ ███████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+█████ ██ ██ ██ ██ ███████ ██ ██ ██████ ███████ ██ █████ ███████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██ ██ ██ ███████ ███████ ██████ ██ ██ ███████ ███████ ██████ ██ ██ ██████ ██ ██ ███████
+*/
void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
{
@@ -712,10 +1057,22 @@ void function OnTitanKilled( entity victim, var damageInfo )
}
}
-void function AddCallback_OnRoundEndCleanup( void functionref() callback )
-{
- file.roundEndCleanupCallbacks.append( callback )
-}
+
+
+
+
+
+
+
+
+
+/*
+████████ ██████ ██████ ██ ███████ ██ ██ ███ ██ ██████ ████████ ██ ██████ ███ ██ ███████
+ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ████ ██ ██
+ ██ ██ ██ ██ ██ ██ █████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███████
+ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ██ ██████ ██████ ███████ ██ ██████ ██ ████ ██████ ██ ██ ██████ ██ ████ ███████
+*/
void function CleanUpEntitiesForRoundEnd()
{
@@ -753,86 +1110,6 @@ void function CleanUpEntitiesForRoundEnd()
SetPlayerDeathsHidden( false )
}
-
-
-// stuff for gamemodes to call
-
-void function SetShouldUsePickLoadoutScreen( bool shouldUse )
-{
- file.usePickLoadoutScreen = shouldUse
-}
-
-void function SetSwitchSidesBased( bool switchSides )
-{
- file.switchSidesBased = switchSides
-}
-
-void function SetSuddenDeathBased( bool suddenDeathBased )
-{
- file.suddenDeathBased = suddenDeathBased
-}
-
-void function SetTimerBased( bool timerBased )
-{
- file.timerBased = timerBased
-}
-
-void function SetShouldUseRoundWinningKillReplay( bool shouldUse )
-{
- SetServerVar( "roundWinningKillReplayEnabled", shouldUse )
-}
-
-void function SetRoundWinningKillReplayKillClasses( bool pilot, bool titan )
-{
- file.roundWinningKillReplayTrackPilotKills = pilot
- file.roundWinningKillReplayTrackTitanKills = titan // player kills in titans should get tracked anyway, might be worth renaming this
-}
-
-void function SetRoundWinningKillReplayAttacker( entity attacker, int inflictorEHandle = -1 )
-{
- file.roundWinningKillReplayTime = Time()
- file.roundWinningKillReplayHealthFrac = GetHealthFrac( attacker )
- file.roundWinningKillReplayAttacker = attacker
- file.roundWinningKillReplayInflictorEHandle = inflictorEHandle == -1 ? attacker.GetEncodedEHandle() : inflictorEHandle
- file.roundWinningKillReplayTimeOfDeath = Time()
-}
-
-void function SetWinner( int team, string winningReason = "", string losingReason = "" )
-{
- SetServerVar( "winningTeam", team )
-
- file.gameWonThisFrame = true
- thread UpdateGameWonThisFrameNextFrame()
-
- if ( winningReason.len() == 0 )
- file.announceRoundWinnerWinningSubstr = 0
- else
- file.announceRoundWinnerWinningSubstr = GetStringID( winningReason )
-
- if ( losingReason.len() == 0 )
- file.announceRoundWinnerLosingSubstr = 0
- else
- file.announceRoundWinnerLosingSubstr = GetStringID( losingReason )
-
- if ( GamePlayingOrSuddenDeath() )
- {
- if ( IsRoundBased() )
- {
- if ( team != TEAM_UNASSIGNED )
- {
- GameRules_SetTeamScore( team, GameRules_GetTeamScore( team ) + 1 )
- GameRules_SetTeamScore2( team, GameRules_GetTeamScore2( team ) + 1 )
- }
-
- SetGameState( eGameState.WinnerDetermined )
- }
- else
- SetGameState( eGameState.WinnerDetermined )
-
- ScoreEvent_MatchComplete( team )
- }
-}
-
void function UpdateGameWonThisFrameNextFrame()
{
WaitFrame()
@@ -840,29 +1117,6 @@ void function UpdateGameWonThisFrameNextFrame()
file.hasKillForGameWonThisFrame = false
}
-void function AddTeamScore( int team, int amount )
-{
- GameRules_SetTeamScore( team, GameRules_GetTeamScore( team ) + amount )
- GameRules_SetTeamScore2( team, GameRules_GetTeamScore2( team ) + amount )
-
- int scoreLimit
- if ( IsRoundBased() )
- scoreLimit = GameMode_GetRoundScoreLimit( GAMETYPE )
- else
- scoreLimit = GameMode_GetScoreLimit( GAMETYPE )
-
- int score = GameRules_GetTeamScore( team )
- if ( score >= scoreLimit || GetGameState() == eGameState.SuddenDeath )
- SetWinner( team )
- else if ( ( file.switchSidesBased && !file.hasSwitchedSides ) && score >= ( scoreLimit.tofloat() / 2.0 ) )
- SetGameState( eGameState.SwitchingSides )
-}
-
-void function SetTimeoutWinnerDecisionFunc( int functionref() callback )
-{
- file.timeoutWinnerDecisionFunc = callback
-}
-
int function GetWinningTeamWithFFASupport()
{
if ( !IsFFAGame() )
@@ -892,8 +1146,6 @@ int function GetWinningTeamWithFFASupport()
unreachable
}
-// idk
-
float function GameState_GetTimeLimitOverride()
{
return 100
@@ -923,8 +1175,6 @@ float function GetTimeLimit_ForGameMode()
return GetCurrentPlaylistVarFloat( playlistString, 10 )
}
-// faction dialogue
-
void function DialoguePlayNormal()
{
int totalScore = GameMode_GetScoreLimit( GameRules_GetGameMode() )
@@ -1008,4 +1258,4 @@ void function DialoguePlayWinnerDetermined()
PlayFactionDialogueToTeam( "scoring_won", winningTeam )
PlayFactionDialogueToTeam( "scoring_lost", losingTeam )
}
-}
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_score.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_score.nut
index be20982df..2a4c4282c 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_score.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_score.nut
@@ -8,6 +8,7 @@ global function ScoreEvent_TitanDoomed
global function ScoreEvent_TitanKilled
global function ScoreEvent_NPCKilled
global function ScoreEvent_MatchComplete
+global function ScoreEvent_RoundComplete
global function ScoreEvent_SetEarnMeterValues
global function ScoreEvent_SetupEarnMeterValuesForMixedModes
@@ -287,8 +288,24 @@ void function ScoreEvent_MatchComplete( int winningTeam )
foreach( entity player in GetPlayerArray() )
{
AddPlayerScore( player, "MatchComplete" )
+ SetPlayerChallengeMatchComplete( player )
if ( player.GetTeam() == winningTeam )
+ {
AddPlayerScore( player, "MatchVictory" )
+ SetPlayerChallengeMatchWon( player, true )
+ }
+ else
+ SetPlayerChallengeMatchWon( player, false )
+ }
+}
+
+void function ScoreEvent_RoundComplete( int winningTeam )
+{
+ foreach( entity player in GetPlayerArray() )
+ {
+ AddPlayerScore( player, "RoundComplete" )
+ if ( player.GetTeam() == winningTeam )
+ AddPlayerScore( player, "RoundVictory" )
}
}
@@ -304,7 +321,7 @@ void function ScoreEvent_SetupEarnMeterValuesForMixedModes() // mixed modes in t
{
// todo needs earn/overdrive values
// player-controlled stuff
- ScoreEvent_SetEarnMeterValues( "KillPilot", 0.07, 0.15 )
+ ScoreEvent_SetEarnMeterValues( "KillPilot", 0.07, 0.15, 0.33 ) // 5% for titan cores
ScoreEvent_SetEarnMeterValues( "KillTitan", 0.0, 0.15 )
ScoreEvent_SetEarnMeterValues( "TitanKillTitan", 0.0, 0.0 ) // unsure
ScoreEvent_SetEarnMeterValues( "PilotBatteryStolen", 0.0, 0.35 ) // this actually just doesn't have overdrive in vanilla even
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_stats.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_stats.nut
index bd64e4caa..74a9088b8 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_stats.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_stats.nut
@@ -334,6 +334,10 @@ void function OnPlayerOrNPCKilled( entity victim, entity attacker, var damageInf
thread SetLastPosForDistanceStatValid_Threaded( victim, false )
HandleDeathStats( victim, attacker, damageInfo )
+
+ if( victim == attacker ) //Suicides are registering stats, afaik vanilla ignores them
+ return
+
HandleKillStats( victim, attacker, damageInfo )
HandleWeaponKillStats( victim, attacker, damageInfo )
HandleTitanStats( victim, attacker, damageInfo )
@@ -489,23 +493,32 @@ void function HandleKillStats( entity victim, entity attacker, var damageInfo )
// get the player and it's pet titan
entity player
entity playerPetTitan
- if ( attacker.IsPlayer() )
+ entity inflictor = DamageInfo_GetInflictor( damageInfo )
+
+ if ( IsValid( inflictor ) )
{
- // the player is just the attacker
- player = attacker
- playerPetTitan = player.GetPetTitan()
+ if ( inflictor.IsProjectile() && IsValid( inflictor.GetOwner() ) ) // Attackers are always the final entity in the owning hierarchy, projectile owners though migh be a player's NPC minion (i.e Auto-Titans)
+ attacker = inflictor.GetOwner()
+
+ else if ( inflictor.IsNPC() ) // NPCs are bypassed as Attackers if they are owned by players, instead they become just inflictors
+ attacker = inflictor
}
- else if ( attacker.IsTitan() && IsPetTitan( attacker ) )
+
+ if ( attacker.IsNPC() )
{
- // the attacker is the player's auto titan
+ if ( !attacker.IsTitan() ) // Normal NPCs case
+ return
+
+ if ( !IsPetTitan( attacker ) ) // NPC Titans case
+ return
+
player = attacker.GetTitanSoul().GetBossPlayer()
playerPetTitan = attacker
}
+ else if ( attacker.IsPlayer() ) // Still checks this because worldspawn might be the attacker
+ player = attacker
else
- {
- // attacker could be something like an NPC, or worldspawn
return
- }
// check things once, for performance
int damageSource = DamageInfo_GetDamageSourceIdentifier( damageInfo )
@@ -931,6 +944,9 @@ void function HandleDistanceAndTimeStats_Threaded()
// track distance stats
foreach ( entity player in GetPlayerArray() )
{
+ if ( !IsValid( player ) )
+ continue
+
if ( player.p.lastPosForDistanceStatValid )
{
// not 100% sure on using Distance2D over Distance tbh
@@ -1035,7 +1051,10 @@ void function SaveStatsPeriodically_Threaded()
while( true )
{
foreach( entity player in GetPlayerArray() )
- Stats_SaveAllStats( player )
+ {
+ if ( IsValid( player ) )
+ Stats_SaveAllStats( player )
+ }
wait 5
}
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
index d0ce4f790..49720bf41 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
@@ -236,6 +236,8 @@ void function OnPrematchStart()
void function PlayerWatchesWargamesIntro( entity player )
{
+ player.EndSignal( "OnDestroy" )
+
if ( IsAlive( player ) )
player.Die()
@@ -258,8 +260,6 @@ void function PlayerWatchesWargamesIntro( entity player )
// we need to wait a frame if we killed ourselves to spawn into this, so just easier to do it all the time to remove any weirdness
WaitFrame()
-
- player.EndSignal( "OnDestroy" )
player.EndSignal( "OnDeath" )
int factionTeam = ConvertPlayerFactionToIMCOrMilitiaTeam( player )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
index 4956375bd..c47552b3e 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
@@ -1,27 +1,30 @@
untyped
-global function InitRatings // temp for testing
-
global function Spawn_Init
-global function SetRespawnsEnabled
-global function RespawnsEnabled
+global function FindSpawnPoint
+
global function SetSpawnpointGamemodeOverride
global function GetSpawnpointGamemodeOverride
global function AddSpawnpointValidationRule
+
+global function SetRespawnsEnabled
+global function RespawnsEnabled
global function CreateNoSpawnArea
global function DeleteNoSpawnArea
-
-global function FindSpawnPoint
+global function SpawnPointInNoSpawnArea
global function RateSpawnpoints_Generic
global function RateSpawnpoints_Frontline
-
-global function SetSpawnZoneRatingFunc
-global function SetShouldCreateMinimapSpawnZones
-global function CreateTeamSpawnZoneEntity
global function RateSpawnpoints_SpawnZones
global function DecideSpawnZone_Generic
-global function DecideSpawnZone_CTF
+
+global struct spawnZoneProperties{
+ int controllingTeam = TEAM_UNASSIGNED
+ entity minimapEnt = null
+ float zoneRating = 0.0
+}
+
+global table< entity, spawnZoneProperties > mapSpawnZones // Global so other scripts can access this for custom ratings if needed
struct NoSpawnArea
{
@@ -35,30 +38,61 @@ struct NoSpawnArea
struct {
bool respawnsEnabled = true
+ array<NoSpawnArea> noSpawnAreas
string spawnpointGamemodeOverride
array< bool functionref( entity, int ) > customSpawnpointValidationRules
-
- table<string, NoSpawnArea> noSpawnAreas
+ bool shouldCreateMinimapSpawnzones
} file
+
+
+
+
+
+
+
+
+
+
+/*
+██████ █████ ███████ ███████ ███████ ██ ██ ███ ██ ██████ ████████ ██ ██████ ███ ██ ███████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ████ ██ ██
+██████ ███████ ███████ █████ █████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███████
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██████ ██ ██ ███████ ███████ ██ ██████ ██ ████ ██████ ██ ██ ██████ ██ ████ ███████
+*/
+
void function Spawn_Init()
-{
+{
+ // callbacks for generic spawns
AddSpawnCallback( "info_spawnpoint_human", InitSpawnpoint )
- AddSpawnCallback( "info_spawnpoint_human_start", InitSpawnpoint )
AddSpawnCallback( "info_spawnpoint_titan", InitSpawnpoint )
+ AddSpawnCallback( "info_spawnpoint_droppod", InitSpawnpoint )
+ AddSpawnCallback( "info_spawnpoint_dropship", InitSpawnpoint )
+ AddSpawnCallback( "info_spawnpoint_human_start", InitSpawnpoint )
AddSpawnCallback( "info_spawnpoint_titan_start", InitSpawnpoint )
-
- // callbacks for generic spawns
- AddCallback_EntitiesDidLoad( InitPreferSpawnNodes )
+ AddSpawnCallback( "info_spawnpoint_droppod_start", InitSpawnpoint )
+ AddSpawnCallback( "info_spawnpoint_dropship_start", InitSpawnpoint )
// callbacks for spawnzone spawns
AddCallback_GameStateEnter( eGameState.Prematch, ResetSpawnzones )
AddSpawnCallbackEditorClass( "trigger_multiple", "trigger_mp_spawn_zone", AddSpawnZoneTrigger )
-}
-
-void function InitSpawnpoint( entity spawnpoint )
-{
- spawnpoint.s.lastUsedTime <- -999
+
+ float friendlyAIValue = 1.75
+ if ( GameModeHasCapturePoints() )
+ friendlyAIValue = 0.75
+
+ SpawnPoints_SetRatingMultipliers_Enemy( TD_TITAN, -10.0, -6.0, -1.0 )
+ SpawnPoints_SetRatingMultipliers_Enemy( TD_PILOT, -10.0, -6.0, -1.0 )
+ SpawnPoints_SetRatingMultipliers_Enemy( TD_AI, -2.0, -0.25, 0.0 )
+
+ SpawnPoints_SetRatingMultipliers_Friendly( TD_TITAN, 0.25, 1.75, friendlyAIValue )
+ SpawnPoints_SetRatingMultipliers_Friendly( TD_PILOT, 0.25, 1.75, friendlyAIValue )
+ SpawnPoints_SetRatingMultipliers_Friendly( TD_AI, 0.5, 0.25, 0.0 )
+
+ SpawnPoints_SetRatingMultiplier_PetTitan( 2.0 )
+
+ file.shouldCreateMinimapSpawnzones = GetCurrentPlaylistVarInt( "spawn_zone_enabled", 1 ) != 0
}
void function SetRespawnsEnabled( bool enabled )
@@ -71,9 +105,10 @@ bool function RespawnsEnabled()
return file.respawnsEnabled
}
-void function AddSpawnpointValidationRule( bool functionref( entity spawn, int team ) rule )
+void function InitSpawnpoint( entity spawnpoint )
{
- file.customSpawnpointValidationRules.append( rule )
+ spawnpoint.s.lastUsedTime <- -999
+ spawnpoint.s.inUse <- false
}
string function CreateNoSpawnArea( int blockSpecificTeam, int blockEnemiesOfTeam, vector position, float lifetime, float radius )
@@ -85,11 +120,12 @@ string function CreateNoSpawnArea( int blockSpecificTeam, int blockEnemiesOfTeam
noSpawnArea.lifetime = lifetime
noSpawnArea.radius = radius
- // generate an id
noSpawnArea.id = UniqueString( "noSpawnArea" )
- thread NoSpawnAreaLifetime( noSpawnArea )
+ if ( lifetime > 0 )
+ thread NoSpawnAreaLifetime( noSpawnArea )
+ file.noSpawnAreas.append( noSpawnArea )
return noSpawnArea.id
}
@@ -101,8 +137,41 @@ void function NoSpawnAreaLifetime( NoSpawnArea noSpawnArea )
void function DeleteNoSpawnArea( string noSpawnIdx )
{
- if ( noSpawnIdx in file.noSpawnAreas )
- delete file.noSpawnAreas[ noSpawnIdx ]
+ foreach ( noSpawnArea in file.noSpawnAreas )
+ {
+ if ( noSpawnArea.id == noSpawnIdx )
+ file.noSpawnAreas.removebyvalue( noSpawnArea )
+ }
+}
+
+bool function SpawnPointInNoSpawnArea( vector vec, int team )
+{
+ foreach ( noSpawnArea in file.noSpawnAreas )
+ {
+ if ( Distance( noSpawnArea.position, vec ) < noSpawnArea.radius )
+ {
+ if ( noSpawnArea.blockedTeam != TEAM_INVALID && noSpawnArea.blockedTeam == team )
+ return true
+
+ if ( noSpawnArea.blockOtherTeams != TEAM_INVALID && noSpawnArea.blockOtherTeams != team )
+ return true
+ }
+ }
+
+ return false
+}
+
+bool function IsSpawnpointValidDrop( entity spawnpoint, int team )
+{
+ if ( spawnpoint.IsOccupied() || spawnpoint.s.inUse )
+ return false
+
+ return true
+}
+
+void function AddSpawnpointValidationRule( bool functionref( entity spawn, int team ) rule )
+{
+ file.customSpawnpointValidationRules.append( rule )
}
void function SetSpawnpointGamemodeOverride( string gamemode )
@@ -114,35 +183,38 @@ string function GetSpawnpointGamemodeOverride()
{
if ( file.spawnpointGamemodeOverride != "" )
return file.spawnpointGamemodeOverride
- else
- return GAMETYPE
- unreachable
+ return GAMETYPE
}
-void function InitRatings( entity player, int team )
-{
- if ( player != null )
- SpawnPoints_InitRatings( player, team ) // no idea what the second arg supposed to be lol
-}
+
+
+
+
+
+
+
+
+
+/*
+███████ ██████ █████ ██ ██ ███ ██ ██████ ██████ ██████ ███████ ██████ ██ ███ ██ ██████
+██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██
+███████ ██████ ███████ ██ █ ██ ██ ██ ██ ██ ██ ██████ ██ ██ █████ ██████ ██ ██ ██ ██ ██ ███
+ ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+███████ ██ ██ ██ ███ ███ ██ ████ ██████ ██ ██ ██████ ███████ ██ ██ ██ ██ ████ ██████
+*/
entity function FindSpawnPoint( entity player, bool isTitan, bool useStartSpawnpoint )
{
int team = player.GetTeam()
- if ( HasSwitchedSides() )
- team = GetOtherTeam( team )
-
+
array<entity> spawnpoints
if ( useStartSpawnpoint )
spawnpoints = isTitan ? SpawnPoints_GetTitanStart( team ) : SpawnPoints_GetPilotStart( team )
else
spawnpoints = isTitan ? SpawnPoints_GetTitan() : SpawnPoints_GetPilot()
- InitRatings( player, player.GetTeam() )
-
- // don't think this is necessary since we call discardratings
- //foreach ( entity spawnpoint in spawnpoints )
- // spawnpoint.CalculateRating( isTitan ? TD_TITAN : TD_PILOT, team, 0.0, 0.0 )
+ SpawnPoints_InitRatings( player, team )
void functionref( int, array<entity>, int, entity ) ratingFunc = isTitan ? GameMode_GetTitanSpawnpointsRatingFunc( GAMETYPE ) : GameMode_GetPilotSpawnpointsRatingFunc( GAMETYPE )
ratingFunc( isTitan ? TD_TITAN : TD_PILOT, spawnpoints, team, player )
@@ -166,46 +238,46 @@ entity function FindSpawnPoint( entity player, bool isTitan, bool useStartSpawnp
spawnpoints = useStartSpawnpoint ? SpawnPoints_GetPilotStart( team ) : SpawnPoints_GetPilot()
}
- entity spawnpoint = GetBestSpawnpoint( player, spawnpoints )
+ entity spawnpoint = GetBestSpawnpoint( player, spawnpoints, isTitan )
spawnpoint.s.lastUsedTime = Time()
player.SetLastSpawnPoint( spawnpoint )
+
+ //SpawnPoints_DiscardRatings()
return spawnpoint
}
-entity function GetBestSpawnpoint( entity player, array<entity> spawnpoints )
+entity function GetBestSpawnpoint( entity player, array<entity> spawnpoints, bool isTitan )
{
- // not really 100% sure on this randomisation, needs some thought
array<entity> validSpawns
+
+ // I know this looks hacky but the native funcs to get the spawns is returning null arrays for FFA idk why.
+ if ( IsFFAGame() )
+ {
+ spawnpoints.clear()
+ if ( isTitan )
+ spawnpoints = GetEntArrayByClass_Expensive( "info_spawnpoint_titan" )
+ else
+ spawnpoints = GetEntArrayByClass_Expensive( "info_spawnpoint_human" )
+ }
+
foreach ( entity spawnpoint in spawnpoints )
{
if ( IsSpawnpointValid( spawnpoint, player.GetTeam() ) )
- {
validSpawns.append( spawnpoint )
-
- if ( validSpawns.len() == 3 ) // arbitrary small sample size
- break
- }
}
- if ( validSpawns.len() == 0 )
+ if ( !validSpawns.len() ) // First validity check
{
- // no valid spawns, very bad, so dont care about spawns being valid anymore
- print( "found no valid spawns! spawns may be subpar!" )
+ CodeWarning( "Map has no valid spawn points for " + GAMETYPE + " gamemode, attempting any other possible spawn point" )
foreach ( entity spawnpoint in spawnpoints )
- {
validSpawns.append( spawnpoint )
-
- if ( validSpawns.len() == 3 ) // arbitrary small sample size
- break
- }
}
- // last resort
- if ( validSpawns.len() == 0 )
+ if ( !validSpawns.len() ) // On all validity check, just gather the most basic spawn
{
- print( "map has literally 0 spawnpoints, as such everything is fucked probably, attempting to use info_player_start if present" )
+ CodeWarning( "Map has no proper spawn points, falling back to info_player_start" )
entity start = GetEnt( "info_player_start" )
if ( IsValid( start ) )
@@ -213,14 +285,19 @@ entity function GetBestSpawnpoint( entity player, array<entity> spawnpoints )
start.s.lastUsedTime <- -999
validSpawns.append( start )
}
+ else
+ throw( "Map has no player spawns at all" )
}
- return validSpawns[ RandomInt( validSpawns.len() ) ] // slightly randomize it
+ if ( IsFFAGame() )
+ return validSpawns.getrandom()
+
+ return validSpawns[0] // Return first entry in the array because native have already sorted everything through the ratings, so first one is the best one
}
bool function IsSpawnpointValid( entity spawnpoint, int team )
{
- if ( !spawnpoint.HasKey( "ignoreGamemode" ) || ( spawnpoint.HasKey( "ignoreGamemode" ) && spawnpoint.kv.ignoreGamemode == "0" ) ) // used by script-spawned spawnpoints
+ if ( !spawnpoint.HasKey( "ignoreGamemode" ) || spawnpoint.HasKey( "ignoreGamemode" ) && spawnpoint.kv.ignoreGamemode == "0" ) // used by script-spawned spawnpoints
{
if ( file.spawnpointGamemodeOverride != "" )
{
@@ -232,223 +309,149 @@ bool function IsSpawnpointValid( entity spawnpoint, int team )
return false
}
- int compareTeam = spawnpoint.GetTeam()
- if ( HasSwitchedSides() && ( compareTeam == TEAM_MILITIA || compareTeam == TEAM_IMC ) )
- compareTeam = GetOtherTeam( compareTeam )
-
foreach ( bool functionref( entity, int ) customValidationRule in file.customSpawnpointValidationRules )
if ( !customValidationRule( spawnpoint, team ) )
return false
- if ( spawnpoint.GetTeam() > 0 && compareTeam != team && !IsFFAGame() )
+ if ( !IsSpawnpointValidDrop( spawnpoint, team ) || Time() - spawnpoint.s.lastUsedTime <= 10.0 )
return false
- if ( spawnpoint.IsOccupied() )
+ if ( SpawnPointInNoSpawnArea( spawnpoint.GetOrigin(), team ) )
return false
-
- if ( Time() - spawnpoint.s.lastUsedTime <= 10.0 )
- return false
-
- foreach ( k, NoSpawnArea noSpawnArea in file.noSpawnAreas )
+
+ // Line of Sight Check, could use IsVisibleToEnemies but apparently that considers only players, not NPCs
+ array< entity > enemyTitans = GetTitanArrayOfEnemies( team )
+ if ( GetConVarBool( "spawnpoint_avoid_npc_titan_sight" ) )
{
- if ( Distance( noSpawnArea.position, spawnpoint.GetOrigin() ) > noSpawnArea.radius )
- continue
-
- if ( noSpawnArea.blockedTeam != TEAM_INVALID && noSpawnArea.blockedTeam == team )
- return false
-
- if ( noSpawnArea.blockOtherTeams != TEAM_INVALID && noSpawnArea.blockOtherTeams != team )
- return false
+ foreach ( titan in enemyTitans )
+ {
+ if ( IsAlive( titan ) && titan.IsNPC() && titan.CanSee( spawnpoint ) )
+ return false
+ }
}
-
- const minEnemyDist = 1000.0 // about 20 meters?
- // in rsquirrel extend returns null unlike in vanilla squirrel
- array< entity > spawnBlockers = GetPlayerArrayEx( "any", TEAM_ANY, TEAM_ANY, spawnpoint.GetOrigin(), minEnemyDist )
- spawnBlockers.extend( GetProjectileArrayEx( "any", TEAM_ANY, TEAM_ANY, spawnpoint.GetOrigin(), minEnemyDist ) )
- foreach ( entity blocker in spawnBlockers )
- if ( blocker.GetTeam() != team )
- return false
- // los check
return !spawnpoint.IsVisibleToEnemies( team )
}
-// SPAWNPOINT RATING FUNCS BELOW
-// generic
-struct {
- array<vector> preferSpawnNodes
-} spawnStateGeneric
-void function RateSpawnpoints_Generic( int checkClass, array<entity> spawnpoints, int team, entity player )
-{
- if ( !IsFFAGame() )
- {
- // use frontline spawns in 2-team modes
- RateSpawnpoints_Frontline( checkClass, spawnpoints, team, player )
- return
- }
- else
- {
- // todo: ffa spawns :terror:
- }
- // old algo: keeping until we have a better ffa spawn algo
- // i'm not a fan of this func, but i really don't have a better way to do this rn, and it's surprisingly good with los checks implemented now
-
- // calculate ratings for preferred nodes
- // this tries to prefer nodes with more teammates, then activity on them
- // todo: in the future it might be good to have this prefer nodes with enemies up to a limit of some sort
- // especially in ffa modes i could deffo see this falling apart a bit rn
- // perhaps dead players could be used to calculate some sort of activity rating? so high-activity points with an even balance of friendly/unfriendly players are preferred
- array<float> preferSpawnNodeRatings
- foreach ( vector preferSpawnNode in spawnStateGeneric.preferSpawnNodes )
+
+
+
+
+/*
+██████ ██████ ██ ███ ██ ████████ ██████ █████ ████████ ██ ███ ██ ██████
+██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██
+██████ ██ ██ ██ ██ ██ ██ ██ ██████ ███████ ██ ██ ██ ██ ██ ██ ███
+██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+██ ██████ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██████
+*/
+
+void function RateSpawnpoints_Generic( int checkClass, array<entity> spawnpoints, int team, entity player )
+{
+ foreach ( entity spawnpoint in spawnpoints )
{
- float currentRating
-
- // this seems weird, not using rn
- //Frontline currentFrontline = GetCurrentFrontline( team )
- //if ( !IsFFAGame() || currentFrontline.friendlyCenter != < 0, 0, 0 > )
- // currentRating += max( 0.0, ( 1000.0 - Distance2D( currentFrontline.origin, preferSpawnNode ) ) / 200 )
+ float currentRating = 0.0
- foreach ( entity nodePlayer in GetPlayerArray() )
- {
- float currentChange = 0.0
-
- // the closer a player is to a node the more they matter
- float dist = Distance2D( preferSpawnNode, nodePlayer.GetOrigin() )
- if ( dist > 600.0 )
- continue
-
- currentChange = ( 600.0 - dist ) / 5
- if ( player == nodePlayer )
- currentChange *= -3 // always try to stay away from places we've already spawned
- else if ( !IsAlive( nodePlayer ) ) // dead players mean activity which is good, but they're also dead so they don't matter as much as living ones
- currentChange *= 0.6
- if ( nodePlayer.GetTeam() != player.GetTeam() ) // if someone isn't on our team and alive they're probably bad
- {
- if ( IsFFAGame() ) // in ffa everyone is on different teams, so this isn't such a big deal
- currentChange *= -0.2
- else
- currentChange *= -0.6
- }
-
- currentRating += currentChange
- }
+ // Gather friendly scoring first to give positive rating first
+ currentRating += spawnpoint.NearbyAllyScore( team, "ai" )
+ currentRating += spawnpoint.NearbyAllyScore( team, "titan" )
+ currentRating += spawnpoint.NearbyAllyScore( team, "pilot" )
- preferSpawnNodeRatings.append( currentRating )
- }
-
- foreach ( entity spawnpoint in spawnpoints )
- {
- float currentRating
- float petTitanModifier
- // scale how much a given spawnpoint matters to us based on how far it is from each node
- bool spawnHasRecievedInitialBonus = false
- for ( int i = 0; i < spawnStateGeneric.preferSpawnNodes.len(); i++ )
- {
- // bonus if autotitan is nearish
- if ( IsAlive( player.GetPetTitan() ) && Distance( player.GetPetTitan().GetOrigin(), spawnStateGeneric.preferSpawnNodes[ i ] ) < 1200.0 )
- petTitanModifier += 10.0
-
- float dist = Distance2D( spawnpoint.GetOrigin(), spawnStateGeneric.preferSpawnNodes[ i ] )
- if ( dist > 750.0 )
- continue
-
- if ( dist < 600.0 && !spawnHasRecievedInitialBonus )
- {
- currentRating += 10.0
- spawnHasRecievedInitialBonus = true // should only get a bonus for simply being by a node once to avoid over-rating
- }
+ // Enemies then subtract that rating ( Values already returns negative, so no need to apply subtract again )
+ currentRating += spawnpoint.NearbyEnemyScore( team, "ai" )
+ currentRating += spawnpoint.NearbyEnemyScore( team, "titan" )
+ currentRating += spawnpoint.NearbyEnemyScore( team, "pilot" )
- currentRating += ( preferSpawnNodeRatings[ i ] * ( ( 750.0 - dist ) / 75 ) ) + max( RandomFloat( 1.25 ), 0.9 )
- if ( dist < 250.0 ) // shouldn't get TOO close to an active node
- currentRating *= 0.7
-
- if ( spawnpoint.s.lastUsedTime < 10.0 )
- currentRating *= 0.7
- }
-
- float rating = spawnpoint.CalculateRating( checkClass, team, currentRating, currentRating + petTitanModifier )
- //print( "spawnpoint at " + spawnpoint.GetOrigin() + " has rating: " + )
+ if ( spawnpoint == player.p.lastSpawnPoint ) // Reduce the rating of the spawn point used previously
+ currentRating += GetConVarFloat( "spawnpoint_last_spawn_rating" )
- if ( rating != 0.0 || currentRating != 0.0 )
- print( "rating = " + rating + ", internal rating = " + currentRating )
- }
-}
-
-void function InitPreferSpawnNodes()
-{
- foreach ( entity hardpoint in GetEntArrayByClass_Expensive( "info_hardpoint" ) )
- {
- if ( !hardpoint.HasKey( "hardpointGroup" ) )
- continue
-
- if ( hardpoint.kv.hardpointGroup != "A" && hardpoint.kv.hardpointGroup != "B" && hardpoint.kv.hardpointGroup != "C" )
- continue
-
- spawnStateGeneric.preferSpawnNodes.append( hardpoint.GetOrigin() )
+ spawnpoint.CalculateRating( checkClass, team, currentRating, currentRating * 0.25 )
}
-
- //foreach ( entity frontline in GetEntArrayByClass_Expensive( "info_frontline" ) )
- // spawnStateGeneric.preferSpawnNodes.append( frontline.GetOrigin() )
}
-// frontline
void function RateSpawnpoints_Frontline( int checkClass, array<entity> spawnpoints, int team, entity player )
{
+ Frontline currentFrontline = GetFrontline( team )
+
+ vector inverseFrontlineDir = currentFrontline.combatDir * -1
+ vector adjustedPosition = currentFrontline.origin + currentFrontline.combatDir * 8000
+
+ SpawnPoints_InitFrontlineData( adjustedPosition, currentFrontline.combatDir, currentFrontline.origin, currentFrontline.friendlyCenter, 4000 )
+
foreach ( entity spawnpoint in spawnpoints )
{
- float rating = spawnpoint.CalculateFrontlineRating()
- spawnpoint.CalculateRating( checkClass, player.GetTeam(), rating, rating > 0 ? rating * 0.25 : rating )
+ float frontlineRating = spawnpoint.CalculateFrontlineRating()
+
+ spawnpoint.CalculateRating( checkClass, team, frontlineRating, frontlineRating * 0.25 )
}
}
-// spawnzones
-struct {
- array<entity> mapSpawnzoneTriggers
- entity functionref( array<entity>, int ) spawnzoneRatingFunc
- bool shouldCreateMinimapSpawnzones = false
-
- // for DecideSpawnZone_Generic
- table<int, entity> activeTeamSpawnzones
- table<int, entity> activeTeamSpawnzoneMinimapEnts
-} spawnStateSpawnzones
+
+
+
+
+
+
+
+
+
+/*
+███████ ██████ █████ ██ ██ ███ ██ ███████ ██████ ███ ██ ███████ ███████
+██ ██ ██ ██ ██ ██ ██ ████ ██ ███ ██ ██ ████ ██ ██ ██
+███████ ██████ ███████ ██ █ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ █████ ███████
+ ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██
+███████ ██ ██ ██ ███ ███ ██ ████ ███████ ██████ ██ ████ ███████ ███████
+*/
void function ResetSpawnzones()
{
- spawnStateSpawnzones.activeTeamSpawnzones.clear()
-
- foreach ( int team, entity minimapEnt in spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts )
- if ( IsValid( minimapEnt ) )
- minimapEnt.Destroy()
-
- spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts.clear()
+ foreach ( zone, zoneProperties in mapSpawnZones )
+ {
+ if ( IsValid( zoneProperties.minimapEnt ) )
+ zoneProperties.minimapEnt.Destroy()
+
+ zoneProperties.controllingTeam = TEAM_UNASSIGNED
+ zoneProperties.zoneRating = 0.0
+ }
}
void function AddSpawnZoneTrigger( entity trigger )
{
- trigger.s.spawnzoneRating <- 0.0
- spawnStateSpawnzones.mapSpawnzoneTriggers.append( trigger )
+ spawnZoneProperties zoneProperties
+ mapSpawnZones[trigger] <- zoneProperties
}
-void function SetSpawnZoneRatingFunc( entity functionref( array<entity>, int ) ratingFunc )
+bool function TeamHasDirtySpawnzone( int team )
{
- spawnStateSpawnzones.spawnzoneRatingFunc = ratingFunc
-}
-
-void function SetShouldCreateMinimapSpawnZones( bool shouldCreateMinimapSpawnzones )
-{
- spawnStateSpawnzones.shouldCreateMinimapSpawnzones = shouldCreateMinimapSpawnzones
+ foreach ( zone, zoneProperties in mapSpawnZones )
+ {
+ if ( zoneProperties.controllingTeam == team )
+ {
+ int numDeadInZone = 0
+ array<entity> teamPlayers = GetPlayerArrayOfTeam( team )
+ foreach ( entity player in teamPlayers )
+ {
+ if ( Time() - player.p.postDeathThreadStartTime < 20.0 && zone.ContainsPoint( player.p.deathOrigin ) )
+ numDeadInZone++
+ }
+
+ if ( numDeadInZone < teamPlayers.len() )
+ return false
+ }
+ }
+
+ return true
}
-entity function CreateTeamSpawnZoneEntity( entity spawnzone, int team )
+void function CreateTeamSpawnZoneEntity( entity spawnzone, int team )
{
entity minimapObj = CreatePropScript( $"models/dev/empty_model.mdl", spawnzone.GetOrigin() )
SetTeam( minimapObj, team )
- minimapObj.Minimap_SetObjectScale( 100.0 / Distance2D( < 0, 0, 0 >, spawnzone.GetBoundingMaxs() ) )
+ minimapObj.Minimap_SetObjectScale( Distance2D( < 0, 0, 0 >, spawnzone.GetBoundingMaxs() ) / 16000 ) // 16000 cuz thats the total space Minimap uses
minimapObj.Minimap_SetAlignUpright( true )
minimapObj.Minimap_AlwaysShow( TEAM_IMC, null )
minimapObj.Minimap_AlwaysShow( TEAM_MILITIA, null )
@@ -461,67 +464,58 @@ entity function CreateTeamSpawnZoneEntity( entity spawnzone, int team )
minimapObj.Minimap_SetCustomState( eMinimapObject_prop_script.SPAWNZONE_MIL )
minimapObj.DisableHibernation()
- return minimapObj
+ mapSpawnZones[spawnzone].minimapEnt = minimapObj
}
void function RateSpawnpoints_SpawnZones( int checkClass, array<entity> spawnpoints, int team, entity player )
{
- if ( spawnStateSpawnzones.spawnzoneRatingFunc == null )
- spawnStateSpawnzones.spawnzoneRatingFunc = DecideSpawnZone_Generic
-
- // don't use spawnzones if we're using start spawns
if ( ShouldStartSpawn( player ) )
{
RateSpawnpoints_Generic( checkClass, spawnpoints, team, player )
return
}
-
- entity spawnzone = spawnStateSpawnzones.spawnzoneRatingFunc( spawnStateSpawnzones.mapSpawnzoneTriggers, player.GetTeam() )
- if ( !IsValid( spawnzone ) ) // no spawn zone, use generic algo
+
+ array< entity > zoneTriggers
+ foreach ( zone, zoneProperties in mapSpawnZones )
+ zoneTriggers.append( zone )
+
+ entity spawnzone = DecideSpawnZone_Generic( zoneTriggers, player.GetTeam() )
+ if ( !IsValid( spawnzone ) )
{
RateSpawnpoints_Generic( checkClass, spawnpoints, team, player )
return
}
- // rate spawnpoints
foreach ( entity spawn in spawnpoints )
{
float rating = 0.0
float distance = Distance2D( spawn.GetOrigin(), spawnzone.GetOrigin() )
if ( distance < Distance2D( < 0, 0, 0 >, spawnzone.GetBoundingMaxs() ) )
- rating = 100.0
- else // max 35 rating if not in zone, rate by closest
- rating = 35.0 * ( 1 - ( distance / 5000.0 ) )
+ rating = 10.0
+ else
+ rating = 2.0 * ( 1 - ( distance / 3000.0 ) )
- spawn.CalculateRating( checkClass, player.GetTeam(), rating, rating )
+ spawn.CalculateRating( checkClass, team, rating, rating * 0.25 )
}
}
entity function DecideSpawnZone_Generic( array<entity> spawnzones, int team )
{
- if ( spawnzones.len() == 0 )
+ if ( !spawnzones.len() )
return null
- // get average team startspawn positions
- int spawnCompareTeam = team
- if ( HasSwitchedSides() )
- spawnCompareTeam = GetOtherTeam( team )
-
- array<entity> startSpawns = SpawnPoints_GetPilotStart( spawnCompareTeam )
- array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( spawnCompareTeam ) )
+ array<entity> startSpawns = SpawnPoints_GetPilotStart( team )
+ array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( team ) )
- if ( startSpawns.len() == 0 || enemyStartSpawns.len() == 0 ) // ensure we don't crash
+ if ( !startSpawns.len() || !enemyStartSpawns.len() )
return null
-
- // get average startspawn position and max dist between spawns
- // could probably cache this, tbh, not like it should change outside of halftimes
- vector averageFriendlySpawns
+
+ vector averageFriendlySpawns
foreach ( entity spawn in startSpawns )
averageFriendlySpawns += spawn.GetOrigin()
averageFriendlySpawns /= startSpawns.len()
- // get average enemy startspawn position
vector averageEnemySpawns
foreach ( entity spawn in enemyStartSpawns )
averageEnemySpawns += spawn.GetOrigin()
@@ -530,250 +524,87 @@ entity function DecideSpawnZone_Generic( array<entity> spawnzones, int team )
float baseDistance = Distance2D( averageFriendlySpawns, averageEnemySpawns )
- bool needNewZone = true
- if ( team in spawnStateSpawnzones.activeTeamSpawnzones )
- {
- foreach ( entity player in GetPlayerArray() )
- {
- // couldn't get IsTouching, GetTouchingEntities or enter callbacks to work in testing, so doing this
- if ( player.GetTeam() != team && spawnStateSpawnzones.activeTeamSpawnzones[ team ].ContainsPoint( player.GetOrigin() ) )
- break
- }
-
- int numDeadInZone = 0
- array<entity> teamPlayers = GetPlayerArrayOfTeam( team )
- foreach ( entity player in teamPlayers )
- {
- // check if they died in the zone recently, get a new zone if too many died
- if ( Time() - player.p.postDeathThreadStartTime < 15.0 && spawnStateSpawnzones.activeTeamSpawnzones[ team ].ContainsPoint( player.p.deathOrigin ) )
- numDeadInZone++
- }
-
- // cast to float so result is float
- if ( float( numDeadInZone ) / teamPlayers.len() <= 0.1 )
- needNewZone = false
- }
-
- if ( needNewZone )
+ if ( TeamHasDirtySpawnzone( team ) )
{
- // find new zone
array<entity> possibleZones
- foreach ( entity spawnzone in spawnStateSpawnzones.mapSpawnzoneTriggers )
+ foreach ( zone, zoneProperties in mapSpawnZones )
{
- // don't remember if you can do a "value in table.values" sorta thing in squirrel so doing manual lookup
- bool spawnzoneTaken = false
- foreach ( int otherTeam, entity otherSpawnzone in spawnStateSpawnzones.activeTeamSpawnzones )
- {
- if ( otherSpawnzone == spawnzone )
- {
- spawnzoneTaken = true
- break
- }
- }
-
- if ( spawnzoneTaken )
+ if ( zoneProperties.controllingTeam == GetOtherTeam( team ) )
continue
- // check zone validity
- bool spawnzoneEvil = false
- foreach ( entity player in GetPlayerArray() )
+ bool spawnzoneHasEnemies = false
+ foreach ( entity enemy in GetPlayerArrayOfEnemies_Alive( team ) )
{
- // couldn't get IsTouching, GetTouchingEntities or enter callbacks to work in testing, so doing this
- if ( player.GetTeam() != team && spawnzone.ContainsPoint( player.GetOrigin() ) )
+ if ( zone.ContainsPoint( enemy.GetOrigin() ) )
{
- spawnzoneEvil = true
+ spawnzoneHasEnemies = true
break
}
}
- // don't choose spawnzones that are closer to enemy base than friendly base
- // note: vanilla spawns might not necessarily require this, worth checking
- if ( !spawnzoneEvil && Distance2D( spawnzone.GetOrigin(), averageFriendlySpawns ) > Distance2D( spawnzone.GetOrigin(), averageEnemySpawns ) )
- spawnzoneEvil = true
+ if ( !spawnzoneHasEnemies && Distance2D( zone.GetOrigin(), averageFriendlySpawns ) > Distance2D( zone.GetOrigin(), averageEnemySpawns ) )
+ spawnzoneHasEnemies = true
- if ( spawnzoneEvil )
+ if ( spawnzoneHasEnemies )
continue
- // rate spawnzone based on distance to frontline
Frontline frontline = GetFrontline( team )
-
- // prefer spawns close to base pos
- float rating = 10 * ( 1.0 - Distance2D( averageFriendlySpawns, spawnzone.GetOrigin() ) / baseDistance )
+ float rating = 10 * ( 1.0 - Distance2D( averageFriendlySpawns, zone.GetOrigin() ) / baseDistance )
if ( frontline.friendlyCenter != < 0, 0, 0 > )
{
- // rate based on distance to frontline, and then prefer spawns in the same dir from the frontline as the combatdir
- rating += rating * ( 1.0 - ( Distance2D( spawnzone.GetOrigin(), frontline.friendlyCenter ) / baseDistance ) )
- rating *= fabs( frontline.combatDir.y - Normalize( spawnzone.GetOrigin() - averageFriendlySpawns ).y )
+ rating += rating * ( 1.0 - ( Distance2D( zone.GetOrigin(), frontline.friendlyCenter ) / baseDistance ) )
+ rating *= fabs( frontline.combatDir.y - Normalize( zone.GetOrigin() - averageFriendlySpawns ).y )
}
- spawnzone.s.spawnzoneRating = rating
- possibleZones.append( spawnzone )
+ zoneProperties.zoneRating = rating
+ possibleZones.append( zone )
}
- if ( possibleZones.len() == 0 )
+ if ( !possibleZones.len() )
return null
- possibleZones.sort( int function( entity a, entity b )
- {
- if ( a.s.spawnzoneRating > b.s.spawnzoneRating )
- return -1
-
- if ( b.s.spawnzoneRating > a.s.spawnzoneRating )
- return 1
-
- return 0
- } )
- entity chosenZone = possibleZones[ minint( RandomInt( 3 ), possibleZones.len() - 1 ) ]
+ possibleZones.sort( SortPossibleZones )
- if ( spawnStateSpawnzones.shouldCreateMinimapSpawnzones )
- {
- entity oldEnt
- if ( team in spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts )
- oldEnt = spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts[ team ]
-
- spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts[ team ] <- CreateTeamSpawnZoneEntity( chosenZone, team )
- if ( IsValid( oldEnt ) )
- oldEnt.Destroy()
- }
-
- spawnStateSpawnzones.activeTeamSpawnzones[ team ] <- chosenZone
- }
-
- return spawnStateSpawnzones.activeTeamSpawnzones[ team ]
-}
-
-// ideally this should be in the gamemode_ctf file, but would need refactors to expose more stuff that's not available there rn
-entity function DecideSpawnZone_CTF( array<entity> spawnzones, int team )
-{
- if ( spawnzones.len() == 0 )
- return null
-
- int otherTeam = GetOtherTeam( team )
- array<entity> enemyPlayers = GetPlayerArrayOfTeam( otherTeam )
-
- // get average team startspawn positions
- int spawnCompareTeam = team
- if ( HasSwitchedSides() )
- spawnCompareTeam = GetOtherTeam( team )
-
- array<entity> startSpawns = SpawnPoints_GetPilotStart( spawnCompareTeam )
- array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( spawnCompareTeam ) )
-
- if ( startSpawns.len() == 0 || enemyStartSpawns.len() == 0 ) // ensure we don't crash
- return null
-
- // get average startspawn position and max dist between spawns
- // could probably cache this, tbh, not like it should change outside of halftimes
- vector averageFriendlySpawns
- foreach ( entity spawn in startSpawns )
- averageFriendlySpawns += spawn.GetOrigin()
-
- averageFriendlySpawns /= startSpawns.len()
-
- // get average enemy startspawn position
- vector averageEnemySpawns
- foreach ( entity spawn in enemyStartSpawns )
- averageEnemySpawns += spawn.GetOrigin()
-
- averageEnemySpawns /= enemyStartSpawns.len()
-
- float baseDistance = Distance2D( averageFriendlySpawns, averageEnemySpawns )
-
- // find new zone
- array<entity> possibleZones
- foreach ( entity spawnzone in spawnStateSpawnzones.mapSpawnzoneTriggers )
- {
- // can't choose zone if another team has it
- if ( otherTeam in spawnStateSpawnzones.activeTeamSpawnzones && spawnStateSpawnzones.activeTeamSpawnzones[ otherTeam ] == spawnzone )
- continue
+ entity chosenZone = possibleZones[ minint( RandomInt( 3 ), possibleZones.len() - 1 ) ]
- // check zone validity
- bool spawnzoneEvil = false
- foreach ( entity player in enemyPlayers )
+ if ( file.shouldCreateMinimapSpawnzones )
{
- // couldn't get IsTouching, GetTouchingEntities or enter callbacks to work in testing, so doing this
- if ( spawnzone.ContainsPoint( player.GetOrigin() ) )
+ foreach ( zone, zoneProperties in mapSpawnZones )
{
- spawnzoneEvil = true
- break
+ if ( chosenZone == zone )
+ continue
+
+ if ( IsValid( zoneProperties.minimapEnt ) && zoneProperties.controllingTeam == team )
+ zoneProperties.minimapEnt.Destroy()
}
+
+ CreateTeamSpawnZoneEntity( chosenZone, team )
}
- // don't choose spawnzones that are closer to enemy base than friendly base
- if ( !spawnzoneEvil && Distance2D( spawnzone.GetOrigin(), averageFriendlySpawns ) > Distance2D( spawnzone.GetOrigin(), averageEnemySpawns ) )
- spawnzoneEvil = true
-
- if ( spawnzoneEvil )
- continue
-
- // rate spawnzone based on distance to frontline
- Frontline frontline = GetFrontline( team )
-
- // prefer spawns close to base pos
- float rating = 10 * ( 1.0 - Distance2D( averageFriendlySpawns, spawnzone.GetOrigin() ) / baseDistance )
-
- if ( frontline.friendlyCenter != < 0, 0, 0 > )
+ foreach ( zone, zoneProperties in mapSpawnZones )
{
- // rate based on distance to frontline, and then prefer spawns in the same dir from the frontline as the combatdir
- rating += rating * ( 1.0 - ( Distance2D( spawnzone.GetOrigin(), frontline.friendlyCenter ) / baseDistance ) )
- rating *= fabs( frontline.combatDir.y - Normalize( spawnzone.GetOrigin() - averageFriendlySpawns ).y )
-
- // reduce rating based on players that can currently see the zone
- bool hasAppliedInitialLoss = false
- foreach ( entity player in enemyPlayers )
- {
- // don't trace here, just do an angle check
- if ( PlayerCanSee( player, spawnzone, false, 65 ) && Distance2D( player.GetOrigin(), spawnzone.GetOrigin() ) <= 2000.0 )
- {
- float distFrac = TraceLineSimple( player.GetOrigin(), spawnzone.GetOrigin(), player )
-
- if ( distFrac >= 0.65 )
- {
- // give a fairly large loss if literally anyone can see it
- if ( !hasAppliedInitialLoss )
- {
- rating *= 0.8
- hasAppliedInitialLoss = true
- }
-
- rating *= ( 1.0 / enemyPlayers.len() ) * distFrac
- }
- }
- }
+ if ( chosenZone == zone )
+ continue
+
+ if ( zoneProperties.controllingTeam == team )
+ zoneProperties.controllingTeam = TEAM_UNASSIGNED
}
- spawnzone.s.spawnzoneRating = rating
- possibleZones.append( spawnzone )
+ mapSpawnZones[chosenZone].controllingTeam = team
+ return chosenZone
}
- if ( possibleZones.len() == 0 )
- return null
-
- possibleZones.sort( int function( entity a, entity b )
- {
- if ( a.s.spawnzoneRating > b.s.spawnzoneRating )
- return -1
+ return null
+}
+
+int function SortPossibleZones( entity a, entity b )
+{
+ if ( mapSpawnZones[a].zoneRating > mapSpawnZones[b].zoneRating )
+ return -1
- if ( b.s.spawnzoneRating > a.s.spawnzoneRating )
- return 1
+ if ( mapSpawnZones[b].zoneRating > mapSpawnZones[a].zoneRating )
+ return 1
- return 0
- } )
- entity chosenZone = possibleZones[ minint( RandomInt( 3 ), possibleZones.len() - 1 ) ]
-
- if ( spawnStateSpawnzones.shouldCreateMinimapSpawnzones )
- {
- entity oldEnt
- if ( team in spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts )
- oldEnt = spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts[ team ]
-
- spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts[ team ] <- CreateTeamSpawnZoneEntity( chosenZone, team )
- if ( IsValid( oldEnt ) )
- oldEnt.Destroy()
- }
-
- spawnStateSpawnzones.activeTeamSpawnzones[ team ] <- chosenZone
-
- return spawnStateSpawnzones.activeTeamSpawnzones[ team ]
+ return 0
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_progression.nut b/Northstar.CustomServers/mod/scripts/vscripts/sh_progression.nut
index 2dc88d0d4..3297643ec 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/sh_progression.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_progression.nut
@@ -88,7 +88,8 @@ bool function ClientCommand_SetProgression( entity player, array<string> args )
/// Resets a specific Titan's Aegis rank back to `0`
/// * `player` - The player entity to perform the action on
-/// * `args` - The arguments passed from the client command. `args[0]` should be an integer corresponding to the index of the Titan to reset.
+/// * `args` - The arguments passed from the client command. `args[0]` should be a string corresponding to the chassis name of the Titan to reset.
+/// Valid chassis are: ion, tone, vanguard, northstar, ronin, legion, and scorch.
///
/// Returns `true` on success and `false` on missing args.
bool function ClientCommand_ResetTitanAegis( entity player, array<string> args )
@@ -96,7 +97,12 @@ bool function ClientCommand_ResetTitanAegis( entity player, array<string> args )
if ( !args.len() )
return false
- int suitIndex = args[0].tointeger()
+ string titanRef = args[0].tolower()
+ if( !PersistenceEnumValueIsValid( "titanClasses", titanRef ) )
+ return false
+
+ int suitIndex = PersistenceGetEnumIndexForItemName( "titanClasses", titanRef )
+
player.SetPersistentVar( "titanFDUnlockPoints[" + suitIndex + "]", 0 )
player.SetPersistentVar( "previousFDUnlockPoints[" + suitIndex + "]", 0 )
player.SetPersistentVar( "fdTitanXP[" + suitIndex + "]", 0 )
@@ -218,11 +224,12 @@ void function ValidateEquippedItems( entity player )
}
// titan loadouts
+ int selectedTitanLoadoutIndex = player.GetPersistentVarAsInt( "titanSpawnLoadout.index" )
for ( int titanLoadoutIndex = 0; titanLoadoutIndex < NUM_PERSISTENT_TITAN_LOADOUTS; titanLoadoutIndex++ )
{
printt( "- VALIDATING TITAN LOADOUT: " + titanLoadoutIndex )
- bool isSelected = titanLoadoutIndex == player.GetPersistentVarAsInt( "titanSpawnLoadout.index" )
+ bool isSelected = titanLoadoutIndex == selectedTitanLoadoutIndex
TitanLoadoutDef loadout = GetTitanLoadout( player, titanLoadoutIndex )
TitanLoadoutDef defaultLoadout = shGlobal.defaultTitanLoadouts[titanLoadoutIndex]
@@ -440,11 +447,18 @@ void function ValidateEquippedItems( entity player )
{
printt( " - SELECTED TITAN CLASS IS LOCKED, RESETTING" )
player.SetPersistentVar( "titanSpawnLoadout.index", 0 )
- Remote_CallFunction_NonReplay( player, "ServerCallback_UpdateTitanModel", 0 )
+ selectedTitanLoadoutIndex = 0
}
}
- Remote_CallFunction_NonReplay( player, "ServerCallback_UpdateTitanModel", player.GetPersistentVarAsInt( "titanSpawnLoadout.index" ) )
+ if ( selectedTitanLoadoutIndex < 0 || selectedTitanLoadoutIndex >= NUM_PERSISTENT_TITAN_LOADOUTS )
+ {
+ printt( "- SELECTED TITAN CLASS IS INVALID, RESETTING" )
+ player.SetPersistentVar( "titanSpawnLoadout.index", 0 )
+ selectedTitanLoadoutIndex = 0
+ }
+
+ Remote_CallFunction_NonReplay( player, "ServerCallback_UpdateTitanModel", selectedTitanLoadoutIndex )
// pilot loadouts
for ( int pilotLoadoutIndex = 0; pilotLoadoutIndex < NUM_PERSISTENT_PILOT_LOADOUTS; pilotLoadoutIndex++ )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/titan_xp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/titan_xp.gnut
index 0436a393c..847881b58 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/titan_xp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/titan_xp.gnut
@@ -1,17 +1,39 @@
global function AddTitanXP
+global function AddFDTitanXP
void function AddTitanXP( entity player, int amount )
{
string titan = GetActiveTitanLoadout( player ).titanClass
int oldLevel = TitanGetLevel( player, titan )
+ int TitanXPMatch = player.GetPersistentVarAsInt( "xp_match[" + XP_TYPE.TITAN_LEVELED + "]" )
// increment xp
player.SetPersistentVar( "titanXP[" + titan + "]", min( TitanGetXP( player, titan ) + amount, TitanGetMaxXP( titan ) ) )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_TitanXPAdded", shTitanXP.titanClasses.find( titan ), TitanGetXP( player, titan ), amount )
// level up notif
if ( TitanGetLevel( player, titan ) != oldLevel )
{
Remote_CallFunction_NonReplay( player, "ServerCallback_TitanLeveledUp", shTitanXP.titanClasses.find( titan ), TitanGetGen( player, titan ), TitanGetLevel( player, titan ) )
AddPlayerScore( player, "TitanLevelUp" )
+ IncrementPlayerChallengeTitanLeveledUp( player )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.TITAN_LEVELED + "]", TitanXPMatch + 1 )
+
+ if( ProgressionEnabledForPlayer( player ) )
+ AwardRandomItemsForTitanLevels( player, titan, oldLevel, TitanGetLevel( player, titan ) )
}
+}
+
+void function AddFDTitanXP( entity player, int fdXPamount )
+{
+ string titanRef = GetActiveTitanLoadout( player ).titanClass
+
+ player.SetPersistentVar( "fdTitanXP[" + titanRef + "]", FD_TitanGetPreviousXP( player, titanRef ) + fdXPamount )
+ int startingLevel = FD_TitanGetLevelForXP( titanRef, FD_TitanGetPreviousXP( player, titanRef ) )
+ int endingLevel = FD_TitanGetLevelForXP( titanRef, FD_TitanGetXP( player, titanRef ) )
+
+ Player_GiveFDUnlockPoints( player, endingLevel - startingLevel )
+
+ if( ProgressionEnabledForPlayer( player ) )
+ AwardRandomItemsForFDTitanLevels( player, titanRef, startingLevel, endingLevel )
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/weapon_xp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/weapon_xp.gnut
index 4e25e3019..0b0084b3c 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/weapon_xp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/weapon_xp.gnut
@@ -6,15 +6,22 @@ void function AddWeaponXP( entity player, int amount )
entity activeWeapon = player.GetActiveWeapon()
string weaponClassname = activeWeapon.GetWeaponClassName()
int oldLevel = WeaponGetLevel( player, weaponClassname )
+ int WeaponXPMatch = player.GetPersistentVarAsInt( "xp_match[" + XP_TYPE.WEAPON_LEVELED + "]" )
// increment xp
player.SetPersistentVar( GetItemPersistenceStruct( weaponClassname ) + ".weaponXP", min( WeaponGetXP( player, weaponClassname ) + amount, WeaponGetMaxXP( weaponClassname ) ) )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_WeaponXPAdded", shWeaponXP.weaponClassNames.find( weaponClassname ), WeaponGetXP( player, weaponClassname ), amount )
// level up notif
if ( WeaponGetLevel( player, weaponClassname ) != oldLevel )
{
Remote_CallFunction_NonReplay( player, "ServerCallback_WeaponLeveledUp", shWeaponXP.weaponClassNames.find( weaponClassname ), WeaponGetGen( player, weaponClassname ), WeaponGetLevel( player, weaponClassname ) )
AddPlayerScore( player, "WeaponLevelUp" )
+ IncrementPlayerChallengeWeaponLeveledUp( player )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.WEAPON_LEVELED + "]", WeaponXPMatch + 1 )
+
+ if( ProgressionEnabledForPlayer( player ) )
+ AwardRandomItemsForWeaponLevels( player, weaponClassname, oldLevel, WeaponGetLevel( player, weaponClassname ) )
}
// proscreen
diff --git a/README.md b/README.md
index 7b6dfaf02..4dbbd6edd 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Issues in this repository should be created if they are related to these domains
- `Northstar.Client` - Localisation files, UI and client-side scripts.
- `Northstar.Coop` - Soon™.
- `Northstar.Custom` - Northstar custom content.
-- `Northstar.CustomServer` - Server config files and scripts necessary for multiplayer.
+- `Northstar.CustomServers` - Server config files and scripts necessary for multiplayer.
### Translating