Compare commits

...

97 Commits

Author SHA1 Message Date
ab48db0206
Merge pull request #49 from zeno0207/master
fix Ext_TraitDemoProfessional
2024-09-14 18:14:38 +03:00
zeno0207
eaa67d7c97
Update Ext_TraitDemoProfessional.uc
fix error
2024-08-21 22:59:18 +08:00
8d8a20b755
fix ServerExt.chn encoding (utf8 -> utf16le) 2024-07-22 16:14:05 +03:00
0273fa9520
Merge pull request #47 from zeno0207/master
Update ServerExt.chn
2024-07-21 23:10:13 +03:00
zeno0207
873c9a37ee
Update ServerExt.chn
Update translation
2024-07-14 15:58:02 +08:00
b5597ce5f8
update ci/cd 2023-12-31 23:21:19 +03:00
55b82bbc3a
update dead links 2023-12-31 20:58:21 +03:00
84c5673b76
update ci/cd 2023-12-31 20:57:45 +03:00
d0e38b6ec8
update github gist link to make steam happy 2023-09-10 00:56:55 +03:00
58ec5f101e
fix code style 2023-09-10 00:06:06 +03:00
d5673b1887
Update README.md 2023-09-10 00:04:08 +03:00
ef199d64dd
add translations of new firebug perks for languages: chn, cht, esn, jpn, rus 2023-09-09 23:54:30 +03:00
b7468d4f7e
remove fake changes from ServerExt.int 2023-09-09 23:34:03 +03:00
fe47ba73e4
update KFServerExt.ini template 2023-09-09 23:23:22 +03:00
62e0ed28ee
Merge pull request #45 from GenZmeY/i18n-chn
add Simplified Chinese translation
2023-09-09 23:21:06 +03:00
e7916876d0
Merge pull request #42 from GenZmeY/group-names
add mutator group name
2023-09-09 23:20:56 +03:00
55567f1933
Merge pull request #40 from GenZmeY/v1137-bugsplat-fix
client-side "Access None" fixes
2023-09-09 23:20:46 +03:00
53b8933952
Merge pull request #44 from secretagentjr/master
Update Recoil / Spread to be flat reduction, capping out at 100%, allow knockback to go above 100%
2023-09-09 23:20:34 +03:00
f30a7fcf2a
fix EOL 2023-09-09 21:16:24 +03:00
99dacdf004
add Simplified Chinese translation 2023-09-09 19:28:17 +03:00
secretagentjr
1e7bcd732c Remove Function 2023-07-29 18:27:35 -07:00
secretagentjr
d38f165464 Revert Health back to regular 2023-07-29 17:24:31 -07:00
secretagentjr
daa0bd72e9 Add Some Firebug Traits 2023-07-23 17:43:50 -07:00
secretagentjr
53f63934b1 Fix Health Not Displaying Properly past 255 2023-07-20 12:18:08 -07:00
secretagentjr
b2d0d28946 Allow Knockdown "Knockback" to go above 100% 2023-07-18 18:06:15 -07:00
secretagentjr
0a8d4bb38c Update Recoil / Spread to be flat 2023-07-17 19:11:20 -07:00
6da2b47013 add mutator group name 2023-06-03 17:09:10 +03:00
06c326507e
Update README.md 2023-05-14 09:19:44 +03:00
64a4edf178
Merge pull request #41 from GenZmeY/MegaLinter
add Mega linter
2023-05-14 06:45:50 +03:00
67baea9f2c exclude upk 2023-05-14 06:37:40 +03:00
fbca6b98f5 fix indent 2023-05-14 06:08:37 +03:00
9c72778720 more style fixes 2023-05-14 06:01:17 +03:00
bababdc366 rm issue_template 2023-05-14 05:52:55 +03:00
2aad26f273 add MegaLinter badge 2023-05-14 05:51:51 +03:00
7175a6c7b6 fix style 2023-05-14 05:49:12 +03:00
47ed596664 upd mega-linter.yml 2023-05-14 05:26:15 +03:00
a57d5781d6 update README.md 2023-05-14 05:25:08 +03:00
6ccd5381fc add MegaLinter 2023-05-14 05:16:54 +03:00
97928b1d47 add .editorconfig 2023-05-14 05:15:46 +03:00
6df0b69568 update build tools to v1.9.2-1-gfb458ac 2023-05-14 05:14:52 +03:00
84ce5f6804 client-side "Access None" fixes 2022-12-25 01:31:24 +03:00
3387658793 update build tools 2022-10-13 23:23:23 +03:00
96438eb969 Merge branch 'master' into RemoveTrader 2022-09-16 06:36:36 +03:00
771f6708f1 fix admin status 2022-09-16 06:36:06 +03:00
19759b99e2 update build tools 2022-09-16 05:51:19 +03:00
a273c2607d remove custom items 2022-09-16 05:50:42 +03:00
5fd2af5f59 update build tools 2022-09-02 16:08:18 +03:00
4cdbf18761 update description 2022-08-30 08:50:36 +03:00
05fe3103c3 added instruction in case of missing configs 2022-08-30 08:11:11 +03:00
32febe3c41 AdminType vars replaced with enum 2022-08-30 07:42:20 +03:00
4ff4e092a9 remove unused config variable: bServerPerksMode 2022-07-12 19:36:33 +03:00
fd12729528 fix: Accessed None 'BestN' (Ext_T_GhostHelper:CanResPlayer)
ScriptWarning: Accessed None 'BestN'
011Ext_T_GhostHelper KF-KillingPool.TheWorld:PersistentLevel.Ext_T_GhostHelper_2
011Function ServerExt.Ext_T_GhostHelper:CanResPlayer:014C
2022-07-12 17:56:05 +03:00
431a429926 fix: Accessed None 'Data' (Ext_TraitSupply)
ScriptWarning: Accessed None 'Data'
011Ext_TraitSupply ServerExt.Default__Ext_TraitSupply
011Function ServerExt.Ext_TraitSupply:CancelEffectOn:0014
2022-07-12 17:48:08 +03:00
d804cdc3bd fix: Accessed None 'CurrentPerk' (ExtPlayerController:CheckPerk)
ScriptWarning: Accessed None 'CurrentPerk'
011ExtPlayerController KF-Crazy_Playboy_Edited.TheWorld:PersistentLevel.ExtPlayerController_1
011Function ServerExt.ExtPlayerController:CheckPerk:00B3
2022-07-12 17:43:51 +03:00
bec774b38c fix: Accessed None 'OwnerController' (Ext_AICommandBasePet:ZedBaseCommand)
ScriptWarning: Accessed None 'OwnerController'
011Ext_AICommandBasePet KF-AnimeDance.TheWorld:PersistentLevel.KFAIController_ZedHusk_0.Ext_AICommandBasePet_0
011State ServerExt.Ext_AICommandBasePet:ZedBaseCommand:047B
2022-07-12 17:39:58 +03:00
de4b7ff714 fix: Accessed None 'Pawn' (ServerExtMut:NetDamage)
ScriptWarning: Accessed None 'Pawn'
011ServerExtMut KF-TrapLandAlter.TheWorld:PersistentLevel.ServerExtMut_0
011Function ServerExtMut.ServerExtMut:NetDamage:0344
2022-07-12 17:34:14 +03:00
f729eebe51 fix: ScriptWarning: Accessed None 'Killer'
ScriptWarning: Accessed None 'Killer'
011ServerExtMut KF-TrapLandAlter.TheWorld:PersistentLevel.ServerExtMut_0
011Function ServerExtMut.ServerExtMut:ScoreKill:00BF
2022-07-12 17:27:51 +03:00
54fd79c924 ref: CustomXP function, fix: DamageTypes out of bounds
- CustomXP function refactoring;
- fix "out of bounds" error:
ScriptWarning: Accessed array 'ServerExtMut_0.DamageTypes' out of bounds (2/2)
011ServerExtMut KF-BigRoom2020.TheWorld:PersistentLevel.ServerExtMut_0
011Function ServerExtMut.ServerExtMut:CustomXP:01CB
2022-07-12 17:12:33 +03:00
cb8b9d52b3 update description 2022-07-11 09:42:08 +03:00
0c3e79a7cd update build tools 2022-07-11 09:41:53 +03:00
5dffb9edea fix exp gain for custom zeds 2022-05-18 08:56:26 +03:00
46c85f41a3 update build tools 2022-05-18 08:40:57 +03:00
c6ff45af5c Update build tools 2022-03-03 21:22:08 +03:00
c1bc9cf7cc Update build tools 2022-03-03 20:48:02 +03:00
bf4b6954ea update deps 2022-01-24 07:40:43 +03:00
09ba47334f
Merge pull request #37 from GenZmeY/localization-ESN
Spanish localization
2022-01-16 09:46:28 +03:00
48f192e17b
Merge pull request #38 from GenZmeY/buildsystem
New build system
2022-01-16 09:45:48 +03:00
91a7bed8f2 add Jason Voorehes to Translators 2022-01-16 07:58:11 +03:00
9e42544b35 update builder 2022-01-16 07:45:18 +03:00
ce188a0074 remove outdated make.sh and update README.md 2022-01-16 06:06:22 +03:00
6f31b8ed01 add Spanish localization 2022-01-16 05:11:30 +03:00
d71572d5e8 add new build system 2022-01-16 05:09:46 +03:00
b037fd2215 move upk to right place 2022-01-13 01:24:58 +03:00
decfcb9d28
Add contributors to README.md
dunno why the hell they are not displayed on the right, but this widget works correctly
2021-08-24 18:52:19 +03:00
a0c5238e60 Update credits 2021-08-24 05:09:49 +03:00
45aa4d6735 Merge branch 'weaponswitch' 2021-08-24 04:59:57 +03:00
6640566c45 fix typo after resolving merge conflicts 2021-08-24 04:59:29 +03:00
b6386ff4f1
Merge pull request #35 from secretagentjr/weaponswitch
weaponswitch speed
2021-08-24 04:48:23 +03:00
637e3675a5
Merge branch 'master' into weaponswitch 2021-08-24 04:48:08 +03:00
d8a19104dc Add WeaponSwitch stat to KFServerExt.ini 2021-08-24 04:06:29 +03:00
80d0c0cc0a Update localization files (cht, jpn, rus) 2021-08-24 03:29:06 +03:00
d7064608c2 Code style 2021-08-24 03:18:38 +03:00
cd0d0db1f8
Merge pull request #34 from secretagentjr/knockdown
make knockdown stat push enemies back similar to heat wave or berserker weapons
2021-08-24 02:27:15 +03:00
683b1e9d93
Code style 2021-08-24 02:26:47 +03:00
1c12cc55a7
Code style 2021-08-24 02:25:27 +03:00
febf439541
Merge pull request #32 from secretagentjr/test
Incorporate Camera View Shake Reduction Into Recoil Reduction
2021-08-24 02:04:56 +03:00
59e99903d3
Style 2021-08-24 02:04:28 +03:00
bae438531c
Style 2021-08-24 02:03:16 +03:00
597dd74851
Merge pull request #33 from secretagentjr/test1
Fix Spread not working on shotguns(Support,Medic) etc
2021-08-24 01:49:53 +03:00
fc98f94b74
Merge pull request #36 from Scaredkid93/patch-1
Update Ext_TraitWPGuns.uc
2021-08-24 01:31:15 +03:00
nateku2
c33116b97c
Update Ext_TraitWPGuns.uc
It never made sense to me that, It only gave you a single pistol considering gunslinger dual wields most of the time.
2021-08-23 06:05:31 -04:00
unknown
d750a2a6d0 weaponswitch speed 2021-08-21 10:53:27 -07:00
unknown
e3df831773 fix perkmanager tieing stumblepower to stunpower 2021-08-21 10:23:39 -07:00
unknown
754262c61d make knockdown stat stumble enemies in addition 2021-08-21 10:14:37 -07:00
unknown
b550ba56e7 Fix Spread not working on shotguns 2021-08-21 09:56:06 -07:00
unknown
0656855070 test 2021-08-21 07:25:27 -07:00
d36068787a update steam description 2021-07-04 00:28:02 +03:00
208 changed files with 1834 additions and 2658 deletions

33
.editorconfig Normal file
View File

@ -0,0 +1,33 @@
root = true
# Global
[*]
indent_style = unset
indent_size = 4
tab_width = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = unset
# Unreal Engine 3 / Source
[*.uc]
indent_style = tab
[*.{uci,upkg}]
# Unreal Engine 3 / i18n
[*.{chn,cht,cze,dan,deu,dut,esl,esn,fra,frc,hun,int,ita,jpn,kor,pol,por,ptb,rus,tur,ukr}]
charset = utf-16le
# Other
[*.md]
indent_style = space
trim_trailing_whitespace = false
[*.yml]
indent_style = space
indent_size = 2
[*.{txt,cfg,conf}]
indent_style = tab

View File

@ -1,8 +0,0 @@
### Details
Tell us exactly what happens, every single detail that you can think of
### Screenshots
Provide a screenshot of the issue so we can debug it's not a user issue
### Steps to reproduce
Tell us how to reproduce the problem, step by step

116
.github/workflows/mega-linter.yml vendored Normal file
View File

@ -0,0 +1,116 @@
---
name: MegaLinter
permissions: read-all
on:
push:
pull_request:
branches:
- master
env:
APPLY_FIXES: none
APPLY_FIXES_EVENT: pull_request
APPLY_FIXES_MODE: commit
FILTER_REGEX_EXCLUDE: (mega-linter.yml)
DISABLE: SPELL
DISABLE_ERRORS_LINTERS: MARKDOWN_MARKDOWN_LINK_CHECK
EDITORCONFIG_EDITORCONFIG_CHECKER_FILTER_REGEX_EXCLUDE: 'xVoteAnnouncer.upk'
concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true
jobs:
megalinter:
name: MegaLinter
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
steps:
- name: Checkout Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: MegaLinter
uses: oxsecurity/megalinter@7e042c726c68415475b05a65a686c612120a1232
id: ml
env:
VALIDATE_ALL_CODEBASE: true
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Archive production artifacts
uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392
if: success() || failure()
with:
name: MegaLinter reports
path: |
megalinter-reports
mega-linter.log
- name: Set APPLY_FIXES_IF var
run: |
printf 'APPLY_FIXES_IF=%s\n' "${{
steps.ml.outputs.has_updated_sources == 1 &&
(
env.APPLY_FIXES_EVENT == 'all' ||
env.APPLY_FIXES_EVENT == github.event_name
) &&
(
github.event_name == 'push' ||
github.event.pull_request.head.repo.full_name == github.repository
)
}}" >> "${GITHUB_ENV}"
- name: Set APPLY_FIXES_IF_* vars
run: |
printf 'APPLY_FIXES_IF_PR=%s\n' "${{
env.APPLY_FIXES_IF == 'true' &&
env.APPLY_FIXES_MODE == 'pull_request'
}}" >> "${GITHUB_ENV}"
printf 'APPLY_FIXES_IF_COMMIT=%s\n' "${{
env.APPLY_FIXES_IF == 'true' &&
env.APPLY_FIXES_MODE == 'commit' &&
(!contains(fromJSON('["refs/heads/main", "refs/heads/master"]'), github.ref))
}}" >> "${GITHUB_ENV}"
- name: Create Pull Request with applied fixes
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38
id: cpr
if: env.APPLY_FIXES_IF_PR == 'true'
with:
token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }}
commit-message: "[MegaLinter] Apply linters automatic fixes"
title: "[MegaLinter] Apply linters automatic fixes"
labels: bot
- name: Create PR output
if: env.APPLY_FIXES_IF_PR == 'true'
run: |
echo "PR Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "PR URL - ${{ steps.cpr.outputs.pull-request-url }}"
- name: Prepare commit
if: env.APPLY_FIXES_IF_COMMIT == 'true'
run: sudo chown -Rc $UID .git/
- name: Commit and push applied linter fixes
uses: stefanzweifel/git-auto-commit-action@8756aa072ef5b4a080af5dc8fef36c5d586e521d
if: env.APPLY_FIXES_IF_COMMIT == 'true'
with:
branch: >-
${{
github.event.pull_request.head.ref ||
github.head_ref ||
github.ref
}}
commit_message: "[MegaLinter] Apply linters fixes"
commit_user_name: "github-actions"
commit_user_email: "github-actions[bot]@users.noreply.github.com"

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.psd
/ignore

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "tools"]
path = tools
url = https://github.com/GenZmeY/KF2-BuildTools

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,7 @@
[img]https://raw.githubusercontent.com/GenZmeY/KF2-Server-Extension/master/PublicationContent/mutbanner.png[/img]
[img]https://img.shields.io/static/v1?logo=GitHub&labelColor=gray&color=blue&logoColor=white&label=&message=Open Source[/img] [img]https://img.shields.io/github/license/GenZmeY/KF2-Server-Extension[/img] [img]https://img.shields.io/steam/downloads/2085786712[/img] [img]https://img.shields.io/steam/favorites/2085786712[/img] [url=https://steamcommunity.com/sharedfiles/filedetails/changelog/2085786712] [img]https://img.shields.io/steam/update-date/2085786712[/img] [img]https://img.shields.io/github/v/tag/GenZmeY/KF2-Server-Extension[/img][/url]
[h1]Description:[/h1]
[i]This mod replaces current perk system in Killing Floor 2 with a serverside perk progression with RPG elements, which let you buy individual stats and traits.
@ -19,21 +21,27 @@ This is a further development of the ServerExt mutator from [url=https://steamco
[/list]
The full changelog is available [url=https://steamcommunity.com/sharedfiles/filedetails/changelog/2085786712]here[/url].
[h1]Usage (single player):[/h1]
1. Subscribe to this mutator;
2. Start KF2;
3. Open console (`) and input:
[h1]Usage (single player)[/h1]
[olist]
[*]Subscribe to this mutator;
[*]Start KF2;
[*]Open console (`) and input:
[code]open KF-BioticsLab?Mutator=ServerExtMut.ServerExtMut[/code]
(You can add some options or replace KF-BioticsLab with the map you want)
[/olist]
[h1]Usage (server):[/h1]
[b]Note:[/b] If you don't understand what is written here, read the article [url=https://wiki.killingfloor2.com/index.php?title=Dedicated_Server_(Killing_Floor_2)][u]Dedicated Server (KF2 wiki)[/u][/url] before following these instructions.
1. Open your PCServer-KFEngine.ini / LinuxServer-KFEngine.ini;
2. Add the following string to the [b][OnlineSubsystemSteamworks.KFWorkshopSteamworks][/b] section:
[code]ServerSubscribedWorkshopItems=2085786712[/code]
3. Start the server and wait while the mutator is downloading;
4. Add [b]?Mutator=ServerExtMut.ServerExtMut[/b] above to launchup URL and restart server.
[h1]Usage (server)[/h1]
[b]Note:[/b] [i]If you don't understand what is written here, read the article [url=https://wiki.killingfloor2.com/index.php?title=Dedicated_Server_(Killing_Floor_2)][u]Dedicated Server (KF2 wiki)[/u][/url] before following these instructions.[/i]
[olist]
[*]Open your [b]PCServer-KFEngine.ini[/b] / [b]LinuxServer-KFEngine.ini[/b];
[*]Find the [b][IpDrv.TcpNetDriver][/b] section and make sure that there is a line (add if not):
[b]DownloadManagers=OnlineSubsystemSteamworks.SteamWorkshopDownload[/b]
❗️ If there are several [b]DownloadManagers=[/b] then the line above should be the first ❗️
[*]Add the following string to the [b][OnlineSubsystemSteamworks.KFWorkshopSteamworks][/b] section (create one if it doesn't exist):
[b]ServerSubscribedWorkshopItems=2085786712[/b]
[*]Start the server and wait while the mutator is downloading;
[*]Add mutator to server start parameters: [b]?Mutator=ServerExtMut.ServerExtMut[/b] and restart the server.
[/olist]
[h1]Mutator setup[/h1]
[url=https://steamcommunity.com/workshop/filedetails/discussion/2085786712/4241773359646835902/][u]Look here[/u][/url]
@ -42,7 +50,7 @@ The full changelog is available [url=https://steamcommunity.com/sharedfiles/file
It will be more efficient to use ServerExt together with other mutators (which add new zeds or game modes).
You can use [u][url=https://steamcommunity.com/workshop/filedetails/discussion/2085786712/4241773359647296750/]my experience[/url][/u] or come up with something of your own.
[h1]If not working[/h1]
[h1]If not working or you have no text[/h1]
Older versions of Server-Ext have the same file names - this can cause conflicts if you subscribe to them. Therefore:
1. Check your subscriptions. If you are subscribed to old versions of the mutator, cancel them.
2. Clear the cache (the game doesn't clear it). It's here:
@ -60,7 +68,32 @@ If you do not want to download the subscriptions again, delete only those folder
ServerExt.u
ServerExtMut.u
[/code]
3. Reinstall ServerExt and try again.
3. Delete all ServerExt localization files. It's here:
[code]
Client:
C:\Users\<YOUR_USERNAME>\Documents\My Games\KillingFloor2\KFGame\Localization\
[/code]
Check all folders and delete all ServerExt.* files you find.
4. Re-subscribe to this mod and try again.
[h1]If configs are not created[/h1]
[olist]
[*]Create the missing configs manually, then add the following content to them ([u]only for configs that you created manually[/u]):
[*]Put in [b]KFServerExtMut.ini[/b] this:
[b][ServerExtMut.ServerExtMut]
SettingsInit=0[/b]
[*]Put in [b]KFxMapVote.ini[/b] this:
[b][ServerExtMut.xVotingHandler]
ConfigVersion=0[/b]
[*]Put in [b]KFServerExt.ini[/b] this: [url=https://gist.githubusercontent.com/GenZmeY/616b6f845f9829c93976f42f6c48505a/raw/1a8c8409d61a2908d5c854ced0932b8ed43a7f51/KFServerExt-Default.ini]<github gist>[/url] [b](*)[/b]
[*]Start the game/server along with the ServerExt, then check the configs - content should be generated.
[/olist]
[b](*)[/b]Thanks to [url=https://steamcommunity.com/profiles/76561198105638699]Cloud[/url] for this file and instructions!
[h1]Bug reports[/h1]
If you find a bug, please create a new issue here:
@ -73,17 +106,23 @@ ServerExt supports localization now!
If you have a desire to help translate ServerExt into other languages, you can read how to do it in this thread: [u][url=https://steamcommunity.com/workshop/filedetails/discussion/2085786712/2942494909176752884]Localization[/url][/u]
[h1]Credits:[/h1]
[url=https://steamcommunity.com/profiles/76561197975509070]Marco[/url] - creating a mutator;
[url=https://steamcommunity.com/profiles/76561197997881512]ForrestMarkX[/url] - updates, fixes, features;
[url=https://steamcommunity.com/profiles/76561198143883210]inklesspen[/url] - updates, fixes, features;
[url=https://steamcommunity.com/profiles/76561198001617867]GenZmeY[/url] - description, localization, fixes and features;
[url=https://www.deviantart.com/cottonvalent]CottonValent[/url] - artist who created gorgeous cat on the preview (I dont know why he is here, it happened).
[h1]Credits[/h1]
[url=https://steamcommunity.com/profiles/76561197975509070]Marco[/url]
[url=https://steamcommunity.com/profiles/76561197997881512]ForrestMarkX[/url]
[url=https://steamcommunity.com/profiles/76561198143883210]inklesspen[/url]
[url=https://steamcommunity.com/profiles/76561198001617867]GenZmeY[/url]
[url=https://steamcommunity.com/profiles/76561198042219365]secret[/url]
[url=https://steamcommunity.com/profiles/76561198004271210]Scaredkid93[/url]
[url=https://www.deviantart.com/cottonvalent]CottonValent[/url][b](*)[/b]
[b]Translators:[/b]
[b]Translators[/b]
[url=https://steamcommunity.com/profiles/76561198001617867]GenZmeY[/url] - Russian [RUS];
[url=https://steamcommunity.com/profiles/76561197967822972]MilkTeaFox[/url] - Traditional Chinese [CHT];
[url=https://steamcommunity.com/profiles/76561198930643117]あさぴっぴ1020[/url] - Japanese [JPN];
[url=https://steamcommunity.com/profiles/76561198080866131]Jason Voorehes [/url] - Spanish [ESN];
[url=https://steamcommunity.com/profiles/76561198200630473/]小曦曦[/url] - Simplified Chinese [CHN];
[h1]Sources:[/h1]
[u]https://github.com/GenZmeY/KF2-Server-Extension[/u] [b](GNU GPLv3)[/b]
[h1]Sources[/h1]
[url=https://github.com/GenZmeY/KF2-Server-Extension]https://github.com/GenZmeY/KF2-Server-Extension[/url] [b](GNU GPLv3)[/b]
[b](*)[/b] CottonValent - is the artist who created gorgeous cat on the preview (I dont know why he is here, it happened).

View File

@ -1,9 +1,11 @@
[![](PublicationContent/mutbanner.png)](https://steamcommunity.com/sharedfiles/filedetails/?id=2085786712)
# Server Extension
[![Banner](PublicationContent/mutbanner.png)](https://steamcommunity.com/sharedfiles/filedetails/?id=2085786712)
[![Steam Workshop](https://img.shields.io/static/v1?message=workshop&logo=steam&labelColor=gray&color=blue&logoColor=white&label=steam%20)](https://steamcommunity.com/sharedfiles/filedetails/?id=2085786712)
[![Steam Subscriptions](https://img.shields.io/steam/subscriptions/2085786712)](https://steamcommunity.com/sharedfiles/filedetails/?id=2085786712)
[![Steam Downloads](https://img.shields.io/steam/downloads/2085786712)](https://steamcommunity.com/sharedfiles/filedetails/?id=2085786712)
[![Steam Favorites](https://img.shields.io/steam/favorites/2085786712)](https://steamcommunity.com/sharedfiles/filedetails/?id=2085786712)
[![Steam Update Date](https://img.shields.io/steam/update-date/2085786712)](https://steamcommunity.com/sharedfiles/filedetails/?id=2085786712)
[![MegaLinter](https://github.com/GenZmeY/KF2-Server-Extension/actions/workflows/mega-linter.yml/badge.svg?branch=master)](https://github.com/GenZmeY/KF2-Server-Extension/actions/workflows/mega-linter.yml)
[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/GenZmeY/KF2-Server-Extension)](https://github.com/GenZmeY/KF2-Server-Extension/tags)
[![GitHub top language](https://img.shields.io/github/languages/top/GenZmeY/KF2-Server-Extension)](https://docs.unrealengine.com/udk/Three/WebHome.html)
[![GitHub](https://img.shields.io/github/license/GenZmeY/KF2-Server-Extension)](LICENSE)
@ -14,7 +16,7 @@
*This is a further development of the ServerExt mutator from [Marco](https://forums.tripwireinteractive.com/index.php?threads/mutator-server-extension-mod.109463) and [Forrest Mark X](https://github.com/ForrestMarkX/KF2-Server-Extension).*
# Features
## Features
- RPG elements (traits and stats);
- New menu system;
- Scoreboard that supports unlimited playercount on server;
@ -31,53 +33,40 @@ The full changelog is available on [steam workshop](https://steamcommunity.com/s
**Note:** If you want to build/test/brew/publish a mutator without git-bash and/or scripts, follow [these instructions](https://tripwireinteractive.atlassian.net/wiki/spaces/KF2SW/pages/26247172/KF2+Code+Modding+How-to) instead of what is described here.
# Build
## Build
1. Install [Killing Floor 2](https://store.steampowered.com/app/232090/Killing_Floor_2/), Killing Floor 2 - SDK and [git for windows](https://git-scm.com/download/win);
2. Open git-bash in the folder: `C:\Users\<USERNAME>\Documents\My Games\KillingFloor2\KFGame`
2. open git-bash and go to any folder where you want to store ServerExt sources:
`cd <ANY_FOLDER_YOU_WANT>`
3. Clone this repository and go to the source folder:
`git clone https://github.com/GenZmeY/KF2-Server-Extension && cd KF2-Server-Extension`
4. Run make.sh script:
`./make.sh --compile`
4. Download ServerExt dependencies:
`git submodule init && git submodule update`
5. Compile ServerExt:
`./tools/builder -c`
5. The compiled files will be here:
`C:\Users\<USERNAME>\Documents\My Games\KillingFloor2\KFGame\Unpublished\BrewedPC\Script\`
# Testing
You can check your build using the `make.sh` script.
Open git-bash in the source folder and run the script:
`./make.sh --test`
On first launch, the script will create `testing.ini` file and launch the game with the settings from it (KF-Nuked map + ServerExtMut). Edit this file if you need to test the mutator with different parameters.
# Using and configuring ServerExt
## Using and configuring ServerExt
A detailed manual is available on the [mod page](https://steamcommunity.com/sharedfiles/filedetails/?id=2085786712) in the steam workshop.
# Publication in steam workshop
1. Modify the publish files if necessary, they are here:
`C:\Users\<USERNAME>\Documents\My Games\KillingFloor2\KFGame\Src\PublicationContent\`
> description.txt
> preview.png
> tags.txt
> title.txt
2. Run this command in the source folder:
`./make.sh -c && ./make.sh -bu && ./make.sh -u`
# Contributing
## Contributing
**Participation is welcome!**
## Bug reports
### Bug reports
If you find a bug, go to the [issue page](https://github.com/GenZmeY/KF2-Server-Extension/issues) and check if there is a description of your bug. If not, create a new issue.
Describe what the bug looks like and how we can reproduce it.
Attach screenshots if you think it might help.
If it's a crash issue, be sure to include the `Launch.log` and `Launch_2.log` files. You can find them here:
If it's a crash issue, be sure to include the `Launch.log` file. You can find them here:
`C:\Users\<USERNAME>\Documents\My Games\KillingFloor2\KFGame\Logs\`
Please note that these files are overwritten every time you start the game/server. Therefore, you must take these files immediately after the game crashes in order not to lose information.
## Localization
### Localization
The mutator supports localization and you can help translate it into other languages.
It does not require any special knowledge or programming skills, so you just need to know the language into which you will translate.
Here's a quick guide on how to do it: [localization guide](https://steamcommunity.com/workshop/filedetails/discussion/2085786712/2942494909176752884)
## Contribute code
### Contribute code
You can help improve ServerExt by fixing bugs and adding new features.
Before making a pull request, make sure that:
1. Your code is working correctly.
@ -86,10 +75,27 @@ Before making a pull request, make sure that:
In the description of the pull request, describe the changes you made.
# License
[GNU GPLv3](LICENSE)
## Contributors
[![ServerExt Contributors](https://contrib.rocks/image?repo=GenZmeY/KF2-Server-Extension)](https://github.com/GenZmeY/KF2-Server-Extension/graphs/contributors)
[Marco](https://steamcommunity.com/profiles/76561197975509070)
[ForrestMarkX](https://steamcommunity.com/profiles/76561197997881512)
[inklesspen](https://steamcommunity.com/profiles/76561198143883210)
[GenZmeY](https://steamcommunity.com/profiles/76561198001617867)
[secret](https://steamcommunity.com/profiles/76561198042219365)
[Scaredkid93](https://steamcommunity.com/profiles/76561198004271210)
**Translators:**
[GenZmeY](https://steamcommunity.com/profiles/76561198001617867) - Russian [RUS];
[MilkTeaFox](https://steamcommunity.com/profiles/76561197967822972) - Traditional Chinese [CHT];
[あさぴっぴ1020](https://steamcommunity.com/profiles/76561198930643117) - Japanese [JPN];
[Jason Voorehes ](https://steamcommunity.com/profiles/76561198080866131) - Spanish [ESN];
[小曦曦](https://steamcommunity.com/profiles/76561198200630473) - Simplified Chinese [CHN].
## License
[![license](https://www.gnu.org/graphics/gplv3-with-text-136x68.png)](LICENSE)
***
**Note about the banner:**
The cat in the picture is [meowbin](https://www.deviantart.com/cottonvalent/gallery/48815375/creepy-cat). And [Cotton Valent](https://www.deviantart.com/cottonvalent) is the artist who designed and painted this magnificent cat.
The cat in the picture is [meowbin](https://twitter.com/meawbinneko). And [Cotton Valent](https://twitter.com/horrormove) is the artist who designed and painted this magnificent cat.

View File

@ -46,4 +46,5 @@ final function SaveData(FMyCustomChar R)
defaultproperties
{
}

View File

@ -2,7 +2,7 @@ class ExtDT_Ballistic_9mm extends KFDT_Ballistic_9mm
abstract
hidedropdown;
DefaultProperties
defaultproperties
{
ModifierPerkList(0) = class'KFPerk_Sharpshooter'
ModifierPerkList(1) = class'KFPerk_Gunslinger'

View File

@ -2,7 +2,7 @@ class ExtDT_Ballistic_Pistol_Medic extends KFDT_Ballistic_Pistol_Medic
abstract
hidedropdown;
DefaultProperties
defaultproperties
{
ModifierPerkList(0) = class'KFPerk_Sharpshooter'
ModifierPerkList(1) = class'KFPerk_Gunslinger'

View File

@ -18,10 +18,6 @@ static final function name GetUnlockedEmote(int ItemId, ExtPlayerController PC)
if (PRI == None)
return Anim;
/*
if (InStr(string(Anim), "Deluxe") != INDEX_NONE && PRI.AdminType == 255)
return 'NONE';
*/
else return Anim;
}

View File

@ -5,6 +5,7 @@ protected function SpecialCringeEffectsfor (Actor Victim, float VictimDist)
return;
}
DefaultProperties
defaultproperties
{
}

View File

@ -0,0 +1,28 @@
class ExtHUD_ObjectiveConatiner extends KFGFxHUD_ObjectiveConatiner;
simulated function SetActive(bool bActive)
{
if (bActive)
{
Super.SetActive(bActive);
}
else
{
SetVisible(bActive);
CurrentObjectiveInterface = none;
// Fix:
// ScriptWarning: Accessed None
// KFGFxHUD_ObjectiveConatiner Transient.ExtMoviePlayer_HUD_0:KFGFxHUD_ObjectiveConatiner_0
// Function KFGame.KFGFxHUD_ObjectiveConatiner:SetActive:01D9
if (GetObject("objectiveNumberMC") != None)
{
GetObject("objectiveNumberMC").SetVisible(false);
}
}
}
DefaultProperties
{
}

View File

@ -29,6 +29,7 @@ function UpdateGrenades()
}
}
DefaultProperties
defaultproperties
{
}

View File

@ -44,4 +44,5 @@ function UpdatePlayerInfo(optional bool bForceUpdate)
defaultproperties
{
}

View File

@ -86,6 +86,7 @@ simulated function SetWeaponGroupList(out array<KFWeapon> WeaponList, byte Group
SetWeaponList(DataProvider, GroupIndex);
}
DefaultProperties
defaultproperties
{
}

View File

@ -2,7 +2,6 @@ class ExtMenu_Gear extends KFGFxObject_Menu;
var ExtPlayerReplicationInfo ExtPRI;
// var KFGFxObject_TraderItems TraderItems;
var KFGFxGearContainer_PerksSelection PerkSelectionContainer;
var KFCharacterInfo_Human CurrentCharInfo;
var string CharInfoPath;
@ -19,7 +18,6 @@ function InitializeMenu(KFGFxMoviePlayer_Manager InManager)
LocalizeText();
EmoteList = class'ExtEmoteList'.static.GetEmoteArray();
InitCharacterMenu();
// TraderItems = KFGameReplicationInfo(GetPC().WorldInfo.GRI).TraderItems;
}
function InitCharacterMenu()
@ -69,7 +67,6 @@ function OnOpen()
return;
CheckForCustomizationPawn(PC);
//GetGameViewportClient().HandleInputAxis = OnAxisModified;
if (PC.PlayerReplicationInfo.bReadyToPlay && PC.WorldInfo.GRI.bMatchHasBegun)
{

View File

@ -62,4 +62,5 @@ function Callback_Equip(int ItemDefinition)
defaultproperties
{
}

View File

@ -93,6 +93,20 @@ final function ShowKillMessageX(PlayerReplicationInfo PRI1, PlayerReplicationInf
}
}
function UpdateObjectiveActive()
{
// Fix:
// ScriptWarning: Accessed None 'KFGRI'
// ExtMoviePlayer_HUD Transient.ExtMoviePlayer_HUD_0
// Function KFGame.KFGFxMoviePlayer_HUD:UpdateObjectiveActive:00B7
if (GetPC() == None || KFGameReplicationInfo(GetPC().WorldInfo.GRI) == None)
{
return;
}
Super.UpdateObjectiveActive();
}
defaultproperties
{
WidgetBindings.Remove((WidgetName="SpectatorInfoWidget",WidgetClass=class'KFGFxHUD_SpectatorInfo'))
@ -103,4 +117,6 @@ defaultproperties
WidgetBindings.Add((WidgetName="PlayerBackpackWidget",WidgetClass=class'ExtHUD_PlayerBackpack'))
WidgetBindings.Remove((WidgetName="WeaponSelectContainer",WidgetClass=class'KFGFxHUD_WeaponSelectWidget'))
WidgetBindings.Add((WidgetName="WeaponSelectContainer",WidgetClass=class'ExtHUD_WeaponSelectWidget'))
WidgetBindings.Remove((WidgetName="ObjectiveContainer",WidgetClass=class'KFGFxHUD_ObjectiveConatiner'))
WidgetBindings.Add((WidgetName="ObjectiveContainer",WidgetClass=class'ExtHUD_ObjectiveConatiner'))
}

View File

@ -111,6 +111,22 @@ function OnMenuOpen(name WidgetPath, KFGFxObject_Menu Widget)
PerksMenu.ActionScriptVoid("closeContainer");
}
event OnClose()
{
// Fix:
// ScriptWarning: Accessed None 'CurrentBackgroundMovie'
// ExtMoviePlayer_Manager KF-BIOTICSLAB.TheWorld:PersistentLevel.ExtPlayerController_0.ExtMoviePlayer_Manager_0
// Function KFGame.KFGFxMoviePlayer_Manager:OnClose:0039
if (CurrentBackgroundMovie != None)
{
Super.OnClose();
}
else
{
CloseMenus();
}
}
defaultproperties
{
InGamePartyWidgetClass=class'ExtWidget_PartyInGame'

View File

@ -98,7 +98,6 @@ function AttachWeaponByItemDefinition(int ItemDefinition)
//setweapon skin
WeaponAttachment.SetWeaponSkin(ItemDefinition);
}
defaultproperties

View File

@ -7,4 +7,5 @@ static simulated event bool IsABoss()
defaultproperties
{
}

View File

@ -7,7 +7,6 @@ Class ExtPerkManager extends KFPerk;
enum EReplicateState
{
REP_CustomCharacters,
REP_CustomInventory,
REP_PerkClasses,
REP_Done
};
@ -451,14 +450,6 @@ function ReplicateTimer()
++RepIndex;
}
break;
case REP_CustomInventory: // Replicate custom trader inventory
if (!PRIOwner.OnRepNextItem(PRIOwner,RepIndex))
{
RepIndex = 0;
++RepState;
}
else ++RepIndex;
break;
case REP_PerkClasses: // Open up all actor channel connections.
if (RepIndex>=UserPerks.Length)
{
@ -516,6 +507,11 @@ simulated function ModifyRecoil(out float CurrentRecoilModifier, KFWeapon KFW)
CurrentPerk.ModifyRecoil(CurrentRecoilModifier,KFW);
}
simulated function float GetCameraViewShakeModifier(KFWeapon KFW)
{
return (CurrentPerk!=None ? CurrentPerk.GetCameraViewShakeModifier(KFW) : 1.f);
}
simulated function ModifySpread(out float InSpread)
{
if (CurrentPerk!=None)
@ -557,7 +553,7 @@ function float GetKnockdownPowerModifier(optional class<DamageType> DamageType,
function float GetStumblePowerModifier(optional KFPawn KFP, optional class<KFDamageType> DamageType, optional out float CooldownModifier, optional byte BodyPart)
{
return (CurrentPerk!=None ? CurrentPerk.GetKnockdownPowerModifier() : 1.f);
return (CurrentPerk!=None ? CurrentPerk.GetStumblePowerModifier() : 1.f);
}
function float GetStunPowerModifier(optional class<DamageType> DamageType, optional byte HitZoneIdx)
@ -894,6 +890,18 @@ simulated function float GetPenetrationModifier(byte Level, class<KFDamageType>
return (Ext_PerkSupport(CurrentPerk)!=None ? Ext_PerkSupport(CurrentPerk).GetPenetrationModifier(Level, DamageType, bForce) : 0.f);
}
simulated function float GetTightChokeModifier()
{
return (CurrentPerk!=None ? CurrentPerk.GetTightChokeModifier() : 1.f);
}
// SwitchSpeed
simulated function ModifyWeaponSwitchTime(out float ModifiedSwitchTime)
{
if (CurrentPerk != None)
CurrentPerk.ModifyWeaponSwitchTime(ModifiedSwitchTime);
}
// Other
function ApplySkillsToPawn()
{

View File

@ -91,15 +91,18 @@ simulated function Destroyed()
function CheckPerk()
{
if (CurrentPerk!=ActivePerkManager)
if (CurrentPerk != ActivePerkManager)
{
CurrentPerk = ActivePerkManager;
if (KFPlayerReplicationInfo(PlayerReplicationInfo)!=None)
{
KFPlayerReplicationInfo(PlayerReplicationInfo).NetPerkIndex = 0;
if (ActivePerkManager.CurrentPerk != None)
{
KFPlayerReplicationInfo(PlayerReplicationInfo).CurrentPerkClass = ActivePerkManager.CurrentPerk.BasePerk;
}
}
}
}
reliable client function AddAdminCmd(string S)

View File

@ -18,29 +18,26 @@ struct FMyCustomChar // Now without constant.
}
};
// For custom trader inventory.
struct FCustomTraderItem
{
var class<KFWeaponDefinition> WeaponDef;
var class<KFWeapon> WeaponClass;
};
var bool bIsMuted,bInitialPT,bIsDev,bHiddenUser,bClientUseCustom,bClientFirstChar,bClientCharListDone,bClientInitChars;
enum E_AdminType
{
AT_Global,
AT_Admin,
AT_Mod,
AT_TMem,
AT_VIP,
AT_Booster,
AT_Player
};
var E_AdminType AdminType;
var int RespawnCounter;
var byte AdminType;
var class<Ext_PerkBase> ECurrentPerk;
var Ext_PerkBase FCurrentPerk;
var int ECurrentPerkLevel,ECurrentPerkPrestige;
var ExtPerkManager PerkManager;
/* AdminTypes:
0 - Super Admin (server owner)
1 - Admin
2 - Moderator
3 - Trusted member
4 - VIP
*/
var string TaggedPlayerName;
var repnotify string NameTag;
var repnotify byte RepLevelProgress;
@ -54,10 +51,6 @@ var repnotify FMyCustomChar CustomCharacter;
var transient array<ExtCharDataInfo> SaveDataObjects;
var transient ExtPlayerReplicationInfo LocalOwnerPRI; // Local playercontroller owner PRI
// Custom trader inventory
var KFGFxObject_TraderItems CustomList;
var array<FCustomTraderItem> CustomItems;
// Supplier data:
var transient struct FSupplierData
{
@ -233,118 +226,28 @@ function UnsetPT()
bInitialPT = false;
}
Delegate bool OnRepNextItem(ExtPlayerReplicationInfo PRI, int RepIndex)
{
return false;
}
simulated reliable client function ClientAddTraderItem(int Index, FCustomTraderItem Item)
{
// Make sure to not execute on server.
if (WorldInfo.NetMode!=NM_Client && (PlayerController(Owner)==None || LocalPlayer(PlayerController(Owner).Player)==None))
return;
if (CustomList==None)
{
CustomList = CreateNewList();
RecheckGRI();
}
CustomItems.AddItem(Item);
SetWeaponInfo(false,Index,Item,CustomList);
}
simulated static final function KFGFxObject_TraderItems CreateNewList()
{
local KFGFxObject_TraderItems L;
L = new class'KFGFxObject_TraderItems';
L.SaleItems = L.Default.SaleItems;
L.ArmorPrice = L.Default.ArmorPrice;
L.GrenadePrice = L.Default.GrenadePrice;
return L;
}
simulated static final function SetWeaponInfo(bool bDedicated, int Index, FCustomTraderItem Item, KFGFxObject_TraderItems List)
{
local array<STraderItemWeaponStats> S;
local int i;
if (List.SaleItems.Length<=Index)
List.SaleItems.Length = Index+1;
List.SaleItems[Index].WeaponDef = Item.WeaponDef;
List.SaleItems[Index].ClassName = Item.WeaponClass.Name;
if (class<KFWeap_DualBase>(Item.WeaponClass)!=None && class<KFWeap_DualBase>(Item.WeaponClass).Default.SingleClass!=None)
List.SaleItems[Index].SingleClassName = class<KFWeap_DualBase>(Item.WeaponClass).Default.SingleClass.Name;
else List.SaleItems[Index].SingleClassName = '';
List.SaleItems[Index].DualClassName = Item.WeaponClass.Default.DualClass!=None ? Item.WeaponClass.Default.DualClass.Name : '';
List.SaleItems[Index].AssociatedPerkClasses = Item.WeaponClass.Static.GetAssociatedPerkClasses();
List.SaleItems[Index].MaxSpareAmmo = Item.WeaponClass.Default.SpareAmmoCapacity[0];
List.SaleItems[Index].MagazineCapacity = Item.WeaponClass.Default.MagazineCapacity[0];
List.SaleItems[Index].InitialSpareMags = Item.WeaponClass.Default.InitialSpareMags[0];
List.SaleItems[Index].MaxSecondaryAmmo = Item.WeaponClass.Default.MagazineCapacity[1] * Item.WeaponClass.Default.SpareAmmoCapacity[1];
List.SaleItems[Index].BlocksRequired = Item.WeaponClass.Default.InventorySize;
List.SaleItems[Index].ItemID = Index;
List.SaleItems[Index].InitialSecondaryAmmo = Item.WeaponClass.Default.InitialSpareMags[1];
List.SaleItems[Index].WeaponUpgradeDmgMultiplier[0] = 1.0;
for (i = 0;i<Min(Item.WeaponClass.Default.WeaponUpgrades.Length, 5);i++)
{
List.SaleItems[Index].WeaponUpgradeWeight[i+1] = Item.WeaponClass.Static.GetUpgradeStatAdd(EWUS_Weight, i+1);
List.SaleItems[Index].WeaponUpgradeDmgMultiplier[i+1] = Item.WeaponClass.Static.GetUpgradeStatScale(EWUS_Damage0, i+1);
}
if (!bDedicated)
{
List.SaleItems[Index].SecondaryAmmoImagePath = Item.WeaponClass.Default.SecondaryAmmoTexture!=None ? PathName(Item.WeaponClass.Default.SecondaryAmmoTexture) : "UI_SecondaryAmmo_TEX.GasTank";
List.SaleItems[Index].TraderFilter = Item.WeaponClass.Static.GetTraderFilter();
List.SaleItems[Index].InventoryGroup = Item.WeaponClass.Default.InventoryGroup;
List.SaleItems[Index].GroupPriority = Item.WeaponClass.Default.GroupPriority;
Item.WeaponClass.Static.SetTraderWeaponStats(S);
List.SaleItems[Index].WeaponStats = S;
}
}
simulated function RecheckGRI()
{
local ExtPlayerController PC;
if (KFGameReplicationInfo(WorldInfo.GRI)==None)
SetTimer(0.1,false,'RecheckGRI');
else
{
KFGameReplicationInfo(WorldInfo.GRI).TraderItems = CustomList;
foreach LocalPlayerControllers(class'ExtPlayerController',PC)
{
if (PC.PurchaseHelper!=None)
{
PC.PurchaseHelper.TraderItems = CustomList;
}
}
}
}
simulated final function bool ShowAdminName()
{
return (bAdmin || AdminType<255);
return (bAdmin || AdminType < AT_Player);
}
simulated function string GetAdminName()
{
switch (AdminType)
{
case 0:
case AT_Global:
return "Super Admin";
case 1:
case 255:
case AT_Admin:
case AT_Player: // TODO: Admin is the same as player? WTF? #1
return "Admin";
case 2:
case AT_Mod:
return "Mod";
case 3:
case AT_TMem:
return "Trusted Member";
case 4:
case AT_VIP:
return "VIP";
case AT_Booster:
return "Booster";
}
}
@ -352,17 +255,19 @@ simulated function string GetAdminNameAbr()
{
switch (AdminType)
{
case 0:
case AT_Global:
return "S";
case 1:
case 255:
case AT_Admin:
case AT_Player: // TODO: Admin is the same as player? WTF? #2
return "A";
case 2:
case AT_Mod:
return "M";
case 3:
case AT_TMem:
return "T";
case 4:
case AT_VIP:
return "V";
case AT_Booster:
return "B";
}
}
@ -370,17 +275,19 @@ simulated function string GetAdminColor()
{
switch (AdminType)
{
case 0:
case AT_Global:
return "FF6600";
case 1:
case 255:
case AT_Admin:
case AT_Player: // TODO: Admin is the same as player? WTF? #3
return "40FFFF";
case 2:
case AT_Mod:
return "FF33FF";
case 3:
case AT_TMem:
return "FF0000";
case 4:
case AT_VIP:
return "FFD700";
case AT_Booster:
return "32A852";
}
}
@ -388,17 +295,19 @@ simulated function color GetAdminColorC()
{
switch (AdminType)
{
case 0:
case AT_Global:
return MakeColor(255,102,0,255);
case 1:
case 255:
case AT_Admin:
case AT_Player: // TODO: Admin is the same as player? WTF? #4
return MakeColor(64,255,255,255);
case 2:
case AT_Mod:
return MakeColor(255,51,255,255);
case 3:
case AT_TMem:
return MakeColor(255,0,0,255);
case 4:
case AT_VIP:
return MakeColor(255,215,0,255);
case AT_Booster:
return MakeColor(50,168,82,255);
}
}
@ -804,22 +713,25 @@ event BeginState(Name N)
switch (N)
{
case 'Global':
AdminType = 0;
AdminType = AT_Global;
break;
case 'Admin':
AdminType = 1;
AdminType = AT_Admin;
break;
case 'Mod':
AdminType = 2;
AdminType = AT_Mod;
break;
case 'TMem':
AdminType = 3;
AdminType = AT_TMem;
break;
case 'VIP':
AdminType = 4;
AdminType = AT_VIP;
break;
case 'Booster':
AdminType = AT_Booster;
break;
case 'User':
AdminType = 255;
AdminType = AT_Player;
break;
}
}
@ -827,6 +739,6 @@ event BeginState(Name N)
defaultproperties
{
RespawnCounter=-1
AdminType=255
AdminType=AT_Player
TaggedPlayerName="Player"
}

View File

@ -45,6 +45,7 @@ function PlayAnimation()
}
}
DefaultProperties
defaultproperties
{
}

View File

@ -5,7 +5,7 @@ function SpawnProjectileShield()
return;
}
DefaultProperties
defaultproperties
{
ExplosionActorClass=class'ExtExplosion_SirenScream'

View File

@ -52,4 +52,5 @@ function SetPerkList()
defaultproperties
{
}

View File

@ -1,7 +1,7 @@
class ExtWeapDef_9mm extends KFWeapDef_9mm
abstract;
DefaultProperties
defaultproperties
{
// Set free ammo
AmmoPricePerMag=0

View File

@ -1,7 +1,7 @@
class ExtWeapDef_MedicPistol extends KFWeapDef_MedicPistol
abstract;
DefaultProperties
defaultproperties
{
// Unsellable weapon
BuyPrice=0

View File

@ -1347,5 +1347,4 @@ defaultproperties
Skins.Add((Id=5303, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet17_MAT.sports_aa12.Vault_Sports_AA12_1P_Mint_MIC"), MIC_3P="WEP_SkinSet17_MAT.sports_aa12.Vault_Sports_AA12_3P_Mint_MIC", MIC_Pickup="WEP_SkinSet17_MAT.sports_aa12.Vault_Sports_AA12_3P_Pickup_MIC"))
Skins.Add((Id=5302, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet17_MAT.sports_aa12.Vault_Sports_AA12_1P_FieldTested_MIC"), MIC_3P="WEP_SkinSet17_MAT.sports_aa12.Vault_Sports_AA12_3P_FieldTested_MIC", MIC_Pickup="WEP_SkinSet17_MAT.sports_aa12.Vault_Sports_AA12_3P_Pickup_MIC"))
Skins.Add((Id=5301, Weapondef=class'KFWeapDef_AA12', MIC_1P=("WEP_SkinSet17_MAT.sports_aa12.Vault_Sports_AA12_1P_BattleScarred_MIC"), MIC_3P="WEP_SkinSet17_MAT.sports_aa12.Vault_Sports_AA12_3P_BattleScarred_MIC", MIC_Pickup="WEP_SkinSet17_MAT.sports_aa12.Vault_Sports_AA12_3P_Pickup_MIC"))
}

View File

@ -77,6 +77,6 @@ function GFxObject RefreshSlot(int SlotIndex, KFPlayerReplicationInfo KFPRI)
return PlayerInfoObject;
}
DefaultProperties
defaultproperties
{
}

View File

@ -69,8 +69,13 @@ Begin:
// See if we are close to our owner
RecheckOwner:
OwnerPawn = Ext_T_MonsterPRI(PlayerReplicationInfo)!=None ? Ext_T_MonsterPRI(PlayerReplicationInfo).OwnerController.Pawn : None;
if (OwnerPawn!=None)
OwnerPawn = None;
if (Ext_T_MonsterPRI(PlayerReplicationInfo) != None
&& Ext_T_MonsterPRI(PlayerReplicationInfo).OwnerController != None)
{
OwnerPawn = Ext_T_MonsterPRI(PlayerReplicationInfo).OwnerController.Pawn;
}
if (OwnerPawn != None)
{
if (Enemy!=None && LineOfSightTo(OwnerPawn) && LineOfSightTo(Enemy)) // We have sight to our owner and can see enemy, go for it!
{
@ -151,6 +156,6 @@ RecheckOwner:
Goto('Begin');
}
DefaultProperties
defaultproperties
{
}

View File

@ -7,9 +7,10 @@ function SpecialMoveStarted(bool bForced, Name PrevMove)
function SpecialMoveEnded(Name PrevMove, Name NextMove)
{
}
DefaultProperties
defaultproperties
{
Handle=KFSM_Taunt
bDisableMovement=true

View File

@ -109,6 +109,7 @@ var localized string StatFireDmg;
var localized string StatAllDmg;
var localized string StatHeadDamage;
var localized string StatHealRecharge;
var localized string StatSwitch;
reliable client simulated function string UIName(FDefPerkStat DefPerkStat)
{
@ -135,6 +136,7 @@ reliable client simulated function string UIName(FDefPerkStat DefPerkStat)
case name("AllDmg"): return StatAllDmg;
case name("HeadDamage"): return StatHeadDamage;
case name("HealRecharge"): return StatHealRecharge;
case name("Switch"): return StatSwitch;
}
return "";
}
@ -710,6 +712,8 @@ static function UpdateConfigs(int OldVer)
AddStatsCfg(18); // Add all damage.
else if (OldVer<=13)
AddStatsCfg(19); // Add HeadDamage and HealRecharge
else if (OldVer<=14)
AddStatsCfg(21); // Add WeaponSwitch
if (OldVer<=5)
{
// Add prestige
@ -1167,10 +1171,10 @@ simulated function float ApplyEffect(name Type, float Value, float Progress)
Modifiers[1] = 1.f + (Value*Progress);
break;
case 'Recoil':
Modifiers[2] = 1.f / (1.f+Value*Progress);
Modifiers[2] = 1.f - (Value*Progress);
break;
case 'Spread':
Modifiers[3] = 1.f / (1.f+Value*Progress);
Modifiers[3] = 1.f - (Value*Progress);
break;
case 'Rate':
Modifiers[4] = 1.f / (1.f+Value*Progress);
@ -1187,8 +1191,8 @@ simulated function float ApplyEffect(name Type, float Value, float Progress)
}
break;
case 'KnockDown':
Modifiers[7] = FMin(1.f + (Value*Progress),2.f);
return (Modifiers[7]-1.f);
Modifiers[7] = 1.f + (Value*Progress);
break;
case 'Welder':
Modifiers[8] = 1.f + (Value*Progress);
break;
@ -1237,6 +1241,9 @@ simulated function float ApplyEffect(name Type, float Value, float Progress)
case 'HealRecharge':
Modifiers[20] = 1.f / (1.f+Value*Progress);
break;
case 'Switch':
Modifiers[21] = 1.f / (1.f+Value*Progress);
break;
}
return (Value*Progress);
}
@ -1293,6 +1300,11 @@ simulated function float GetReloadRateScale(KFWeapon KFW)
return (IsWeaponOnPerk(KFW) ? Modifiers[5] : 1.f);
}
simulated function float GetCameraViewShakeModifier(KFWeapon KFW)
{
return Modifiers[2];
}
function ModifyHealth(out int InHealth)
{
InHealth *= Modifiers[6];
@ -1313,6 +1325,11 @@ function float GetStunPowerModifier(optional class<DamageType> DamageType, optio
return Modifiers[7];
}
function float GetStumblePowerModifier( optional KFPawn KFP, optional class<KFDamageType> DamageType, optional out float CooldownModifier, optional byte BodyPart )
{
return Modifiers[7];
}
simulated function ModifyMeleeAttackSpeed(out float InDuration);
function AddDefaultInventory(KFPawn P)
@ -1467,6 +1484,11 @@ simulated function float GetIronSightSpeedModifier(KFWeapon KFW)
return 1.f;
}
simulated function ModifyWeaponSwitchTime(out float ModifiedSwitchTime)
{
ModifiedSwitchTime *= Modifiers[21];
}
function OnWaveEnded();
function NotifyZedTimeStarted();
@ -1475,9 +1497,14 @@ simulated function float GetZedTimeExtensions(byte Level)
return 1.f;
}
simulated function float GetTightChokeModifier()
{
return Modifiers[3];
}
defaultproperties
{
CurrentConfigVer=14
CurrentConfigVer=15
bOnlyRelevantToOwner=true
bCanBeGrabbed=true
NetUpdateFrequency=1
@ -1529,8 +1556,8 @@ defaultproperties
DefPerkStats(0)=(MaxValue=50,CostPerValue=1,StatType="Speed",Progress=0.4)
DefPerkStats(1)=(MaxValue=1000,CostPerValue=1,StatType="Damage",Progress=0.5)
DefPerkStats(2)=(MaxValue=90,CostPerValue=1,StatType="Recoil",Progress=1)
DefPerkStats(3)=(MaxValue=80,CostPerValue=1,StatType="Spread",Progress=0.75)
DefPerkStats(2)=(MaxValue=100,CostPerValue=1,StatType="Recoil",Progress=1)
DefPerkStats(3)=(MaxValue=100,CostPerValue=1,StatType="Spread",Progress=1)
DefPerkStats(4)=(MaxValue=1000,CostPerValue=1,StatType="Rate",Progress=0.5)
DefPerkStats(5)=(MaxValue=1000,CostPerValue=1,StatType="Reload",Progress=0.5)
DefPerkStats(6)=(MaxValue=150,CostPerValue=1,StatType="Health",Progress=1)
@ -1548,6 +1575,7 @@ defaultproperties
DefPerkStats(18)=(MaxValue=500,CostPerValue=1,StatType="AllDmg",Progress=0.25)
DefPerkStats(19)=(MaxValue=500,CostPerValue=1,StatType="HeadDamage",Progress=1,bHiddenConfig=true)
DefPerkStats(20)=(MaxValue=200,CostPerValue=1,StatType="HealRecharge",Progress=0.5,bHiddenConfig=true)
DefPerkStats(21)=(MaxValue=100,CostPerValue=1,StatType="Switch",Progress=1)
Modifiers.Add(1.f)
Modifiers.Add(1.f)
@ -1570,6 +1598,7 @@ defaultproperties
Modifiers.Add(1.f)
Modifiers.Add(0.f)
Modifiers.Add(1.f)
Modifiers.Add(1.f)
EnemyDistDraw.Add(500)
EnemyDistDraw.Add(700)

View File

@ -1,5 +1,48 @@
Class Ext_PerkFirebug extends Ext_PerkBase;
var bool bUseInferno,bUsePyromaniac,bUseGroundFire,bUseHeatWave;
replication
{
// Things the server should send to the client.
if (true)
bUseInferno,bUsePyromaniac,bUseGroundFire,bUseHeatWave;
}
simulated final private function bool IsInfernoActive()
{
return bUseInferno && WorldInfo.TimeDilation < 1.f;
}
simulated function bool GetIsUberAmmoActive(KFWeapon KFW)
{
return bUsePyromaniac && IsWeaponOnPerk(KFW) && WorldInfo.TimeDilation < 1.f;
}
simulated function float GetZedTimeModifier(KFWeapon W)
{
local name StateName;
if (bUsePyromaniac && IsWeaponOnPerk(W))
{
StateName = W.GetStateName();
if (BasePerk.Default.ZedTimeModifyingStates.Find(StateName) != INDEX_NONE || StateName == 'Reloading')
return 1.f;
}
return 0.f;
}
simulated final private function bool IsGroundFireActive()
{
return bUseGroundFire;
}
simulated final private function bool IsHeatWaveActive()
{
return bUseHeatWave;
}
defaultproperties
{
PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Firebug'
@ -7,6 +50,10 @@ defaultproperties
DefTraitList.Add(class'Ext_TraitNapalm')
DefTraitList.Add(class'Ext_TraitFireExplode')
DefTraitList.Add(class'Ext_TraitFireRange')
DefTraitList.Add(class'Ext_TraitInferno')
DefTraitList.Add(class'Ext_TraitPyromaniac')
DefTraitList.Add(class'Ext_TraitGroundFire')
DefTraitList.Add(class'Ext_TraitHeatWave')
BasePerk=class'KFPerk_Firebug'
PrimaryMelee=class'KFWeap_Knife_Firebug'

View File

@ -28,6 +28,16 @@ simulated function float GetZedTimeModifier(KFWeapon W)
return 0.f;
}
function float GetStumblePowerModifier( optional KFPawn KFP, optional class<KFDamageType> DamageType, optional out float CooldownModifier, optional byte BodyPart )
{
if (bRapidAssault)
{
return 2.f * Modifiers[7];
}
return Modifiers[7];
}
defaultproperties
{
PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_SWAT'

View File

@ -2,4 +2,5 @@ class Ext_TGroupMonster extends Ext_TGroupBase;
defaultproperties
{
}

View File

@ -33,4 +33,5 @@ static final function byte GetMaxLimit(Ext_PerkBase Perk)
defaultproperties
{
}

View File

@ -2,4 +2,5 @@ class Ext_TGroupZEDTime extends Ext_TGroupBase;
defaultproperties
{
}

View File

@ -8,6 +8,8 @@ var bool bTeleporting,bIsDelayed;
function bool CanResPlayer(KFPawn_Human Other, byte Level)
{
local Actor SpawnPoint;
if (bTeleporting)
{
if (LastDied!=None)
@ -23,11 +25,17 @@ function bool CanResPlayer(KFPawn_Human Other, byte Level)
else if (Level==1 && Rand(2)==0)
return false;
LastDied = Other;
bTeleporting = true;
if (SpawnPointer==None)
SpawnPointer = class'ExtSpawnPointHelper'.Static.FindHelper(WorldInfo);
ResPoint = SpawnPointer.PickBestSpawn().Location;
SpawnPoint = SpawnPointer.PickBestSpawn();
if (SpawnPoint == None)
return false;
LastDied = Other;
bTeleporting = true;
ResPoint = SpawnPoint.Location;
LastDied.FindSpot(vect(36,36,86),ResPoint);
if (VSizeSq(LastDied.Location-ResPoint)<1.f) // Prevent division by zero errors in future.
ResPoint.Z+=5;

View File

@ -24,4 +24,5 @@ function Timer()
defaultproperties
{
}

View File

@ -194,7 +194,7 @@ simulated function Destroyed()
}
}
DefaultProperties
defaultproperties
{
InteractionIndex=IMT_ReceiveAmmo
RemoteRole=ROLE_SimulatedProxy

View File

@ -8,4 +8,5 @@ var class<Ext_TraitBase> TraitClass;
defaultproperties
{
}

View File

@ -15,7 +15,7 @@ static function bool MeetsRequirements(byte Lvl, Ext_PerkBase Perk)
{
local int i;
if (Perk.CurrentLevel<Default.MinLevel || Perk.CurrentPrestige<3)
if (Perk.CurrentLevel<Default.MinLevel || Perk.CurrentPrestige<1)
return false;
if (Lvl==0)

View File

@ -0,0 +1,16 @@
Class Ext_TraitGroundFire extends Ext_TraitBase;
static function TraitActivate(Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_PerkFirebug(Perk).bUseGroundFire = true;
}
static function TraitDeActivate(Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_PerkFirebug(Perk).bUseGroundFire = false;
}
defaultproperties
{
DefLevelCosts(0)=50
}

View File

@ -0,0 +1,16 @@
Class Ext_TraitHeatWave extends Ext_TraitBase;
static function TraitActivate(Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_PerkFirebug(Perk).bUseHeatWave = true;
}
static function TraitDeActivate(Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_PerkFirebug(Perk).bUseHeatWave = false;
}
defaultproperties
{
DefLevelCosts(0)=50
}

View File

@ -0,0 +1,31 @@
Class Ext_TraitInferno extends Ext_TraitBase;
var localized string GroupDescription;
function string GetPerkDescription()
{
local string S;
S = Super.GetPerkDescription();
S $= "|"$GroupDescription;
return S;
}
static function TraitActivate(Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_PerkFirebug(Perk).bUseInferno = true;
}
static function TraitDeActivate(Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_PerkFirebug(Perk).bUseInferno = false;
}
defaultproperties
{
SupportedPerk=class'Ext_PerkFirebug'
TraitGroup=class'Ext_TGroupZEDTime'
NumLevels=1
DefLevelCosts(0)=50
//DefMinLevel=65
}

View File

@ -0,0 +1,31 @@
Class Ext_TraitPyromaniac extends Ext_TraitBase;
var localized string GroupDescription;
function string GetPerkDescription()
{
local string S;
S = Super.GetPerkDescription();
S $= "|"$GroupDescription;
return S;
}
static function TraitActivate(Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_PerkFirebug(Perk).bUsePyromaniac = true;
}
static function TraitDeActivate(Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_PerkFirebug(Perk).bUsePyromaniac = false;
}
defaultproperties
{
SupportedPerk=class'Ext_PerkFirebug'
TraitGroup=class'Ext_TGroupZEDTime'
NumLevels=1
DefLevelCosts(0)=50
//DefMinLevel=65
}

View File

@ -4,17 +4,17 @@ var() Texture2D SupplyIcon;
static function ApplyEffectOn(KFPawn_Human Player, Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_TraitSupplyData(Data).SpawnSupplier(Player);
if (Data != None) Ext_TraitSupplyData(Data).SpawnSupplier(Player);
}
static function CancelEffectOn(KFPawn_Human Player, Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_TraitSupplyData(Data).RemoveSupplier();
if (Data != None) Ext_TraitSupplyData(Data).RemoveSupplier();
}
static function PlayerDied(Ext_PerkBase Perk, byte Level, optional Ext_TraitDataStore Data)
{
Ext_TraitSupplyData(Data).RemoveSupplier();
if (Data != None) Ext_TraitSupplyData(Data).RemoveSupplier();
}
defaultproperties

View File

@ -2,8 +2,8 @@ Class Ext_TraitWPGuns extends Ext_TraitWeapons;
defaultproperties
{
LevelEffects(0)=(LoadoutClasses=(class'KFWeap_Pistol_Colt1911'))
LevelEffects(1)=(LoadoutClasses=(class'KFWeap_Pistol_Deagle'))
LevelEffects(2)=(LoadoutClasses=(class'KFWeap_Revolver_SW500'))
LevelEffects(0)=(LoadoutClasses=(class'KFWeap_Pistol_DualColt1911'))
LevelEffects(1)=(LoadoutClasses=(class'KFWeap_Pistol_DualDeagle'))
LevelEffects(2)=(LoadoutClasses=(class'KFWeap_Revolver_DualSW500'))
LevelEffects(3)=(LoadoutClasses=(class'KFWeap_Pistol_DualColt1911',class'KFWeap_Pistol_DualDeagle',class'KFWeap_Revolver_DualSW500'))
}

View File

@ -59,4 +59,5 @@ function PreClientTravel(string PendingURL, ETravelType TravelType, bool bIsSeam
defaultproperties
{
}

View File

@ -298,7 +298,7 @@ event PostRender()
EPRI = ExtPlayerReplicationInfo(KFPlayerOwner.PlayerReplicationInfo);
else if (EPRI.RespawnCounter>0)
DrawRespawnCounter();
bMeAdmin = (EPRI!=None && EPRI.AdminType<=1);
bMeAdmin = (EPRI != None && EPRI.AdminType <= AT_Admin);
if (KillMessages.Length>0)
RenderKillMsg();
if (DeathMessages.Length>0)

View File

@ -53,4 +53,5 @@ function DrawMenu()
defaultproperties
{
}

View File

@ -90,4 +90,5 @@ function HandleMouseClick(bool bRight);
defaultproperties
{
}

View File

@ -56,4 +56,5 @@ function PreDraw()
defaultproperties
{
}

View File

@ -1,928 +0,0 @@
//====================================================================
// HTML Text box, written by Marco
// Simply call SetContents to change window contents.
// Only callback available is for LaunchKFURL.
// ====================================================================
class KFGUI_HTMLTextBox extends KFGUI_MultiComponent;
struct FTextLine
{
var string Text,URL;
var color Color,ALColor;
var Font Font;
var float FontScale;
var byte Align,FontSize;
var int X,Y,XS,YS,Tab,TOffset;
var byte LineSkips;
var array<int> ImgList;
var bool bHasURL,bSplit;
};
var array<FTextLine> Lines;
struct FImageEntry
{
var Surface Img;
var int X,Y,XS,YS,YOffset,XOffset;
var byte Align,Style;
};
var array<FImageEntry> Images;
var FImageEntry BgImage;
var float OldXSize,OldYSize;
var int YSize,HoverOverLinkLine,OldHoverLine;
var() Color BGColor,WhiteColor,BlueColor,RedColor;
var KFGUI_ScrollBarV MyScrollBar;
var string TitleString;
var int CurTab;
var byte DefaultFontSize;
var bool bNeedsInit,bHasSplitLines,bNeedScrollbar;
function InitMenu()
{
Super.InitMenu();
MyScrollBar = KFGUI_ScrollBarV(FindComponentID('Scrollbar'));
}
final function int AddText( string Input, color TextColor, byte TextAlign, byte FontSize, out byte NumSkips )
{
local int i;
i = Lines.Length;
Lines.Length = i+1;
Lines[i].Text = Input;
Lines[i].Color = TextColor;
Lines[i].Align = TextAlign;
Lines[i].FontSize = FontSize;
Lines[i].LineSkips = NumSkips;
Lines[i].Tab = CurTab;
NumSkips = 0;
return i;
}
final function string ParseLinkType( string URL )
{
if( InStr(URL,"//")>0 )
return URL;
if( Left(URL,4)~="ftp." )
return "ftp://"$URL;
return "http://"$URL;
}
final function AddImage( string Input )
{
local string Temp;
local byte Align,Sty;
local Material M;
local int X,Y,XS,YS,i,j,z;
Align = 3;
Temp = GetOption(Input, "ALIGN=");
if (Temp != "")
{
switch( Caps(Temp) )
{
case "LEFT":
case "0":
Align = 0;
break;
case "CENTER":
case "1":
Align = 1;
break;
case "RIGHT":
case "2":
Align = 2;
break;
}
}
Temp = GetOption(Input, "STYLE=");
if (Temp != "")
{
switch( Caps(Temp) )
{
case "NORMAL":
case "0":
Sty = 0;
break;
case "STRETCH":
case "1":
Sty = 1;
break;
case "TILEDX":
case "2":
Sty = 2;
break;
case "TILEDY":
case "3":
Sty = 3;
break;
case "TILED":
case "4":
Sty = 4;
break;
}
}
Temp = GetOption(Input, "SRC=");
if (Temp != "")
M = Surface(DynamicLoadObject(Temp,Class'Surface'));
if( M==None )
M = class'Canvas'.Default.DefaultTexture;
X = int(GetOption(Input, "VSPACE="));
Y = int(GetOption(Input, "HSPACE="));
XS = int(GetOption(Input, "WIDTH="));
YS = int(GetOption(Input, "HEIGHT="));
if( XS==0 )
XS = M.GetSurfaceWidth();
if( YS==0 )
YS = M.GetSurfaceHeight();
i = Images.Length;
Images.Length = i+1;
Images[i].Img = M;
Images[i].XOffset = X;
Images[i].YOffset = Y;
Images[i].XS = XS;
Images[i].YS = YS;
Images[i].Style = Sty;
Images[i].Align = Align;
j = Lines.Length-1;
z = Lines[j].ImgList.Length;
Lines[j].ImgList.Length = z+1;
Lines[j].ImgList[z] = i;
}
final function SetContents( string Input )
{
local string LeftText,HTML,RightText,Output,Temp,Link;
local int Index;
local color TextColor,LinkColor,ALinkColor,OrgTextColor;
local byte Alignment,FontScaler,NextLineSkips;
CurTab = 0;
BGColor.A = 0;
BgImage.Img = None;
Lines.Length = 0;
Images.Length = 0;
TitleString = "";
bHasSplitLines = false;
bNeedsInit = true;
// First remove new liners
Input = Repl(Input, Chr(13)$Chr(10), "");
Input = Repl(Input, Chr(13), "");
Input = Repl(Input, Chr(10), "");
Input = Repl(Input, Chr(9), " ");
Input = Repl(Input, "\\n", "<BR>");
TextColor = WhiteColor;
OrgTextColor = WhiteColor;
LinkColor = BlueColor;
ALinkColor = RedColor;
FontScaler = 3;
DefaultFontSize = 3;
Index = -1;
while (Input != "")
{
ParseHTML(Input, LeftText, HTML, RightText);
switch (GetTag(HTML))
{
// multiline HTML tags
case "P":
Output $= LeftText;
if( Output!="" )
{
Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
NextLineSkips = 2;
Output = "";
}
else ++NextLineSkips;
break;
case "BR":
Output $= LeftText;
if( Output!="" )
{
Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
NextLineSkips = 1;
Output = "";
}
else ++NextLineSkips;
break;
case "BODY":
Temp = GetOption(HTML, "BGCOLOR=");
if (Temp != "")
BGColor = ParseColor(Temp);
Temp = GetOption(HTML, "LINK=");
if (Temp != "")
LinkColor = ParseColor(Temp);
Temp = GetOption(HTML, "ALINK=");
if (Temp != "")
ALinkColor = ParseColor(Temp);
Temp = GetOption(HTML, "TEXT=");
if (Temp != "")
{
TextColor = ParseColor(Temp);
OrgTextColor = TextColor;
}
Temp = GetOption(HTML, "SIZE=");
if (Temp != "")
{
FontScaler = int(Temp);
DefaultFontSize = FontScaler;
}
Temp = GetOption(Input, "IMG=");
if (Temp != "")
{
if( BGColor.A==0 )
BGColor = Class'Hud'.Default.WhiteColor;
BgImage.Img = Surface(DynamicLoadObject(Temp,Class'Surface'));
if( BgImage.Img==None )
BgImage.Img = Class'Canvas'.Default.DefaultTexture;
BgImage.X = BgImage.Img.GetSurfaceWidth();
BgImage.Y = BgImage.Img.GetSurfaceHeight();
switch( Caps(GetOption(Input, "IMGSTYLE=")) )
{
case "TILED":
BgImage.XS = BgImage.X;
BgImage.YS = BgImage.Y;
BgImage.Style = 1;
Temp = GetOption(Input, "TILEX=");
if (Temp != "")
BgImage.XS = int(Temp);
Temp = GetOption(Input, "TILEY=");
if (Temp != "")
BgImage.YS = int(Temp);
break;
case "FITX":
BgImage.Style = 2;
break;
case "FITY":
BgImage.Style = 3;
break;
default: // FIT
BgImage.Style = 0;
}
BgImage.Align = 0;
if( GetOption(Input, "IMGLOCK=")=="0" )
BgImage.Align = 1;
}
Output $= LeftText;
break;
case "CENTER":
Output $= LeftText;
if ( Output!="" )
{
Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
Output = "";
}
NextLineSkips = Max(NextLineSkips,1);
Alignment = 1;
break;
case "RIGHT":
Output $= LeftText;
if ( Output!="" )
{
Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
Output = "";
}
NextLineSkips = Max(NextLineSkips,1);
Alignment = 2;
break;
case "/CENTER":
case "/RIGHT":
Index = AddText(Output $ LeftText,TextColor,Alignment,FontScaler,NextLineSkips);
++NextLineSkips;
Alignment = 0;
Output = "";
break;
// Inline HTML tags
case "H1":
Output $= LeftText;
if ( Output!="" )
{
Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
Output = "";
}
NextLineSkips = Max(NextLineSkips,1);
FontScaler = 5;
Alignment = 1;
break;
case "/H1":
Index = AddText(Output $ LeftText,TextColor,Alignment,FontScaler,NextLineSkips);
++NextLineSkips;
Output = "";
FontScaler = DefaultFontSize;
Alignment = 0;
break;
case "FONT":
Output $= LeftText;
if( Output!="" )
{
Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
Output = "";
}
Temp = GetOption(HTML, "COLOR=");
if (Temp != "")
TextColor = ParseColor(Temp);
Temp = GetOption(HTML, "SIZE=");
if (Temp != "")
FontScaler = int(Temp);
break;
case "/FONT":
Output $= LeftText;
if( Output!="" )
{
Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
Output = "";
}
TextColor = OrgTextColor;
FontScaler = DefaultFontSize;
break;
case "TAB":
Output $= LeftText;
if( Output!="" )
{
Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
Output = "";
}
CurTab = int(GetOption(HTML, "X="));
break;
case "/TAB":
Output $= LeftText;
if( Output!="" )
{
Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
Output = "";
}
CurTab = 0;
break;
case "TITLE":
Output $= LeftText;
break;
case "/TITLE":
TitleString = LeftText;
break;
case "A":
Output $= LeftText;
if( Output!="" )
{
Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
Output = "";
}
Link = GetOption(HTML, "HREF=");
break;
case "/A":
Output $= LeftText;
Index = AddText(Output,LinkColor,Alignment,FontScaler,NextLineSkips);
Lines[Index].ALColor = ALinkColor;
Lines[Index].bHasURL = true;
if( Link=="" )
Lines[Index].URL = ParseLinkType(Output);
else Lines[Index].URL = ParseLinkType(Link);
Output = "";
FontScaler = DefaultFontSize;
Alignment = 0;
break;
case "IMG":
Output $= LeftText;
if( Output!="" || NextLineSkips>0 )
AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
Output = "";
AddImage(HTML);
break;
default:
Output = Output $ LeftText;
break;
}
Input = RightText;
}
AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips);
}
// Get the next HTML tag, the text before it and everthing after it.
final function ParseHTML(string Input, out string LeftText, out string HTML, out string RightText)
{
local int i;
i = InStr(Input, "<");
if (i == -1)
{
LeftText = Input;
HTML = "";
RightText = "";
return;
}
LeftText = Left(Input, i);
HTML = Mid(Input, i);
i = InStr(HTML, ">");
if (i == -1)
{
RightText = "";
return;
}
RightText = Mid(HTML, i+1);
HTML = Left(HTML, i+1);
}
final function string GetTag(string HTML)
{
local int i;
if (HTML == "")
return "";
HTML = Mid(HTML, 1); // lose <
i = FirstMatching(InStr(HTML, ">"), InStr(HTML, " "));
if (i == -1)
return Caps(HTML);
else
return Caps(Left(HTML, i));
}
final function string GetOption(string HTML, string Option)
{
local int i, j;
local string s;
i = InStr(Caps(HTML), Caps(Option));
if (i == 1 || Mid(HTML, i-1, 1) == " ")
{
s = Mid(HTML, i+Len(Option));
j = FirstMatching(InStr(s, ">"), InStr(s, " "));
s = Left(s, j);
if (Left(s, 1) == "\"")
s = Mid(s, 1);
if (Right(s, 1) == "\"")
s = Left(s, Len(s) - 1);
return s;
}
return "";
}
final function int FirstMatching(int i, int j)
{
if (i == -1)
return j;
if (j == -1)
return i;
return Min(i, j);
}
final function Color ParseColor(string S)
{
local Color C;
local int i;
S = Caps(S);
if (Left(S, 1) == "#")
{
C.R = (GetHexDigit(Mid(S, 1, 1)) << 4) + GetHexDigit(Mid(S, 2, 1));
C.G = (GetHexDigit(Mid(S, 3, 1)) << 4) + GetHexDigit(Mid(S, 4, 1));
C.B = (GetHexDigit(Mid(S, 5, 1)) << 4) + GetHexDigit(Mid(S, 6, 1));
}
else if (Left(S, 4) == "RGB(")
{
S = Mid(S, 4);
i = InStr(S,",");
C.R = int(Left(S,i));
S = Mid(S,i+1);
i = InStr(S,",");
C.G = int(Left(S,i));
C.B = int(Mid(S,i+1));
}
else
{
switch( S )
{
case "RED":
C.R = 255;
C.G = 0;
C.B = 0;
break;
case "BLUE":
C.R = 0;
C.G = 0;
C.B = 255;
break;
case "GREEN":
C.R = 0;
C.G = 255;
C.B = 0;
break;
case "YELLOW":
C.R = 255;
C.G = 255;
C.B = 0;
break;
case "BLACK":
C.R = 0;
C.G = 0;
C.B = 0;
break;
default: // WHITE
C.R = 255;
C.G = 255;
C.B = 255;
}
}
C.A = 255;
return C;
}
final function byte GetHexDigit(string D)
{
local byte i;
i = Asc(D);
if( i>=48 && i<=57 ) // i>='0' && i<='9'
return (i-48); // i-'0'
return Min(i-55,15); // i-('A'-10)
}
final function SplitLine( int iLine, int iOffset )
{
local int i;
local string S;
++iLine;
Lines.Insert(iLine,1);
S = Lines[iLine-1].Text;
for( i=iOffset; i<Len(S); ++i )
if( Mid(S,i,1)!=" " )
break;
Lines[iLine].Text = Mid(S,i);
Lines[iLine-1].Text = Left(S,iOffset);
Lines[iLine].URL = Lines[iLine-1].URL;
Lines[iLine].Color = Lines[iLine-1].Color;
Lines[iLine].ALColor = Lines[iLine-1].ALColor;
Lines[iLine].Align = Lines[iLine-1].Align;
Lines[iLine].FontSize = Lines[iLine-1].FontSize;
Lines[iLine].Tab = Lines[iLine-1].Tab;
Lines[iLine].LineSkips = 1;
Lines[iLine].bHasURL = Lines[iLine-1].bHasURL;
Lines[iLine].bSplit = true;
bHasSplitLines = true;
}
final protected function InitHTMLArea()
{
local float XS,YS;
local int i,j,X,Y,iStart,BestHeight,FontSize,PrevY,Remain,iLastWord,iLen,z,ImgHeight;
// Used to detect resolution changes when text needs realignment.
OldXSize = CompPos[2];
OldYSize = CompPos[3];
// Merge splitted lines again
if( bHasSplitLines )
{
bHasSplitLines = false;
for( i=1; i<Lines.Length; ++i )
{
if( Lines[i].bSplit )
{
Lines[i-1].Text @= Lines[i].Text;
Lines.Remove(i--,1);
}
}
}
// Setup background image scaling
if( BgImage.Img!=None )
{
switch( BgImage.Style )
{
case 1: // Tiled
if( BgImage.X==BgImage.XS )
BgImage.XOffset = Canvas.ClipX;
else
{
XS = Canvas.ClipX / float(BgImage.XS) * float(BgImage.X);
BgImage.XOffset = XS;
}
if( BgImage.Y==BgImage.YS )
BgImage.YOffset = Canvas.ClipY;
else
{
XS = Canvas.ClipY / float(BgImage.YS) * float(BgImage.Y);
BgImage.YOffset = XS;
}
break;
case 2: // Fit X
XS = Canvas.ClipY * (Canvas.ClipX / float(BgImage.X));
BgImage.YS = XS;
break;
case 3: // Fit Y
XS = Canvas.ClipX * (Canvas.ClipY / float(BgImage.Y));
BgImage.XS = XS;
break;
}
}
FontSize = Owner.CurrentStyle.DefaultFontSize;
Canvas.SetPos(0,0);
if( Lines.Length>0 )
{
while( true )
{
if( i>=Lines.Length || (i>0 && Lines[i].LineSkips>0) )
{
for( j=iStart; j<i; ++j )
{
switch( Lines[j].Align )
{
case 0: // Left
Lines[j].X = Lines[j].TOffset;
break;
case 1: // Center
Lines[j].X = (Canvas.ClipX-X+Lines[j].TOffset)/2;
break;
case 2: // Right
Lines[j].X = Canvas.ClipX-X+Lines[j].TOffset;
break;
}
}
if( i>=Lines.Length )
break;
X = 0;
iStart = i;
PrevY = BestHeight;
BestHeight = 0;
}
if( Lines[i].FontSize>=247 )
Lines[i].Font = Owner.CurrentStyle.PickFont(Max(Lines[i].FontSize-247,0),Lines[i].FontScale);
else Lines[i].Font = Owner.CurrentStyle.PickFont(Max(FontSize+Lines[i].FontSize,0),Lines[i].FontScale);
Canvas.Font = Lines[i].Font;
if( Lines[i].Text=="" )
{
Canvas.TextSize("ABC",XS,YS,Lines[i].FontScale,Lines[i].FontScale);
XS = 0;
}
else Canvas.TextSize(Lines[i].Text,XS,YS,Lines[i].FontScale,Lines[i].FontScale);
if( Lines[i].LineSkips>0 )
{
if( PrevY==0 )
PrevY = YS;
Y+=(PrevY*Lines[i].LineSkips);
}
X = Max(X,Lines[i].Tab);
Lines[i].TOffset = X;
Lines[i].Y = Y;
Lines[i].YS = YS;
BestHeight = Max(BestHeight,YS);
if( (X+XS)>Canvas.ClipX )
{
// Split to next row.
Remain = Canvas.ClipX-X;
iLastWord = 0;
iLen = Len(Lines[i].Text);
for( j=1; j<iLen; ++j )
{
Canvas.TextSize(Left(Lines[i].Text,j),XS,YS,Lines[i].FontScale,Lines[i].FontScale);
if( Remain<XS )
{
if( iLastWord==0 ) // Must cut off a word now.
SplitLine(i,Max(j-1,0));
else SplitLine(i,iLastWord);
break;
}
if( Mid(Lines[i].Text,j,1)==" " )
iLastWord = j+1;
}
Canvas.TextSize(Lines[i].Text,XS,YS,Lines[i].FontScale,Lines[i].FontScale);
}
Lines[i].XS = XS;
X+=XS;
for( j=0; j<Lines[i].ImgList.Length; ++j )
{
z = Lines[i].ImgList[j];
if( Images[z].Align==3 )
Images[z].X = X+Images[z].XOffset;
else Images[z].X = Images[z].XOffset;
Images[z].Y = Y+Images[z].YOffset;
ImgHeight = Max(ImgHeight,Images[z].Y+Images[z].YS);
}
++i;
}
YSize = Max(Y+BestHeight,ImgHeight);
}
else YSize = 0;
bNeedScrollbar = (YSize>Canvas.ClipY);
if( bNeedScrollbar )
{
MyScrollBar.SetDisabled(false);
MyScrollBar.UpdateScrollSize(0,YSize,Canvas.ClipY,512);
}
else MyScrollBar.SetDisabled(true);
}
simulated final function DrawTileStretchedClipped( Surface M, float XS, float YS )
{
Canvas.CurX += Canvas.OrgX;
Canvas.CurY += Canvas.OrgY;
if( Canvas.CurX<Canvas.OrgX )
{
XS-=(Canvas.OrgX-Canvas.CurX);
Canvas.CurX = Canvas.OrgX;
}
if( Canvas.CurY<Canvas.OrgY )
{
YS-=(Canvas.OrgY-Canvas.CurY);
Canvas.CurY = Canvas.OrgY;
}
if( (Canvas.CurX+XS)>Canvas.ClipX )
XS = (Canvas.ClipX-Canvas.CurX);
if( (Canvas.CurY+YS)>Canvas.ClipY )
YS = (Canvas.ClipY-Canvas.CurY);
if( Texture(M)!=None )
Canvas.DrawTileStretched(Texture(M),XS,YS);
}
function DrawMenu()
{
local float YS;
local int i,YOffset,MX,MY;
local bool bMouseOnClient;
if( bNeedsInit || OldXSize!=CompPos[2] || OldYSize!=CompPos[3] )
{
bNeedsInit = false;
InitHTMLArea();
}
if( bNeedScrollbar )
YOffset = MyScrollBar.CurrentScroll;
if( BGColor.A>0 )
{
C.SetPos(0,0);
C.DrawColor = BGColor;
if( BgImage.Img!=None )
{
if( BgImage.Align==1 ) // not locked on screen.
MX = YOffset;
switch( BgImage.Style )
{
case 0: // Stretched to fit
C.DrawTileClipped(BgImage.Img,C.ClipX,C.ClipY,0,MX,BgImage.X,BgImage.Y);
break;
case 1: // Tiled
C.DrawTileClipped(BgImage.Img,C.ClipX,C.ClipY,0,MX,BgImage.XOffset,BgImage.YOffset);
break;
case 2: // Fit X
C.DrawTileClipped(BgImage.Img,C.ClipX,C.ClipY,0,MX,BgImage.X,BgImage.YS);
break;
case 3: // Fit Y
C.DrawTileClipped(BgImage.Img,C.ClipX,C.ClipY,0,MX,BgImage.XS,BgImage.Y);
break;
}
}
else C.DrawTile(Texture'WhiteTexture',C.ClipX,C.ClipY,0,0,1,1);
}
MX = Controller.MouseX-C.OrgX;
MY = Controller.MouseY-C.OrgY;
bMouseOnClient = (MX>=0 && MX<=C.ClipX && MY>=0 && MY<=C.ClipY);
HoverOverLinkLine = -1;
MY+=YOffset;
C.DrawColor = Class'HUD'.Default.WhiteColor;
for( i=0; i<Images.Length; ++i )
{
C.CurY = Images[i].Y-YOffset;
if( (C.CurY+Images[i].YS)<0 || C.CurY>C.ClipY )
continue;
switch( Images[i].Align )
{
case 0: // Left
case 3: // Unaligned, postition after text.
C.CurX = 0;
break;
case 1: // Center
C.CurX = (C.ClipX-Images[i].XS)/2;
break;
case 1: // Right
C.CurX = C.ClipX-Images[i].XS;
break;
}
C.CurX += Images[i].X;
switch( Images[i].Style )
{
case 1: // Stretched
DrawTileStretchedClipped(C,Images[i].Img,Images[i].XS,Images[i].YS);
break;
case 2: // Tiled on X axis
C.DrawTileClipped(Images[i].Img,Images[i].XS,Images[i].YS,0,0,Images[i].XS,Images[i].Img.MaterialVSize());
break;
case 3: // Tiled on Y axis
C.DrawTileClipped(Images[i].Img,Images[i].XS,Images[i].YS,0,0,Images[i].Img.MaterialUSize(),Images[i].YS);
break;
case 4: // Fully tiled
C.DrawTileClipped(Images[i].Img,Images[i].XS,Images[i].YS,0,0,Images[i].XS,Images[i].YS);
break;
default: // Normal
C.DrawTileClipped(Images[i].Img,Images[i].XS,Images[i].YS,0,0,Images[i].Img.MaterialUSize(),Images[i].Img.MaterialVSize());
}
}
for( i=0; i<Lines.Length; ++i )
{
C.SetPos(Lines[i].X,Lines[i].Y-YOffset);
if( (C.CurY+Lines[i].YS)<0 || Lines[i].Text=="" )
continue;
if( C.CurY>C.ClipY )
break;
// Check if mouse hovers over URL
if( bMouseOnClient && Lines[i].bHasURL && MX>=Lines[i].X && MX<=(Lines[i].X+Lines[i].XS)
&& MY>=Lines[i].Y && MY<=(Lines[i].Y+Lines[i].YS) )
{
HoverOverLinkLine = i;
bMouseOnClient = false; // No need to check on rest anymore.
C.DrawColor = Lines[i].ALColor;
}
else C.DrawColor = Lines[i].Color;
C.Font = Lines[i].Font;
C.DrawTextClipped(Lines[i].Text);
if( Lines[i].bHasURL )
{
YS = Max(Lines[i].YS/15,1);
C.SetPos(Lines[i].X,Lines[i].Y+Lines[i].YS-(YS*2)-YOffset);
if( C.CurY<C.ClipY )
C.DrawTileClipped(Texture'WhiteTexture',Lines[i].XS,YS,0,0,1,1);
}
}
if( OldHoverLine!=HoverOverLinkLine )
{
OldHoverLine = HoverOverLinkLine;
if( HoverOverLinkLine>=0 )
{
Controller.PlayInterfaceSound(CS_Hover);
SetToolTipText(Lines[HoverOverLinkLine].URL);
}
else SetToolTipText("");
}
}
function bool LaunchURL(GUIComponent Sender)
{
if( HoverOverLinkLine>=0 )
{
if( Left(Lines[HoverOverLinkLine].URL,8)~="kfurl://" )
LaunchKFURL(Mid(Lines[HoverOverLinkLine].URL,8));
else if( Left(Lines[HoverOverLinkLine].URL,5)~="kf://" )
ChangeGameURL(Mid(Lines[HoverOverLinkLine].URL,5));
else LaunchURLPage(Lines[HoverOverLinkLine].URL);
}
}
delegate LaunchKFURL( string URL );
delegate ChangeGameURL( string URL )
{
Class'SRLevelCleanup'.Static.AddSafeCleanup(PlayerOwner(),URL);
}
delegate LaunchURLPage( string URL )
{
PlayerOwner().Player.Console.DelayedConsoleCommand("START "$URL);
}
defaultproperties
{
bNeedsInit=true
PropagateVisibility=true
OnDraw=RenderHTMLText
OnClick=LaunchURL
Begin Object Class=GUIVertScrollBar Name=TheScrollbar
bBoundToParent=true
bScaleToParent=true
WinWidth=0.03
WinLeft=0.97
WinTop=0.0
WinHeight=1.0
bVisible=true
PropagateVisibility=true
OnPreDraw=TheScrollbar.GripPreDraw
End Object
MyScrollBar=TheScrollbar
StyleName="NoBackground"
bAcceptsInput=True
Begin Object Class=GUIToolTip Name=GUIListBoxBaseToolTip
ExpirationSeconds=0
End Object
ToolTip=GUIListBoxBaseToolTip
WhiteColor=(R=255,G=255,B=255,A=255)
BlueColor=(R=0,G=0,B=255,A=255)
RedColor=(R=255,G=0,B=0,A=255)
}

View File

@ -64,4 +64,5 @@ function DrawMenu()
defaultproperties
{
}

View File

@ -2,4 +2,5 @@ Class KFGUI_ScrollBarH extends KFGUI_ScrollBarBase;
defaultproperties
{
}

View File

@ -49,4 +49,5 @@ function DrawMenu()
defaultproperties
{
}

View File

@ -64,4 +64,5 @@ function PreDraw()
defaultproperties
{
}

View File

@ -68,4 +68,5 @@ final function RenderProgress()
defaultproperties
{
}

View File

@ -2,4 +2,5 @@ Class SRPerkManager extends ExtPerkManager;
defaultproperties
{
}

View File

@ -125,7 +125,7 @@ function DrawMenu()
if (KFGRI==None)
return;
}
bMeAdmin = (PC.WorldInfo.NetMode!=NM_Client || (ExtPlayerReplicationInfo(PC.PlayerReplicationInfo)!=None && ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).AdminType<=1));
bMeAdmin = (PC.WorldInfo.NetMode!=NM_Client || (ExtPlayerReplicationInfo(PC.PlayerReplicationInfo) != None && ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).AdminType <= AT_Admin));
// Sort player list.
for (i=(KFGRI.PRIArray.Length-1); i>0; --i)

View File

@ -257,4 +257,5 @@ function SetSaveVersion(int Num)
defaultproperties
{
}

View File

@ -0,0 +1,17 @@
class KFGI_Access extends Object
within KFGameInfo;
// Bypass protected modifier for these lists
function bool IsCustomZed(class<KFPawn_Monster> KFPM)
{
if (AIClassList.Find(KFPM) != INDEX_NONE) return false;
if (NonSpawnAIClassList.Find(KFPM) != INDEX_NONE) return false;
if (AIBossClassList.Find(KFPM) != INDEX_NONE) return false;
return true;
}
defaultproperties
{
}

View File

@ -26,7 +26,7 @@ struct FSavedInvEntry
};
var array<FSavedInvEntry> PlayerInv;
var config array<string> PerkClasses,CustomChars,AdminCommands,CustomItems,BonusGameSongs,BonusGameFX;
var config array<string> PerkClasses,CustomChars,AdminCommands,BonusGameSongs,BonusGameFX;
var config array<CFGCustomZedXP> CustomZedXP;
var array< class<Ext_PerkBase> > LoadedPerks;
var array<FCustomCharEntry> CustomCharList;
@ -44,9 +44,6 @@ var transient class<DamageType> LastKillDamageType;
var SoundCue BonusGameCue;
var Object BonusGameFXObj;
var array<FCustomTraderItem> CustomItemList;
var KFGFxObject_TraderItems CustomTrader;
const SettingsTagVer=14;
var KFGameReplicationInfo KF;
var config int SettingsInit;
@ -58,10 +55,8 @@ var int LastWaveNum,NumWaveSwitches;
var ExtSpawnPointHelper SpawnPointer;
var bool bRespawnCheck,bSpecialSpawn,bGameHasEnded,bIsPostGame;
var config bool bKillMessages,bDamageMessages,bEnableMapVote,bNoAdminCommands,bNoWebAdmin,bNoBoomstickJumping,bDumpXMLStats,bRagdollFromFall,bRagdollFromMomentum,bRagdollFromBackhit,bAddCountryTags,bThrowAllWeaponsOnDeath;
var config bool bServerPerksMode;
var config bool bDontUseOriginalWeaponry;
var config bool bAllowStandartPistolUpgrade;
var config bool bDisableCustomTrader;
var KFGI_Access KFGIA;
//Custom XP lightly array
struct CustomZedXPStruct
@ -102,6 +97,7 @@ function PostBeginPlay()
class'OnlineSubsystem'.Static.StringToUniqueNetId(DevList[i],Id);
DevNetID[i] = Id;
}
ServerStatLoader = new (None) class'ExtPlayerStat';
WorldInfo.Game.HUDType = class'KFExtendedHUD';
WorldInfo.Game.PlayerControllerClass = class'ExtPlayerController';
@ -110,12 +106,7 @@ function PostBeginPlay()
KFGameInfo(WorldInfo.Game).CustomizationPawnClass = class'ExtPawn_Customization';
KFGameInfo(WorldInfo.Game).KFGFxManagerClass = class'ExtMoviePlayer_Manager';
// trader things
if (!bDisableCustomTrader && CustomTrader==None)
{
CustomTrader = class'ExtPlayerReplicationInfo'.Static.CreateNewList();
SetTimer(0.001, false, 'EditTraiderItems');
}
KFGIA = new(KFGameInfo(WorldInfo.Game)) class'KFGI_Access';
if (ServerMOTD=="")
ServerMOTD = "Message of the Day";
@ -173,11 +164,6 @@ function PostBeginPlay()
if (SettingsInit<9)
MaxTopPlayers = 50;
if (SettingsInit < 13)
{
bAllowStandartPistolUpgrade = True;
bDisableCustomTrader = False;
}
if (SettingsInit < 14)
{
bThrowAllWeaponsOnDeath = False;
@ -289,96 +275,6 @@ function UpdateCustomZedXPArray()
}
}
// function CheckPickupFactories()
// {
// local KFPickupFactory_Item ItemFactory;
// // Disable 9mm and medpistol in all PickupFactories
// foreach AllActors(class'KFPickupFactory_Item', ItemFactory)
// {
// for (i=0;i<ItemFactory.ItemPickups.Length;i++)
// {
// if (ItemFactory.ItemPickups[i].ItemClass == class'KFGameContent.KFWeap_Pistol_9mm'
// || ItemFactory.ItemPickups[i].ItemClass == class'KFGameContent.KFWeap_Pistol_Medic')
// {
// ItemFactory.ItemPickups.Remove(i, 1);
// break;
// }
// }
// }
// }
function EditTraiderItems()
{
local int i;
local KFGFxObject_TraderItems Trad;
if (!bDontUseOriginalWeaponry)
{
Trad = KFGameReplicationInfo(WorldInfo.GRI).TraderItems;
// Remove dual 9mm, 9mm, medpistol
for (i=0;i<Trad.SaleItems.Length;i++)
{
if (string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Pistol_Dual9mm"
|| string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Pistol_Medic"
|| string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Pistol_9mm")
{
// Remove pistols
continue;
}
// Adding original weapon
AddCIToTraderEx(Trad.SaleItems[i].WeaponDef);
}
}
// Reinfo and resorting items
// MyKFGI.MyKFGRI.TraderItems.SetItemsInfo(MyKFGI.MyKFGRI.TraderItems.SaleItems);
// MyKFGI.MyKFGRI.TraderItems.SortItemsInfo(MyKFGI.MyKFGRI.TraderItems.SaleItems);
if (bAllowStandartPistolUpgrade)
{
// Add custom 9mm for upgrades
AddCIToTrader("ServerExt.ExtWeapDef_9mm");
// Add custom medpistol for upgrades
AddCIToTrader("ServerExt.ExtWeapDef_MedicPistol");
}
// Add custom items from WebAdmin
for (i=0; i<CustomItems.Length; ++i)
{
AddCIToTrader(CustomItems[i]);
}
InitGRIList();
}
function AddCIToTrader(string weapdef)
{
local FCustomTraderItem CI;
CI.WeaponDef = class<KFWeaponDefinition>(DynamicLoadObject(weapdef,class'Class'));
if (CI.WeaponDef == None)
return;
CI.WeaponClass = class<KFWeapon>(DynamicLoadObject(CI.WeaponDef.Default.WeaponClassPath,class'Class'));
if (CI.WeaponClass == None)
return;
CustomItemList.AddItem(CI);
class'ExtPlayerReplicationInfo'.Static.SetWeaponInfo(WorldInfo.NetMode==NM_DedicatedServer,CustomTrader.SaleItems.Length,CI,CustomTrader);
}
function AddCIToTraderEx(class<KFWeaponDefinition> weapdef)
{
local FCustomTraderItem CI;
CI.WeaponDef = weapdef;
if (CI.WeaponDef == None)
return;
CI.WeaponClass = class<KFWeapon>(DynamicLoadObject(CI.WeaponDef.Default.WeaponClassPath,class'Class'));
if (CI.WeaponClass == None)
return;
CustomItemList.AddItem(CI);
class'ExtPlayerReplicationInfo'.Static.SetWeaponInfo(WorldInfo.NetMode==NM_DedicatedServer,CustomTrader.SaleItems.Length,CI,CustomTrader);
}
static final function string GetStatFile(const out UniqueNetId UID)
{
return Repl(Default.StatFileDir,"%s","U_"$class'OnlineSubsystem'.Static.UniqueNetIdToString(UID));
@ -394,21 +290,6 @@ final function bool IsDev(const out UniqueNetId UID)
return false;
}
function InitGRIList()
{
local ExtPlayerController PC;
KFGameReplicationInfo(WorldInfo.GRI).TraderItems = CustomTrader;
// Must sync up local client.
if (WorldInfo.NetMode==NM_StandAlone)
{
foreach LocalPlayerControllers(class'ExtPlayerController',PC)
if (PC.PurchaseHelper!=None)
PC.PurchaseHelper.TraderItems = CustomTrader;
}
}
function CheckWave()
{
if (KF==None)
@ -527,46 +408,49 @@ function bool IsFromMod(Object O)
return true;
}
function bool HasModsInDamageInfo(DamageInfo DI)
{
local class<Actor> DamageCauser;
local class<KFDamageType> DamageType;
foreach DI.DamageCausers(DamageCauser)
if (IsFromMod(DamageCauser))
return true;
foreach DI.DamageTypes(DamageType)
if (IsFromMod(DamageType))
return true;
return false;
}
function CustomXP(Controller Killer, Controller Killed)
{
local KFPlayerController KFPC;
local KFPawn_Monster KFM;
local int i, j;
local int i;
local KFPlayerReplicationInfo DamagerKFPRI;
local float XP;
local KFPerk InstigatorPerk;
local bool cont;
local DamageInfo DamageInfo;
local class<KFPerk> DamagePerk;
KFM = KFPawn_Monster(Killed.Pawn);
for (i = 0; i < KFM.DamageHistory.Length; i++)
foreach KFM.DamageHistory(DamageInfo)
{
DamagerKFPRI = KFPlayerReplicationInfo(KFM.DamageHistory[i].DamagerPRI);
if (DamagerKFPRI != None)
{
// Check that no mods are used in this kill
cont = true;
for (j=0; j < KFM.DamageHistory[i].DamageCausers.Length; j++)
{
if (IsFromMod(KFM.DamageHistory[i].DamageCausers[j]) || IsFromMod(KFM.DamageHistory[i].DamageTypes[j]))
{
cont = false;
break;
}
}
if (cont && !IsFromMod(KFM))
{
// No mods - exit the loop, the game will add experience by itself
continue;
}
DamagerKFPRI = KFPlayerReplicationInfo(DamageInfo.DamagerPRI);
if (DamagerKFPRI == None) continue;
// if no mods - exit the loop, the game will add experience by itself
if (!HasModsInDamageInfo(DamageInfo) && !KFGIA.IsCustomZed(KFM.class)) continue;
// Distribute experience points
KFPC = KFPlayerController(DamagerKFPRI.Owner);
if (KFPC != none)
if (KFPC == None) continue;
i = CustomZedXPArray.Find('zedclass', KFM.Class);
if (i != INDEX_NONE)
{
j = CustomZedXPArray.Find('zedclass', KFM.Class);
if(j != -1)
{
XP = CustomZedXPArray[j].XPValues[MyKFGI.GameDifficulty];
XP = CustomZedXPArray[i].XPValues[MyKFGI.GameDifficulty];
}
else
{
@ -576,19 +460,17 @@ function CustomXP(Controller Killer, Controller Killed)
InstigatorPerk = KFPC.GetPerk();
// Special for survivalist - he gets experience for everything
// And for TF2Sentry - he has no perk in DamageHistory
if (InstigatorPerk.ShouldGetAllTheXP() || KFM.DamageHistory[i].DamagePerks.Length == 0)
// and for TF2Sentry - it has no perk in DamageHistory
if (InstigatorPerk.ShouldGetAllTheXP() || DamageInfo.DamagePerks.Length == 0)
{
KFPC.OnPlayerXPAdded(XP, InstigatorPerk.Class);
continue;
}
XP /= KFM.DamageHistory[i].DamagePerks.Length;
for (j = 0; j < KFM.DamageHistory[i].DamagePerks.Length; j++)
XP /= DamageInfo.DamagePerks.Length;
foreach DamageInfo.DamagePerks(DamagePerk)
{
KFPC.OnPlayerXPAdded(FCeil(XP), KFM.DamageHistory[i].DamagePerks[j]);
}
}
KFPC.OnPlayerXPAdded(FCeil(XP), DamagePerk);
}
}
}
@ -596,28 +478,34 @@ function CustomXP(Controller Killer, Controller Killed)
function ScoreKill(Controller Killer, Controller Killed)
{
local KFPlayerController KFPC;
local ExtPlayerController ExtPC;
local ExtPerkManager KillersPerk;
local KFPawn_Monster KFPM;
if (bRespawnCheck && Killed.bIsPlayer)
CheckRespawn(Killed);
if (KFPawn_Monster(Killed.Pawn) != None && Killed.GetTeamNum() != 0 && Killer.bIsPlayer && Killer.GetTeamNum() == 0)
KFPM = KFPawn_Monster(Killed.Pawn);
if (KFPM != None && Killed.GetTeamNum() != 0
&& Killer != None && Killer.bIsPlayer && Killer.GetTeamNum() == 0)
{
if (ExtPlayerController(Killer)!=None && ExtPlayerController(Killer).ActivePerkManager!=None)
ExtPlayerController(Killer).ActivePerkManager.PlayerKilled(KFPawn_Monster(Killed.Pawn),LastKillDamageType);
if (bKillMessages && Killer.PlayerReplicationInfo!=None)
BroadcastKillMessage(Killed.Pawn,Killer);
ExtPC = ExtPlayerController(Killer);
if (ExtPC != None && ExtPC.ActivePerkManager != None)
ExtPC.ActivePerkManager.PlayerKilled(KFPM, LastKillDamageType);
if (bKillMessages && Killer.PlayerReplicationInfo != None)
BroadcastKillMessage(Killed.Pawn, Killer);
CustomXP(Killer, Killed);
}
if (MyKFGI != None && MyKFGI.IsZedTimeActive() && KFPawn_Monster(Killed.Pawn) != None)
if (MyKFGI != None && MyKFGI.IsZedTimeActive() && KFPM != None)
{
KFPC = KFPlayerController(Killer);
if (KFPC != none)
if (KFPC != None)
{
KillersPerk = ExtPerkManager(KFPC.GetPerk());
if (MyKFGI.ZedTimeRemaining > 0.f && KillersPerk != none && KillersPerk.GetZedTimeExtensions( KFPC.GetLevel() ) > MyKFGI.ZedTimeExtensionsUsed)
if (MyKFGI.ZedTimeRemaining > 0.f && KillersPerk != None && KillersPerk.GetZedTimeExtensions(KFPC.GetLevel() ) > MyKFGI.ZedTimeExtensionsUsed)
{
MyKFGI.DramaticEvent(1.0);
MyKFGI.ZedTimeExtensionsUsed++;
@ -625,8 +513,10 @@ function ScoreKill(Controller Killer, Controller Killed)
}
}
if (ExtPlayerController(Killed) != None)
CheckPerkChange(ExtPlayerController(Killed));
ExtPC = ExtPlayerController(Killed);
if (ExtPC != None)
CheckPerkChange(ExtPC);
if (NextMutator != None)
NextMutator.ScoreKill(Killer, Killed);
}
@ -764,7 +654,7 @@ function NetDamage(int OriginalDamage, out int Damage, Pawn Injured, Controller
HackSetHistory(KFPawn(Injured),Injured,Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo).OwnerController,Damage,HitLocation);
}
}
else if (KFPawn(InstigatedBy.Pawn).GetTeamNum() != KFPawn(Injured).GetTeamNum())
else if (InstigatedBy.Pawn != None && KFPawn(InstigatedBy.Pawn).GetTeamNum() != KFPawn(Injured).GetTeamNum())
{
Momentum = vect(0,0,0);
Damage = 0;
@ -868,8 +758,6 @@ function NotifyLogin(Controller NewPlayer)
if (bAddCountryTags && NetConnection(PlayerController(NewPlayer).Player)!=None)
ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo).SetPlayerNameTag(class'CtryDatabase'.Static.GetClientCountryStr(PlayerController(NewPlayer).GetPlayerNetworkAddress()));
ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo).bIsDev = IsDev(NewPlayer.PlayerReplicationInfo.UniqueId);
if (WorldInfo.NetMode!=NM_StandAlone)
ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo).OnRepNextItem = GetNextItem;
if (BonusGameCue!=None || BonusGameFXObj!=None)
ExtPlayerController(NewPlayer).ClientSetBonus(BonusGameCue,BonusGameFXObj);
if (bRespawnCheck)
@ -1505,7 +1393,7 @@ final function InitCustomChars(ExtPlayerReplicationInfo PRI)
final function bool HasPrivs(ExtPlayerReplicationInfo P)
{
return WorldInfo.NetMode==NM_StandAlone || (P!=None && P.ShowAdminName() && (P.AdminType<=1 || P.AdminType==255));
return WorldInfo.NetMode==NM_StandAlone || (P != None && P.ShowAdminName() && (P.AdminType <= AT_Admin || P.AdminType == AT_Player));
}
function AdminCommand(ExtPlayerController PC, int PlayerID, int Action)
@ -1690,14 +1578,6 @@ function PlayerChangeSpec(ExtPlayerController PC, bool bSpectator)
}
}
function bool GetNextItem(ExtPlayerReplicationInfo PRI, int RepIndex)
{
if (RepIndex>=CustomItemList.Length)
return false;
PRI.ClientAddTraderItem(RepIndex,CustomItemList[RepIndex]);
return true;
}
function InitWebAdmin(ExtWebAdmin_UI UI)
{
local int i;
@ -1747,12 +1627,6 @@ function string WebAdminGetValue(name PropName, int ElementIndex)
return string(MaxTopPlayers);
case 'MinUnloadPerkLevel':
return string(MinUnloadPerkLevel);
case 'bDontUseOriginalWeaponry':
return string(bDontUseOriginalWeaponry);
case 'bDisableCustomTrader':
return string(bDisableCustomTrader);
case 'bAllowStandartPistolUpgrade':
return string(bAllowStandartPistolUpgrade);
case 'UnloadPerkExpCost':
return string(UnloadPerkExpCost);
case 'PerkClasses':
@ -1761,8 +1635,6 @@ function string WebAdminGetValue(name PropName, int ElementIndex)
return (ElementIndex==-1 ? string(CustomChars.Length) : CustomChars[ElementIndex]);
case 'AdminCommands':
return (ElementIndex==-1 ? string(AdminCommands.Length) : AdminCommands[ElementIndex]);
case 'CustomItems':
return (ElementIndex==-1 ? string(CustomItems.Length) : CustomItems[ElementIndex]);
case 'ServerMOTD':
return Repl(ServerMOTD,"|",Chr(10));
case 'BonusGameSongs':
@ -1824,12 +1696,6 @@ function WebAdminSetValue(name PropName, int ElementIndex, string Value)
bRagdollFromMomentum = bool(Value); break;
case 'bRagdollFromBackhit':
bRagdollFromBackhit = bool(Value); break;
case 'bDontUseOriginalWeaponry':
bDontUseOriginalWeaponry = bool(Value); break;
case 'bDisableCustomTrader':
bDisableCustomTrader = bool(Value); break;
case 'bAllowStandartPistolUpgrade':
bAllowStandartPistolUpgrade = bool(Value); break;
case 'bAddCountryTags':
bAddCountryTags = bool(Value); break;
case 'MaxTopPlayers':
@ -1842,8 +1708,6 @@ function WebAdminSetValue(name PropName, int ElementIndex, string Value)
UpdateArray(CustomChars,ElementIndex,Value); break;
case 'AdminCommands':
UpdateArray(AdminCommands,ElementIndex,Value); break;
case 'CustomItems':
UpdateArray(CustomItems,ElementIndex,Value); break;
case 'BonusGameSongs':
UpdateArray(BonusGameSongs,ElementIndex,Value); break;
case 'BonusGameFX':
@ -1858,6 +1722,8 @@ function WebAdminSetValue(name PropName, int ElementIndex, string Value)
defaultproperties
{
GroupNames.Add("ServerExt")
// Main devs
DevList.Add("0x0110000100E8984E") // Marco
DevList.Add("0x01100001023DF8A8") // ForrestMarkX
@ -1892,9 +1758,4 @@ defaultproperties
WebConfigs.Add((PropType=3,PropName="ServerMOTD",UIName="MOTD",UIDesc="Message of the Day"))
WebConfigs.Add((PropType=2,PropName="BonusGameSongs",UIName="Bonus Game Songs",UIDesc="List of custom musics to play during level change pong game.",NumElements=-1))
WebConfigs.Add((PropType=2,PropName="BonusGameFX",UIName="Bonus Game FX",UIDesc="List of custom FX to play on pong game.",NumElements=-1))
WebConfigs.Add((PropType=1,PropName="bDisableCustomTrader",UIName="Disable custom trader",UIDesc="Warning! That option will disable all settings below"))
WebConfigs.Add((PropType=2,PropName="CustomItems",UIName="Custom Inventory",UIDesc="List of custom inventory to add to trader (must be KFWeaponDefinition class).",NumElements=-1))
WebConfigs.Add((PropType=1,PropName="bDontUseOriginalWeaponry",UIName="Disable original weapons",UIDesc="Allows to buy default weapons"))
WebConfigs.Add((PropType=1,PropName="bAllowStandartPistolUpgrade",UIName="Standard pistol upgrades",UIDesc="Allows to upgrade standard pistol"))
}

View File

@ -34,4 +34,5 @@ event AllowBroadcastLocalizedTeam(int TeamIndex, actor Sender, class<LocalMessag
defaultproperties
{
}

61
builder.cfg Normal file
View File

@ -0,0 +1,61 @@
### Build parameters ###
# If True - compresses the mutator when compiling
# Scripts will be stored in binary form
# (reduces the size of the output file)
StripSource="True"
# Mutators to be compiled
# Specify them with a space as a separator,
# Mutators will be compiled in the specified order
PackageBuildOrder="ServerExt ServerExtMut"
### Brew parameters ###
# Packages you want to brew using @peelz's patched KFEditor.
# Useful for cases where regular brew doesn't put *.upk inside the package.
# Specify them with a space as a separator,
# The order doesn't matter
PackagePeelzBrew=""
### Steam Workshop upload parameters ###
# Mutators that will be uploaded to the workshop
# Specify them with a space as a separator,
# The order doesn't matter
PackageUpload="ServerExt ServerExtMut"
### Test parameters ###
# Map:
Map="KF-Nuked"
# Game:
# Survival: KFGameContent.KFGameInfo_Survival
# WeeklyOutbreak: KFGameContent.KFGameInfo_WeeklySurvival
# Endless: KFGameContent.KFGameInfo_Endless
# Objective: KFGameContent.KFGameInfo_Objective
# Versus: KFGameContent.KFGameInfo_VersusSurvival
Game="KFGameContent.KFGameInfo_Survival"
# Difficulty:
# Normal: 0
# Hard: 1
# Suicide: 2
# Hell: 3
Difficulty="0"
# GameLength:
# 4 waves: 0
# 7 waves: 1
# 10 waves: 2
GameLength="0"
# Mutators
Mutators="ServerExtMut.ServerExtMut"
# Additional parameters
Args=""

221
make.sh
View File

@ -1,221 +0,0 @@
#!/bin/bash
# Requirements: git-bash
# https://git-scm.com/download/win
set -Eeuo pipefail
trap cleanup SIGINT SIGTERM ERR EXIT
function winpath2unix () # $1: win path
{
echo "$*" | \
sed -r 's|^(.):|\\\1|' | \
sed 's|\\|/|g'
}
function unixpath2win () # $1: unix path
{
echo "$*" | \
sed -r 's|^/(.)|\1:|' | \
sed 's|/|\\|g'
}
function reg_readkey () # $1: path, $2: key
{
winpath2unix $(
reg query "$1" //v "$2" | \
grep -F "$2" | \
awk '{ $1=$2=""; print $0 }' )
}
function show_help ()
{
echo "$ScriptName"
echo "Usage:"
echo "${ScriptName} OPTION"
echo "Options:"
echo " -c, --compile"
echo " -b, --brew"
echo " -bu, --brew-unpublished"
echo " -u, --upload"
echo " -t, --test"
echo " -h, --help"
}
function cleanup()
{
trap - SIGINT SIGTERM ERR EXIT
restore_kfeditorconf
}
function get_latest_multini ()
{
local ApiUrl="https://api.github.com/repos/GenZmeY/multini/releases/latest"
local LatestTag=$(curl --silent "$ApiUrl" | grep -Po '"tag_name": "\K.*?(?=")')
local DownloadUrl="https://github.com/GenZmeY/multini/releases/download/$LatestTag/multini-windows-amd64.exe"
mkdir -p "$ThirdPartyBin"
curl -LJs "$DownloadUrl" -o "$ThirdPartyBin/multini.exe"
}
function backup_kfeditorconf ()
{
cp "$KFEditorConf" "$KFEditorConfBackup"
}
function restore_kfeditorconf ()
{
if [[ -f "$KFEditorConfBackup" ]]; then
mv -f "$KFEditorConfBackup" "$KFEditorConf"
fi
}
function setup_modpackages ()
{
multini --set "$KFEditorConf" 'ModPackages' 'ModPackages' 'ServerExt'
multini --add "$KFEditorConf" 'ModPackages' 'ModPackages' 'ServerExtMut'
multini --set "$KFEditorConf" 'ModPackages' 'ModPackagesInPath' "$(unixpath2win "$MutSource")"
}
function compiled ()
{
test -f "$MutStructScript/ServerExt.u" && test -f "$MutStructScript/ServerExtMut.u"
}
function compile ()
{
if ! command -v multini &> /dev/null; then
get_latest_multini
fi
backup_kfeditorconf && setup_modpackages
rm -rf "$MutUnpublish"
mkdir -p \
"$MutUnpublish" \
"$MutStructScript" \
"$MutStructPackages" \
"$MutStructLocalization"
cp -rf "$MutSource/Localization"/* "$MutStructLocalization"
cp -rf "$MutSource/ServerExtMut"/*.upk "$MutStructPackages"
CMD //C "$(unixpath2win "$KFEditor")" make -useunpublished &
local PID="$!"
while ps -p "$PID" &> /dev/null
do
if compiled ; then
kill "$PID"; break
fi
sleep 2
done
restore_kfeditorconf
}
function brew ()
{
echo "brew command is broken. Use --brew-unpublished or brew from WorkshopUploadToolGUI instead of this."
# CMD //C "$(unixpath2win "$KFEditor")" brewcontent -platform=PC ServerExt ServerExtMut -useunpublished
}
function brew_unpublished ()
{
rm -rf "$MutPublish"
if ! compiled ; then
compile
fi
cp -rf "$MutUnpublish" "$MutPublish"
}
function generate_wsinfo () # $1: package dir
{
local Description=$(cat "$MutPubContent/description.txt")
local Title=$(cat "$MutPubContent/title.txt")
local Preview=$(unixpath2win "$MutPubContent/preview.png")
local Tags=$(cat "$MutPubContent/tags.txt")
local PackageDir=$(unixpath2win "$1")
echo "\$Description \"$Description\"
\$Title \"$Title\"
\$PreviewFile \"$Preview\"
\$Tags \"$Tags\"
\$MicroTxItem \"false\"
\$PackageDirectory \"$PackageDir\"
" > "$MutWsInfo"
}
function upload ()
{
PackageDir=$(mktemp -d -u -p "$KFDoc")
cp -rf "$MutPublish"/* "$PackageDir"
generate_wsinfo "$PackageDir"
CMD //C "$(unixpath2win "$KFWorkshop")" "$MutWsInfoName"
rm -rf "$PackageDir"
rm -f "$MutWsInfo"
}
function create_default_testing_ini ()
{
echo "Map=\"KF-Nuked\"
Game=\"KFGameContent.KFGameInfo_Survival\"
Difficulty=\"0\"
GameLength=\"0\"
Mutators=\"ServerExtMut.ServerExtMut\"
Args=\"\"" > "$MutTestingIni"
}
function game_test ()
{
if ! [[ -r "$MutTestingIni" ]]; then
create_default_testing_ini
fi
source "$MutTestingIni"
CMD //C "$(unixpath2win "$KFGame")" ${Map}?Difficulty=${Difficulty}?GameLength=${GameLength}?Game=${Game}?Mutator=${Mutators}?${Args} -useunpublished -log
}
ScriptFullname=$(readlink -e "$0")
ScriptName=$(basename "$0")
ScriptDir=$(dirname "$ScriptFullname")
SteamPath=$(reg_readkey "HKCU\Software\Valve\Steam" "SteamPath")
DocumentsPath=$(reg_readkey "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" "Personal")
KFPath="$SteamPath/steamapps/common/killingfloor2"
KFBin="$KFPath/Binaries"
KFEditor="$KFBin/Win64/KFEditor.exe"
KFGame="$KFBin/Win64/KFGame.exe"
KFWorkshop="$KFBin/WorkshopUserTool.exe"
KFDoc="$DocumentsPath/My Games/KillingFloor2"
KFConfig="$KFDoc/KFGame/Config"
KFEditorConf="$KFConfig/KFEditor.ini"
KFEditorConfBackup="${KFEditorConf}.backup"
MutSource="$ScriptDir"
MutPubContent="$MutSource/PublicationContent"
MutUnpublish="$KFDoc/KFGame/Unpublished"
MutPublish="$KFDoc/KFGame/Published"
MutStructScript="$MutUnpublish/BrewedPC/Script"
MutStructPackages="$MutUnpublish/BrewedPC/Packages"
MutStructLocalization="$MutUnpublish/BrewedPC/Localization"
MutTestingIni="$MutSource/testing.ini"
MutWsInfoName="wsinfo_serverext.txt"
MutWsInfo="$KFDoc/$MutWsInfoName"
ThirdPartyBin="$MutSource/3rd-party-bin"
export PATH="$PATH:$ThirdPartyBin"
if [[ $# -eq 0 ]]; then show_help; exit 0; fi
case $1 in
-h|--help ) show_help ; ;;
-c|--compile ) compile ; ;;
-b|--brew ) brew ; ;;
-bu|--brew-unpublished ) brew_unpublished ; ;;
-u|--upload ) upload ; ;;
-t|--test ) game_test ; ;;
* ) echo "Command not recognized: $1"; exit 1;;
esac

1
tools Submodule

@ -0,0 +1 @@
Subproject commit fb458ac61f7e6c6426b8dff366dd5e7499e0d95f