diff --git a/.github/release_template.md b/.github/release_template.md index 015e5c302..dd6d32175 100644 --- a/.github/release_template.md +++ b/.github/release_template.md @@ -1,4 +1,6 @@ -Download `DXRandoInstaller.exe` in the Assets section under the changelog. +Download `DXRandoInstaller.exe` in the Assets section under the changelog. It is safe to update in the middle of a playthrough. + +For Nvidia users, setting ultra low latency mode for DXRando.exe can help performance. If you're having an issue with the installer program: [Manual Installation Instructions for Vanilla, Lay D Denton, GMDX, Revision, HX co-op, and Vanilla? Madder.](https://github.com/Die4Ever/deus-ex-randomizer/wiki/Installation-Instructions-and-performance-tweaks) diff --git a/.github/run-tests/action.yml b/.github/run-tests/action.yml index 5292177c9..b21a9f7ce 100644 --- a/.github/run-tests/action.yml +++ b/.github/run-tests/action.yml @@ -10,11 +10,9 @@ runs: - run: pip3 install -r installer/requirements.txt 2>&1 shell: bash + # download launchers - run: mkdir installer/3rdParty shell: bash - - run: mkdir installer/3rdParty/d3d10drv - shell: bash - # https://github.com/Defaultplayer001/Deus-Ex-Universe-Community-Update-/tree/master/%5B1.0%5D%20Deus%20Ex%20-%20Windows-Linux-macOS-Android/DXCU%20Installer%20Source/Mods/Community%20Update/System/Alternative%20EXEs - run: >- curl -Lo installer/3rdParty/KentieDeusExe.exe @@ -25,7 +23,34 @@ runs: "https://github.com/Defaultplayer001/Deus-Ex-Universe-Community-Update-/raw/master/%5B1.0%5D%20Deus%20Ex%20-%20Windows-Linux-macOS-Android/DXCU%20Installer%20Source/Mods/Community%20Update/System/Alternative%20EXEs/Launch.exe" shell: bash - # D3D10 stuff + # download D3D9 + - run: >- + curl -Lo installer/3rdParty/D3D9Drv.dll + "https://github.com/Defaultplayer001/Deus-Ex-Universe-Community-Update-/raw/master/%5B1.0%5D%20Deus%20Ex%20-%20Windows-Linux-macOS-Android/DXCU%20Installer%20Source/Mods/Community%20Update/System/D3D9Drv.dll" + shell: bash + - run: >- + curl -Lo installer/3rdParty/D3D9Drv.hut + "https://github.com/Defaultplayer001/Deus-Ex-Universe-Community-Update-/raw/master/%5B1.0%5D%20Deus%20Ex%20-%20Windows-Linux-macOS-Android/DXCU%20Installer%20Source/Mods/Community%20Update/System/D3D9Drv.hut" + shell: bash + - run: >- + curl -Lo installer/3rdParty/D3D9Drv.int + "https://github.com/Defaultplayer001/Deus-Ex-Universe-Community-Update-/raw/master/%5B1.0%5D%20Deus%20Ex%20-%20Windows-Linux-macOS-Android/DXCU%20Installer%20Source/Mods/Community%20Update/System/D3D9Drv.int" + shell: bash + + # download and extract OpenGL 2.0 https://github.com/Defaultplayer001/Deus-Ex-Universe-Community-Update-/tree/master/%5B1.0%5D%20Deus%20Ex%20-%20Windows-Linux-macOS-Android/CommunityUpdateFileArchiveDXPC/OpenGL + - run: >- + curl -Lo dxglr21.zip + "https://github.com/Defaultplayer001/Deus-Ex-Universe-Community-Update-/raw/master/%5B1.0%5D%20Deus%20Ex%20-%20Windows-Linux-macOS-Android/CommunityUpdateFileArchiveDXPC/OpenGL/dxglr21.zip" + shell: bash + - run: unzip dxglr21.zip + shell: bash + - run: cp OpenGLDrv.dll installer/3rdParty/OpenGLDrv.dll + shell: bash + + # download D3D10 stuff + - run: mkdir installer/3rdParty/d3d10drv + shell: bash + - run: >- curl -Lo installer/3rdParty/D3D10Drv.int "https://github.com/Defaultplayer001/Deus-Ex-Universe-Community-Update-/raw/master/%5B1.0%5D%20Deus%20Ex%20-%20Windows-Linux-macOS-Android/DXCU%20Installer%20Source/Mods/Community%20Update/System/D3D10Drv.int" @@ -97,6 +122,19 @@ runs: "https://github.com/LayDDentonProject/Lay-D-Denton-Project/releases/download/v1.1/FemJC.u" shell: bash + # download DXVK 32bit, the extracted folder has the version number in it like dxvk-2.3 + - run: >- + curl -Lo dxvk.tar.gz + "https://github.com/doitsujin/dxvk/releases/download/v2.3/dxvk-2.3.tar.gz" + shell: bash + - run: tar -xf dxvk.tar.gz + shell: bash + - run: mkdir installer/3rdParty/dxvk/ + shell: bash + - run: cp dxvk-*/x32/* installer/3rdParty/dxvk/ + shell: bash + + # check syntax and run tests - run: python3 -m compileall -q . 2>&1 shell: bash - run: python3 installer/tests.py 2>&1 @@ -112,7 +150,7 @@ runs: shell: bash # small test, use timeouts because on windows the GUI can catch errors and a dialog box will freeze the program - - run: timeout 30 ./dist/DXRandoInstaller --version 2>&1 + - run: timeout 30 ./dist/DXRandoInstaller --version --verbose 2>&1 shell: bash - run: timeout 30 ./dist/BingoViewer --version 2>&1 shell: bash @@ -135,5 +173,10 @@ runs: # working-directory: ./dist/BingoViewer # shell: bash + # make sure we got everything + - run: find installer/3rdParty/ + shell: bash + - run: find dist/ + shell: bash - run: ls -lah dist/ shell: bash diff --git a/.github/workflows/commit.yml b/.github/workflows/commit.yml index 903cb181e..b6094142e 100644 --- a/.github/workflows/commit.yml +++ b/.github/workflows/commit.yml @@ -1,6 +1,7 @@ name: commit -'on': - - push +on: + push: + paths: ['*', 'installer/**', '.github/**'] jobs: windows: diff --git a/.github/workflows/release-installer.yml b/.github/workflows/release-installer.yml index d62a8f1fa..01f693855 100644 --- a/.github/workflows/release-installer.yml +++ b/.github/workflows/release-installer.yml @@ -20,6 +20,7 @@ jobs: repo_token: ${{ secrets.GITHUB_TOKEN }} file: dist/DXRandoInstaller.exe asset_name: DXRandoInstaller.exe + tag: ${{ github.ref }} - name: Upload Release Asset BingoViewer id: upload-release-asset-bingoviewer @@ -28,6 +29,7 @@ jobs: repo_token: ${{ secrets.GITHUB_TOKEN }} file: dist/BingoViewer.exe asset_name: BingoViewer.exe + tag: ${{ github.ref }} linux: runs-on: ubuntu-latest @@ -50,6 +52,7 @@ jobs: repo_token: ${{ secrets.GITHUB_TOKEN }} file: dist/DXRandoInstaller asset_name: DXRandoInstaller-Linux + tag: ${{ github.ref }} - name: Upload Release Asset BingoViewer id: upload-release-asset-bingoviewer @@ -58,3 +61,4 @@ jobs: repo_token: ${{ secrets.GITHUB_TOKEN }} file: dist/BingoViewer asset_name: BingoViewer-Linux + tag: ${{ github.ref }} diff --git a/BingoDisplay.py b/BingoDisplay.py index c575cb910..704930ef6 100644 --- a/BingoDisplay.py +++ b/BingoDisplay.py @@ -5,6 +5,7 @@ import os.path import urllib.request import urllib.parse +import re from tkinter import filedialog as fd from tkinter import font from tkinter import messagebox @@ -40,6 +41,7 @@ def __init__(self,targetFile): self.height=500 self.selectedMod="" self.prevLines=None + self.bingoLineMatch = re.compile(r'bingoexport\[(?P\d+)\]=\(Event="(?P.*)",Desc="(?P.*)",Progress=(?P\d+),Max=(?P\d+),Active=(?P\d+)\)') self.initDrawnBoard() def closeWindow(self): @@ -139,20 +141,19 @@ def bingoNumberToCoord(self,bingoNumber): return (x,y) def parseBingoLine(self,bingoLine): - bingoNumber = int(bingoLine.split("[")[1].split("]")[0]) + bingoMatches=self.bingoLineMatch.match(bingoLine) + if (bingoMatches==None): + return + + bingoNumber=int(bingoMatches.group('key')) bingoCoord = self.bingoNumberToCoord(bingoNumber) - state = "=".join(bingoLine.split("=")[1:])[1:-1] - fields = state.split(",") + bingoItem = dict() - for field in fields: - split = field.split("=") - fieldName = split[0].lower() - fieldVal = split[1].replace('"',"") - try: - fieldVal = int(fieldVal) - except: - pass - bingoItem[fieldName]=fieldVal + bingoItem["event"]=bingoMatches.group('event') + bingoItem["desc"]=bingoMatches.group('desc') + bingoItem["progress"]=int(bingoMatches.group('progress')) + bingoItem["max"]=int(bingoMatches.group('max')) + bingoItem["active"]=int(bingoMatches.group('active')) self.board[bingoCoord[0]][bingoCoord[1]] = bingoItem diff --git a/DXRCore/DeusEx/Classes/DXRActorsBase.uc b/DXRCore/DeusEx/Classes/DXRActorsBase.uc index d5dd04dc3..2356faab9 100644 --- a/DXRCore/DeusEx/Classes/DXRActorsBase.uc +++ b/DXRCore/DeusEx/Classes/DXRActorsBase.uc @@ -13,7 +13,6 @@ struct FMinMax { }; struct safe_rule { - var string map; var name item_name; var vector min_pos; var vector max_pos; @@ -1269,13 +1268,36 @@ function Vector GetCenter(Actor test) return (MinVect+MaxVect)/2; } -function int GetSafeRule(safe_rule rules[64], name item_name, vector newpos) +function safe_rule FixSafeRule(safe_rule r) +{ + local float a, b; + r.min_pos *= coords_mult; + r.max_pos *= coords_mult; + + a = FMin(r.min_pos.X, r.max_pos.X); + b = FMax(r.min_pos.X, r.max_pos.X); + r.min_pos.X = a; + r.max_pos.X = b; + + a = FMin(r.min_pos.Y, r.max_pos.Y); + b = FMax(r.min_pos.Y, r.max_pos.Y); + r.min_pos.Y = a; + r.max_pos.Y = b; + + a = FMin(r.min_pos.Z, r.max_pos.Z); + b = FMax(r.min_pos.Z, r.max_pos.Z); + r.min_pos.Z = a; + r.max_pos.Z = b; + + return r; +} + +function int GetSafeRule(safe_rule rules[16], name item_name, vector newpos) { local int i; for(i=0; i npClass; @@ -51,6 +53,19 @@ function PreFirstEntryMapFixes() npClass.static.SpawnInfoDevice(self,class'#var(prefix)NewspaperOpen',vectm(1700.929810,-519.988037,57.729870),rotm(0,0,0),'02_Newspaper06'); //Joe Greene article, table in room next to break room (near bathrooms) npClass.static.SpawnInfoDevice(self,class'#var(prefix)NewspaperOpen',vectm(-1727.644775,2479.614990,1745.724976),rotm(0,0,0),'02_Newspaper06'); //Next to apartment(?) door on rooftops, near elevator + //Remove the small boxes in the sewers near the ladder so that bigger boxes don't shuffle into those spots + foreach AllActors(class'DeusExMover',d,'DrainGrate'){break;} + foreach d.RadiusActors(class'#var(prefix)BoxSmall',bs,800){bs.Destroy();} + + //A switch in the sewer swimming path to allow backtracking + AddSwitch( vect(-1518.989136,278.541260,-439.973816), rot(0, 2768, 0), 'DrainGrate'); + + //A keypad in the sewer walking path to allow backtracking + kp = Spawn(class'Keypad2',,,vectm(-622.685,497.4295,-60.437), rotm(0,-49192,0)); + kp.validCode="2577"; + kp.bToggleLock=False; + kp.Event='DoorToWarehouse'; + class'PlaceholderEnemy'.static.Create(self,vectm(782,-1452,48)); class'PlaceholderEnemy'.static.Create(self,vectm(1508,-1373,256)); class'PlaceholderEnemy'.static.Create(self,vectm(1814,-1842,48)); diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM04.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM04.uc index 8d76e87a1..0bcad6098 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM04.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM04.uc @@ -170,8 +170,8 @@ function AnyEntryMapFixes() switch (dxr.localURL) { -#ifdef vanilla case "04_NYC_HOTEL": +#ifdef vanilla NYC_04_CheckPaulUndead(); if( ! dxr.flagbase.GetBool('PaulDenton_Dead') ) SetTimer(1, True); @@ -182,8 +182,10 @@ function AnyEntryMapFixes() // conversations are transient, so they need to be fixed in AnyEntry FixConversationFlag(GetConversation('PaulAfterAttack'), 'M04RaidDone', true, 'PaulLeftHotel', true); FixConversationFlag(GetConversation('PaulDuringAttack'), 'M04RaidDone', false, 'PaulLeftHotel', false); - break; #endif + DeleteConversationFlag(GetConversation('FamilySquabbleWrapUpGilbertDead'), 'PlayerKilledRenton', false); + FixConversationFlag(GetConversation('SandraMadAtPlayer'), 'PlayerKilledRenton', true, 'AlwaysFalse', true); + break; #ifdef vanillamaps case "04_NYC_SMUG": diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM09.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM09.uc index 836bb1bb7..54ca08af3 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM09.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM09.uc @@ -33,6 +33,7 @@ function PreFirstEntryMapFixes() local Switch1 s; local #var(prefix)Barrel1 barrel; local #var(prefix)RatGenerator rg; + local #var(prefix)FlagTrigger ft; switch(dxr.localURL) { @@ -208,6 +209,26 @@ function PreFirstEntryMapFixes() barrel = #var(prefix)Barrel1(AddActor(class'#var(prefix)Barrel1', vect(-1112.480469,1120.735840,29.096186))); barrel.SkinColor = SC_Explosive; barrel.BeginPlay(); + + // just in case the gate keeper gets stuck, or if you wanna go fast! + foreach AllActors(class'#var(prefix)FlagTrigger', ft, 'CheckGateFlag') { + ft.FlagName = 'RingForService_Played'; + ft.SetCollision(false,false,false); + ft.SetCollisionSize(0,0); + } + foreach AllActors(class'#var(prefix)FlagTrigger', ft, 'MakeGateOpen') { + ft.SetCollision(false,false,false); + ft.SetCollisionSize(0,0); + } + + // allow jumping over the fence better, these are near the cars and the gatekeeper + foreach RadiusActors(class'BlockPlayer', bp, 385, vectm(682.616821, 1052.459473, 291.541748)) { + bp.bBlockPlayers = false; + } + // these are on the other side towards the tombstones + foreach RadiusActors(class'BlockPlayer', bp, 150, vectm(255.896591, 976.539551, 291.541748)) { + bp.bBlockPlayers = false; + } break; } } diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupVandenberg.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupVandenberg.uc index 81b633f70..1cafdd4b6 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupVandenberg.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupVandenberg.uc @@ -321,7 +321,11 @@ function TimerMapFixes() if (!M12GaryHostageBriefing && dxr.flagbase.GetBool('PageHostageBriefing_played') && !dxr.flagbase.GetBool('GaryHostageBriefing_played')){ if (player().conPlay==None){ foreach AllActors(class'#var(prefix)GarySavage',gary){ break;} - M12GaryHostageBriefing = (player().StartConversationByName('GaryHostageBriefing',gary)); + if (dxr.FlagBase.GetBool('LDDPJCIsFemale')){ + M12GaryHostageBriefing = (player().StartConversationByName('FemJCGaryHostageBriefing',gary)); + } else { + M12GaryHostageBriefing = (player().StartConversationByName('GaryHostageBriefing',gary)); + } } } break; diff --git a/DXRModules/DeusEx/Classes/DXRAutosave.uc b/DXRModules/DeusEx/Classes/DXRAutosave.uc index 24a6a0352..c042c3762 100644 --- a/DXRModules/DeusEx/Classes/DXRAutosave.uc +++ b/DXRModules/DeusEx/Classes/DXRAutosave.uc @@ -5,6 +5,11 @@ var config float save_delay; var transient float save_timer; var transient int autosave_combat; +var vector player_pos; +var rotator player_rot; +var bool set_player_pos; +var float old_game_speed; + const Disabled = 0; const FirstEntry = 1; const EveryEntry = 2; @@ -55,16 +60,28 @@ function NeedSave() bNeedSave = true; save_timer = save_delay; Enable('Tick'); - if(autosave_combat>0 || !PawnIsInCombat(player())) + if(old_game_speed==0) { + old_game_speed = Level.Game.GameSpeed; + } + if(autosave_combat>0 || !PawnIsInCombat(player())) { + autosave_combat = 1;// we're all in on this autosave because of the player rotation + if(!set_player_pos) { + set_player_pos = true; + player_pos = player().Location; + player_rot = player().ViewRotation; + } SetGameSpeed(0); + } } function SetGameSpeed(float s) { local MissionScript mission; + local int i; s = FMax(s, 0.01);// ServerSetSloMo only goes down to 0.1 if(s == Level.Game.GameSpeed) return; + l("SetGameSpeed " @ s); Level.Game.GameSpeed = s; Level.TimeDilation = s; Level.Game.SetTimer(s, true); @@ -75,6 +92,25 @@ function SetGameSpeed(float s) if(s <= 0.1) s /= 2;// might as well run the timer faster? foreach AllActors(class'MissionScript', mission) { mission.SetTimer(mission.checkTime * s, true); + i++; + } + // if no mission script found, clean up the traveling flag + if(i==0 && dxr.flagbase.GetBool('PlayerTraveling')) { + info("no missionscript found in " $ dxr.localURL); + dxr.flagbase.SetBool('PlayerTraveling', false); + } +} + +function FixPlayer(optional bool pos) +{ + local #var(PlayerPawn) p; + + if(set_player_pos) { + p=player(); + if(pos) p.SetLocation(player_pos); + p.ViewRotation = player_rot; + p.Velocity = vect(0,0,0); + p.Acceleration = vect(0,0,0); } } @@ -83,14 +119,17 @@ function Tick(float delta) delta /= Level.Game.GameSpeed; delta = FClamp(delta, 0.01, 0.05);// a single slow frame should not expire the timer by itself save_timer -= delta; + FixPlayer(); if(bNeedSave) { if(save_timer <= 0) { doAutosave(); } } else if(save_timer <= 0) { + if(Level.Game.GameSpeed == 1)// TODO: figure out what's wrong with using old_game_speed instead of 1 + Disable('Tick'); + FixPlayer(Level.Game.GameSpeed == 1); SetGameSpeed(1); - Disable('Tick'); } else { SetGameSpeed(0); } @@ -168,7 +207,7 @@ function doAutosave() info("doAutosave() " $ lastMission @ dxr.dxInfo.MissionNumber @ saveSlot @ saveName @ p.GetStateName() @ save_delay); bNeedSave = false; - SetGameSpeed(1); + SetGameSpeed(1);// TODO: figure out what's wrong with using old_game_speed instead of 1 class'DXRStats'.static.IncDataStorageStat(p, "DXRStats_autosaves"); p.SaveGame(saveSlot, saveName); SetGameSpeed(0); diff --git a/DXRModules/DeusEx/Classes/DXRDatacubes.uc b/DXRModules/DeusEx/Classes/DXRDatacubes.uc index cda1910ed..dc6a35dfe 100644 --- a/DXRModules/DeusEx/Classes/DXRDatacubes.uc +++ b/DXRModules/DeusEx/Classes/DXRDatacubes.uc @@ -1,6 +1,6 @@ class DXRDatacubes extends DXRPasswordsBase abstract; -var config safe_rule datacubes_rules[64]; +var safe_rule datacubes_rules[16]; var config float min_hack_adjust, max_hack_adjust; function CheckConfig() @@ -9,18 +9,15 @@ function CheckConfig() if( ConfigOlderThan(2,3,4,3) ) { min_hack_adjust = 0.5; max_hack_adjust = 1.5; - - for(i=0; i=ArrayCount(actor_watch)){ + err("Watched Actor list length exceeded!"); + return; + } + + actor_watch[num_watched_actors].a = a; + actor_watch[num_watched_actors].BingoEvent=eventName; + num_watched_actors++; +} + +function CheckWatchedActors() { + local int i; + + for (i=0;i(GetClassFromString(DecorationsOverwrites[i].type, class'DeusExDecoration')); + DecorationsOverwrites[i].bFlammable = c.default.bFlammable; + DecorationsOverwrites[i].Flammability = c.default.Flammability; + DecorationsOverwrites[i].bExplosive = c.default.bExplosive; + DecorationsOverwrites[i].explosionDamage = c.default.explosionDamage; + DecorationsOverwrites[i].explosionRadius = c.default.explosionRadius; + DecorationsOverwrites[i].bPushable = c.default.bPushable; + Super.CheckConfig(); for(i=0; i 0 && dxr.dxInfo.missionNumber < 99) { p.ReducedDamageType = 'All';// god mode - p.ServerSetSloMo(2); p.AllWeapons(); p.AllAmmo(); - if(dxr.localURL == "01_NYC_UNATCOISLAND") + if(dxr.localURL == "01_NYC_UNATCOISLAND") { p.ConsoleCommand("legend"); + } + +#ifdef injections + autosave = DXRAutosave(dxr.FindModule(class'DXRAutosave')); + if(autosave == None || !autosave.bNeedSave) { + p.ServerSetSloMo(2); + } else { + autosave.old_game_speed = 2; + } +#else + p.ServerSetSloMo(2); +#endif } //Disable achievements for Revision Rando, as requested @@ -438,6 +453,11 @@ function CheckConfig() Super.CheckConfig(); } +function FlagsSettings GetDifficulty(int diff) +{ + return difficulty_settings[diff]; +} + function FlagsSettings SetDifficulty(int new_difficulty) { local bool memes_enabled; @@ -568,6 +588,7 @@ function FlagsSettings SetDifficulty(int new_difficulty) bingo_duration = 1; bingo_scale = 0; + l("applying walton ware, DXRando: " $ dxr @ dxr.seed); SetGlobalSeed("random starting map"); settings.starting_map = class'DXRStartMap'.static.ChooseRandomStartMap(dxr,10); } @@ -646,137 +667,6 @@ simulated function AddDXRCredits(CreditsWindow cw) cw.PrintLn(); } -simulated function ExecMaxRando() -{ - // set local seed - // set a flag to save that we are in Max Rando mode - info("ExecMaxRando()"); - SetGlobalSeed("ExecMaxRando"); - maxrando = 1; - - RandomizeSettings(False); -} - -//Initialize the values that get tweaked by max rando -simulated function InitMaxRandoSettings() -{ - settings.merchants = difficulty_settings[difficulty].merchants; - settings.dancingpercent = difficulty_settings[difficulty].dancingpercent; - settings.medbots = difficulty_settings[difficulty].medbots; - settings.repairbots = difficulty_settings[difficulty].repairbots; - settings.enemiesrandomized=difficulty_settings[difficulty].enemiesrandomized; - settings.enemystats=difficulty_settings[difficulty].enemystats; - settings.enemies_nonhumans=difficulty_settings[difficulty].enemies_nonhumans; - settings.bot_weapons=difficulty_settings[difficulty].bot_weapons; - settings.bot_stats=difficulty_settings[difficulty].bot_stats; - settings.turrets_move=difficulty_settings[difficulty].turrets_move; - settings.turrets_add=difficulty_settings[difficulty].turrets_add; - settings.skills_reroll_missions=difficulty_settings[difficulty].skills_reroll_missions; - settings.minskill=difficulty_settings[difficulty].minskill; - settings.maxskill=difficulty_settings[difficulty].maxskill; - settings.banned_skills=difficulty_settings[difficulty].banned_skills; - settings.banned_skill_levels=difficulty_settings[difficulty].banned_skill_levels; - settings.ammo=difficulty_settings[difficulty].ammo; - settings.multitools=difficulty_settings[difficulty].multitools; - settings.lockpicks=difficulty_settings[difficulty].lockpicks; - settings.biocells=difficulty_settings[difficulty].biocells; - settings.medkits=difficulty_settings[difficulty].medkits; - settings.equipment = difficulty_settings[difficulty].equipment; - settings.min_weapon_dmg=difficulty_settings[difficulty].min_weapon_dmg; - settings.max_weapon_dmg=difficulty_settings[difficulty].max_weapon_dmg; - settings.min_weapon_shottime=difficulty_settings[difficulty].min_weapon_shottime; - settings.max_weapon_shottime=difficulty_settings[difficulty].max_weapon_shottime; - settings.enemyrespawn = difficulty_settings[difficulty].enemyrespawn; - settings.health = difficulty_settings[difficulty].health; - settings.energy = difficulty_settings[difficulty].energy; -} - -//Randomize the values. If forceMenuOptions is set, we will only allow the values to be set to -//the options available in DXRMenuSetupRando -simulated function RandomizeSettings(bool forceMenuOptions) -{ - info("RandomizeSettings("$string(forceMenuOptions)$")"); - - // change the flags normally configurable on the Advanced Settings page, but try to keep the difficulty balanced - // also make sure to randomize the doors mode and stuff - MaxRandoVal(settings.merchants); - MaxRandoVal(settings.dancingpercent); - MaxRandoVal(settings.medbots); - MaxRandoVal(settings.repairbots); - - settings.medbotuses = rng(7) + 1; - settings.repairbotuses = rng(7) + 1; - - settings.medbotcooldowns = int(rngb()) + 1;// 1 or 2 - settings.repairbotcooldowns = int(rngb()) + 1; - settings.medbotamount = int(rngb()) + 1; - settings.repairbotamount = int(rngb()) + 1; - - if (forceMenuOptions){ - //Eventually we can add logic to randomize between the door menu options - } else { - settings.doorsmode = undefeatabledoors + doorindependent; - settings.doorsdestructible = rng(100); - settings.doorspickable = rng(100); - } - - /* To match the menu options, we just randomize between 0, 25, 50, 75, and 100 */ - if (forceMenuOptions){ - settings.deviceshackable = rng(5)*25; - } else { - settings.deviceshackable = rng(100); - } - - MaxRandoVal(settings.enemiesrandomized); - settings.hiddenenemiesrandomized = settings.enemiesrandomized; - MaxRandoVal(settings.enemystats); - settings.enemiesshuffled = 100; - MaxRandoVal(settings.enemies_nonhumans); - - if(rngb()) { - settings.enemyrespawn = rng(120) + 120; - } else { - settings.enemyrespawn = 0; - } - - if(rngb()) { - settings.bot_weapons = 4; - } else { - settings.enemyrespawn = 0; - } - - if(rngb()) { - settings.bot_stats = 100; - } else { - settings.bot_stats = 0; - } - - MaxRandoVal(settings.turrets_move); - MaxRandoVal(settings.turrets_add); - - MaxRandoVal(settings.skills_reroll_missions); - settings.skills_independent_levels = int(rngb()); - MaxRandoValPair(settings.minskill, settings.maxskill); - MaxRandoVal(settings.banned_skills); - MaxRandoVal(settings.banned_skill_levels); - settings.skill_value_rando = 100; - - MaxRandoVal(settings.ammo); - MaxRandoVal(settings.multitools); - MaxRandoVal(settings.lockpicks); - MaxRandoVal(settings.biocells); - MaxRandoVal(settings.medkits); - settings.equipment += int(rngb()); - - MaxRandoValPair(settings.min_weapon_dmg, settings.max_weapon_dmg); - MaxRandoValPair(settings.min_weapon_shottime, settings.max_weapon_shottime); - - settings.aug_value_rando = 100; - - settings.health += rng(100) - 50; - MaxRandoVal(settings.energy); -} - simulated function TutorialDisableRandomization(bool enableSomeRando) { // a little bit of safe rando just to get a taste? @@ -940,119 +830,6 @@ function int ScoreFlags() return score * 5;// lazy multiply by 5 at the end } -function NewGamePlus() -{ - local #var(PlayerPawn) p; - local DataStorage ds; - local DXRSkills skills; - local DXRWeapons weapons; - local DXRAugmentations augs; - local int i, bingo_win,bingo_freespaces; - local float exp; - local int randomStart; - - if( flagsversion == 0 ) { - warning("NewGamePlus() flagsversion == 0"); - LoadFlags(); - } - p = player(); - - info("NewGamePlus()"); - seed++; - dxr.seed = seed; - NewPlaythroughId(); - ds = class'DataStorage'.static.GetObj(dxr); - if( ds != None ) ds.playthrough_id = playthrough_id; - newgameplus_loops++; - bingoBoardRoll=0; - p.saveCount=0; - exp = 1; - randomStart = settings.starting_map; - bingo_win = settings.bingo_win; - bingo_freespaces = settings.bingo_freespaces; - - // always enable maxrando when doing NG+? - maxrando = 1; - if(maxrando > 0) { - // rollback settings to the default for the current difficulty - // we only want to do this on maxrando because we want to retain the user's custom choices - SetDifficulty(difficulty); - ExecMaxRando(); - // increase difficulty on each flag like exp = newgameplus_loops; x *= 1.2 ^ exp; - exp = newgameplus_loops; - } - - SetGlobalSeed("NewGamePlus"); - p.CombatDifficulty=FClamp(p.CombatDifficulty*1.3,0,15); //Anything over 15 is kind of unreasonably impossible - NewGamePlusVal(settings.minskill, 1.2, exp, 1000); - NewGamePlusVal(settings.maxskill, 1.2, exp, 1500); - NewGamePlusVal(settings.enemiesrandomized, 1.2, exp, 1500); - NewGamePlusVal(settings.enemystats, 1.2, exp, 100); - NewGamePlusVal(settings.hiddenenemiesrandomized, 1.2, exp, 1500); - NewGamePlusVal(settings.ammo, 0.9, exp); - NewGamePlusVal(settings.medkits, 0.8, exp); - NewGamePlusVal(settings.multitools, 0.8, exp); - NewGamePlusVal(settings.lockpicks, 0.8, exp); - NewGamePlusVal(settings.biocells, 0.8, exp); - NewGamePlusVal(settings.medbots, 0.8, exp); - NewGamePlusVal(settings.repairbots, 0.8, exp); - NewGamePlusVal(settings.turrets_add, 1.3, exp, 1000); - NewGamePlusVal(settings.merchants, 0.9, exp); - settings.bingo_win = bingo_win; - settings.bingo_freespaces = bingo_freespaces; - if (randomStart!=0){ - settings.starting_map = class'DXRStartMap'.static.ChooseRandomStartMap(dxr,randomStart); - } - - if (p.KeyRing != None) - { - p.KeyRing.RemoveAllKeys(); - if ((Role == ROLE_Authority) && (Level.NetMode != NM_Standalone)) - { - p.KeyRing.ClientRemoveAllKeys(); - } - } - p.DeleteAllNotes(); - p.DeleteAllGoals(); - p.ResetConversationHistory(); - p.SetInHandPending(None); - p.SetInHand(None); - p.bInHandTransition = False; - p.RestoreAllHealth(); - - skills = DXRSkills(dxr.FindModule(class'DXRSkills')); - if( skills != None ) { - for(i=0; i<5; i++) - skills.DowngradeRandomSkill(p); - p.SkillPointsAvail /= 2; - } - else p.SkillPointsAvail = 0; - p.SkillPointsTotal = 0; //This value doesn't seem to actually get used in vanilla, but we use it for scoring - - augs = DXRAugmentations(dxr.FindModule(class'DXRAugmentations')); - if( augs != None ) - augs.RemoveRandomAug(p); - - weapons = DXRWeapons(dxr.FindModule(class'DXRWeapons')); - if( weapons != None ) - weapons.RemoveRandomWeapon(p); - - //Should you actually get fresh augs and credits on a NG+ non-vanilla start map? - //Technically it should make up for levels you skipped past, so maybe? - class'DXRStartMap'.static.AddStartingCredits(dxr,p); - class'DXRStartMap'.static.AddStartingAugs(dxr,p); - class'DXRStartMap'.static.AddStartingSkillPoints(dxr,p); - - info("NewGamePlus() deleting all flags"); - f.DeleteAllFlags(); - DeusExRootWindow(p.rootWindow).ResetFlags(); - info("NewGamePlus() deleted all flags"); - SaveFlags(); - p.bStartNewGameAfterIntro = true; - class'PlayerDataItem'.static.ResetData(p); - Level.Game.SendPlayer(p, "00_intro"); -} - function RunTests() { Super.RunTests(); diff --git a/DXRModules/DeusEx/Classes/DXRFlagsNGPMaxRando.uc b/DXRModules/DeusEx/Classes/DXRFlagsNGPMaxRando.uc new file mode 100644 index 000000000..d2b00b85e --- /dev/null +++ b/DXRModules/DeusEx/Classes/DXRFlagsNGPMaxRando.uc @@ -0,0 +1,292 @@ +class DXRFlagsNGPMaxRando extends DXRFlagsBase transient; + +simulated function ExecMaxRando() +{ + // set local seed + // set a flag to save that we are in Max Rando mode + info("ExecMaxRando()"); + SetGlobalSeed("ExecMaxRando"); + maxrando = 1; + + RandomizeSettings(False); +} + +//Initialize the values that get tweaked by max rando +simulated function InitMaxRandoSettings() +{ + local FlagsSettings difficulty_settings; + difficulty_settings = DXRFlags(self).GetDifficulty(difficulty); + + settings.merchants = difficulty_settings.merchants; + settings.dancingpercent = difficulty_settings.dancingpercent; + settings.medbots = difficulty_settings.medbots; + settings.repairbots = difficulty_settings.repairbots; + settings.enemiesrandomized=difficulty_settings.enemiesrandomized; + settings.enemystats=difficulty_settings.enemystats; + settings.enemies_nonhumans=difficulty_settings.enemies_nonhumans; + settings.bot_weapons=difficulty_settings.bot_weapons; + settings.bot_stats=difficulty_settings.bot_stats; + settings.turrets_move=difficulty_settings.turrets_move; + settings.turrets_add=difficulty_settings.turrets_add; + settings.skills_reroll_missions=difficulty_settings.skills_reroll_missions; + settings.minskill=difficulty_settings.minskill; + settings.maxskill=difficulty_settings.maxskill; + settings.banned_skills=difficulty_settings.banned_skills; + settings.banned_skill_levels=difficulty_settings.banned_skill_levels; + settings.ammo=difficulty_settings.ammo; + settings.multitools=difficulty_settings.multitools; + settings.lockpicks=difficulty_settings.lockpicks; + settings.biocells=difficulty_settings.biocells; + settings.medkits=difficulty_settings.medkits; + settings.equipment = difficulty_settings.equipment; + settings.min_weapon_dmg=difficulty_settings.min_weapon_dmg; + settings.max_weapon_dmg=difficulty_settings.max_weapon_dmg; + settings.min_weapon_shottime=difficulty_settings.min_weapon_shottime; + settings.max_weapon_shottime=difficulty_settings.max_weapon_shottime; + settings.enemyrespawn = difficulty_settings.enemyrespawn; + settings.health = difficulty_settings.health; + settings.energy = difficulty_settings.energy; +} + +//Randomize the values. If forceMenuOptions is set, we will only allow the values to be set to +//the options available in DXRMenuSetupRando +simulated function RandomizeSettings(bool forceMenuOptions) +{ + info("RandomizeSettings("$string(forceMenuOptions)$")"); + + // change the flags normally configurable on the Advanced Settings page, but try to keep the difficulty balanced + // also make sure to randomize the doors mode and stuff + MaxRandoVal(settings.merchants); + MaxRandoVal(settings.dancingpercent); + MaxRandoVal(settings.medbots); + MaxRandoVal(settings.repairbots); + + settings.medbotuses = rng(7) + 1; + settings.repairbotuses = rng(7) + 1; + + settings.medbotcooldowns = int(rngb()) + 1;// 1 or 2 + settings.repairbotcooldowns = int(rngb()) + 1; + settings.medbotamount = int(rngb()) + 1; + settings.repairbotamount = int(rngb()) + 1; + + if (forceMenuOptions){ + //Eventually we can add logic to randomize between the door menu options + } else { + settings.doorsmode = undefeatabledoors + doorindependent; + settings.doorsdestructible = rng(100); + settings.doorspickable = rng(100); + } + + /* To match the menu options, we just randomize between 0, 25, 50, 75, and 100 */ + if (forceMenuOptions){ + settings.deviceshackable = rng(5)*25; + } else { + settings.deviceshackable = rng(100); + } + + MaxRandoVal(settings.enemiesrandomized); + settings.hiddenenemiesrandomized = settings.enemiesrandomized; + MaxRandoVal(settings.enemystats); + settings.enemiesshuffled = 100; + MaxRandoVal(settings.enemies_nonhumans); + + if(rngb()) { + settings.enemyrespawn = rng(120) + 120; + } else { + settings.enemyrespawn = 0; + } + + if(rngb()) { + settings.bot_weapons = 4; + } else { + settings.enemyrespawn = 0; + } + + if(rngb()) { + settings.bot_stats = 100; + } else { + settings.bot_stats = 0; + } + + MaxRandoVal(settings.turrets_move); + MaxRandoVal(settings.turrets_add); + + MaxRandoVal(settings.skills_reroll_missions); + settings.skills_independent_levels = int(rngb()); + MaxRandoValPair(settings.minskill, settings.maxskill); + MaxRandoVal(settings.banned_skills); + MaxRandoVal(settings.banned_skill_levels); + settings.skill_value_rando = 100; + + MaxRandoVal(settings.ammo); + MaxRandoVal(settings.multitools); + MaxRandoVal(settings.lockpicks); + MaxRandoVal(settings.biocells); + MaxRandoVal(settings.medkits); + settings.equipment += int(rngb()); + + MaxRandoValPair(settings.min_weapon_dmg, settings.max_weapon_dmg); + MaxRandoValPair(settings.min_weapon_shottime, settings.max_weapon_shottime); + + settings.aug_value_rando = 100; + + settings.health += rng(100) - 50; + MaxRandoVal(settings.energy); +} + +function ClearDataVaultImages() +{ + local DataVaultImage image; + + while (player().FirstImage!=None){ + image = player().FirstImage; + player().FirstImage=image.nextImage; + //Theoretically if we were being more discriminating with the image deletion, + //we'd want to also fix the prevImage value, but since we're just trashing + //everything, it's unnecessary + image.Destroy(); + } +} + +function NewGamePlus() +{ + local #var(PlayerPawn) p; + local DataStorage ds; + local DXRSkills skills; + local DXRAugmentations augs; + local int i, bingo_win,bingo_freespaces; + local float exp; + local int randomStart; + + if( flagsversion == 0 ) { + warning("NewGamePlus() flagsversion == 0"); + LoadFlags(); + } + p = player(); + + info("NewGamePlus()"); + seed++; + dxr.seed = seed; + NewPlaythroughId(); + ds = class'DataStorage'.static.GetObj(dxr); + if( ds != None ) ds.playthrough_id = playthrough_id; + newgameplus_loops++; + bingoBoardRoll=0; + p.saveCount=0; + exp = 1; + randomStart = settings.starting_map; + bingo_win = settings.bingo_win; + bingo_freespaces = settings.bingo_freespaces; + + // always enable maxrando when doing NG+? + maxrando = 1; + if(maxrando > 0) { + // rollback settings to the default for the current difficulty + // we only want to do this on maxrando because we want to retain the user's custom choices + SetDifficulty(difficulty); + ExecMaxRando(); + // increase difficulty on each flag like exp = newgameplus_loops; x *= 1.2 ^ exp; + exp = newgameplus_loops; + } + + SetGlobalSeed("NewGamePlus"); + p.CombatDifficulty=FClamp(p.CombatDifficulty*1.3,0,15); //Anything over 15 is kind of unreasonably impossible + NewGamePlusVal(settings.minskill, 1.2, exp, 1000); + NewGamePlusVal(settings.maxskill, 1.2, exp, 1500); + NewGamePlusVal(settings.enemiesrandomized, 1.2, exp, 1500); + NewGamePlusVal(settings.enemystats, 1.2, exp, 100); + NewGamePlusVal(settings.hiddenenemiesrandomized, 1.2, exp, 1500); + NewGamePlusVal(settings.ammo, 0.9, exp); + NewGamePlusVal(settings.medkits, 0.8, exp); + NewGamePlusVal(settings.multitools, 0.8, exp); + NewGamePlusVal(settings.lockpicks, 0.8, exp); + NewGamePlusVal(settings.biocells, 0.8, exp); + NewGamePlusVal(settings.medbots, 0.8, exp); + NewGamePlusVal(settings.repairbots, 0.8, exp); + NewGamePlusVal(settings.turrets_add, 1.3, exp, 1000); + NewGamePlusVal(settings.merchants, 0.9, exp); + settings.bingo_win = bingo_win; + settings.bingo_freespaces = bingo_freespaces; + if (randomStart!=0){ + settings.starting_map = class'DXRStartMap'.static.ChooseRandomStartMap(dxr,randomStart); + } + + if (p.KeyRing != None) + { + p.KeyRing.RemoveAllKeys(); + if ((Role == ROLE_Authority) && (Level.NetMode != NM_Standalone)) + { + p.KeyRing.ClientRemoveAllKeys(); + } + } + p.DeleteAllNotes(); + p.DeleteAllGoals(); + p.ResetConversationHistory(); + p.RestoreAllHealth(); + ClearDataVaultImages(); + + skills = DXRSkills(dxr.FindModule(class'DXRSkills')); + if( skills != None ) { + for(i=0; i<5; i++) + skills.DowngradeRandomSkill(p); + p.SkillPointsAvail /= 2; + } + else p.SkillPointsAvail = 0; + p.SkillPointsTotal = 0; //This value doesn't seem to actually get used in vanilla, but we use it for scoring + + augs = DXRAugmentations(dxr.FindModule(class'DXRAugmentations')); + if( augs != None ) + augs.RemoveRandomAug(p); + + ClearInHand(p); + RemoveRandomWeapon(p); + + //Should you actually get fresh augs and credits on a NG+ non-vanilla start map? + //Technically it should make up for levels you skipped past, so maybe? + class'DXRStartMap'.static.AddStartingCredits(dxr,p); + class'DXRStartMap'.static.AddStartingAugs(dxr,p); + class'DXRStartMap'.static.AddStartingSkillPoints(dxr,p); + + info("NewGamePlus() deleting all flags"); + f.DeleteAllFlags(); + DeusExRootWindow(p.rootWindow).ResetFlags(); + info("NewGamePlus() deleted all flags"); + SaveFlags(); + p.bStartNewGameAfterIntro = true; + class'PlayerDataItem'.static.ResetData(p); + Level.Game.SendPlayer(p, "00_intro"); +} + +simulated function ClearInHand(#var(PlayerPawn) p) +{ + p.SetInHand(None); + p.SetInHandPending(None); + p.bInHandTransition = False; + p.LastinHand = None; + p.ClientinHandPending = None; + p.inHandPending = None; + p.inHand = None; +} + +simulated function RemoveRandomWeapon(#var(PlayerPawn) p) +{ + local Inventory i, next; + local Weapon weaps[64]; + local int numWeaps, slot; + + for( i = p.Inventory; i != None; i = next ) { + next = i.Inventory; + if( Weapon(i) == None ) continue; + weaps[numWeaps++] = Weapon(i); + } + + // don't take the player's only weapon + if( numWeaps <= 1 ) return; + + SetSeed( "RemoveRandomWeapon " $ numWeaps ); + + slot = rng(numWeaps); + info("RemoveRandomWeapon("$p$") Removing weapon "$weaps[slot]$", numWeaps was "$numWeaps); + p.DeleteInventory(weaps[slot]); + weaps[slot].Destroy(); +} diff --git a/DXRModules/DeusEx/Classes/DXRKeys.uc b/DXRModules/DeusEx/Classes/DXRKeys.uc index 9e2008aa6..68ac7c99a 100644 --- a/DXRModules/DeusEx/Classes/DXRKeys.uc +++ b/DXRModules/DeusEx/Classes/DXRKeys.uc @@ -1,23 +1,21 @@ class DXRKeys extends DXRActorsBase transient; -var config safe_rule keys_rules[64]; +var safe_rule keys_rules[16]; function CheckConfig() { local int i; - if( ConfigOlderThan(2,5,2,5) ) { - for(i=0; i 528.007446, X < 1047.852173, Y < 436.867401, Z > -1653.906006 == storage closet - // 1888.000000, 544.000000, -1536.000000 == glabs door, X > -414.152771 && X < 1888 && Y < 1930.014771 == before greasel labs - // 4856.000000, 3515.999512, -1816.000000 == crew quarters door - - keys_rules[i].map = "15_area51_entrance"; - keys_rules[i].item_name = 'Factory'; - keys_rules[i].min_pos = vect(-816, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = true; - i++; + switch(dxr.localURL) { + case "01_NYC_UNATCOISLAND": + keys_rules[i].item_name = 'UNhatchdoor'; + keys_rules[i].min_pos = vect(-999999, -999999, -999999); + keys_rules[i].max_pos = vect(999999, 999999, 999999); + keys_rules[i].allow = true; + i++; + break; + + case "03_NYC_AIRFIELD": + keys_rules[i].item_name = 'eastgate'; + keys_rules[i].min_pos = vect(1915, 2332, -999999); + keys_rules[i].max_pos = vect(5579, 4031, 999999); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'eastgate'; + keys_rules[i].min_pos = vect(-999999, -999999, -999999); + keys_rules[i].max_pos = vect(999999, 999999, 999999); + keys_rules[i].allow = true; + i++; + break; + + case "03_NYC_747": + keys_rules[i].item_name = 'lebedevdoor'; + keys_rules[i].min_pos = vect(570, -312, 153);//ban the annoying spot behind the crates + keys_rules[i].max_pos = vect(602, -280, 185); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'lebedevdoor'; + keys_rules[i].min_pos = vect(166, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "06_HONGKONG_WANCHAI_STREET": + // in DXRFixup we spawn an extra one anyways + keys_rules[i].item_name = 'JocksKey'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "06_HONGKONG_STORAGE": + keys_rules[i].item_name = 'NanoContainmentDoor'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "10_Paris_Chateau": + keys_rules[i].item_name = 'duclare_chateau'; + keys_rules[i].min_pos = vect(-99999, -99999, -125); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "11_Paris_Cathedral": + keys_rules[i].item_name = 'cath_maindoors'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + + keys_rules[i].item_name = 'cathedralgatekey'; + keys_rules[i].min_pos = vect(-4907, 1802, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "12_Vandenberg_Tunnels": + //disallow this weird locked room under water + keys_rules[i].item_name = 'control_room'; + keys_rules[i].min_pos = vect(-3521.955078, 3413.110352, -3246.771729); + keys_rules[i].max_pos = vect(-2844.053467, 3756.776855, -3097.210205); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'maintenancekey'; + keys_rules[i].min_pos = vect(-3521.955078, 3413.110352, -3246.771729); + keys_rules[i].max_pos = vect(-2844.053467, 3756.776855, -3097.210205); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'control_room'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + + keys_rules[i].item_name = 'maintenancekey'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "12_VANDENBERG_CMD": + //This key is added in DXRFixupVandenberg + //Allow anywhere from the Hazard Lab to the front guardhouse + keys_rules[i].item_name = 'TimsClosetKey'; + keys_rules[i].min_pos = vect(-2398.429688,2211.012695,-99999); + keys_rules[i].max_pos = vect(7217.410156,8535.856445,99999); + keys_rules[i].allow = true; + i++; + break; + + case "14_oceanlab_lab": + //disallow the crew quarters + keys_rules[i].item_name = 'crewkey'; + keys_rules[i].min_pos = vect(-99999, 3034, -99999); + keys_rules[i].max_pos = vect(3633, 99999, 99999); + keys_rules[i].allow = false; + i++; + + //disallow west (smaller X) of the flooded area + keys_rules[i].item_name = 'crewkey'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(-414.152771, 99999, 99999); + keys_rules[i].allow = false; + i++; + + //allow anything to the west (smaller X) of the crew quarters, except for the flooded area + keys_rules[i].item_name = 'crewkey'; + keys_rules[i].min_pos = vect(-414.152771, -99999, -99999); + keys_rules[i].max_pos = vect(4856, 99999, 99999); + keys_rules[i].allow = true; + i++; + + //disallow flooded area + keys_rules[i].item_name = 'storage'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(-414.152771, 99999, 99999); + keys_rules[i].allow = false; + i++; + + //disallow flooded area + keys_rules[i].item_name = 'Glab'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(-414.152771, 99999, 99999); + keys_rules[i].allow = false; + i++; + + //disallow storage closet + keys_rules[i].item_name = 'storage'; + keys_rules[i].min_pos = vect(528.007446, -99999, -1653.906006); + keys_rules[i].max_pos = vect(1047.852173, 436.867401, 99999); + keys_rules[i].allow = false; + i++; + + //disallow below storage + keys_rules[i].item_name = 'Glab'; + keys_rules[i].min_pos = vect(500, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, -1644.895142); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'storage'; + keys_rules[i].min_pos = vect(500, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, -1644.895142); + keys_rules[i].allow = false; + i++; + + //disallow east of greasel lab door + keys_rules[i].item_name = 'Glab'; + keys_rules[i].min_pos = vect(1879, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'storage'; + keys_rules[i].min_pos = vect(1879, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = false; + i++; + + //allow before greasel lab + keys_rules[i].item_name = 'storage'; + keys_rules[i].min_pos = vect(-414.152771, -99999, -99999); + keys_rules[i].max_pos = vect(1888, 1930.014771, 99999); + keys_rules[i].allow = true; + i++; + // X < -414.152771 == flooded area... + // X > 528.007446, X < 1047.852173, Y < 436.867401, Z > -1653.906006 == storage closet + // 1888.000000, 544.000000, -1536.000000 == glabs door, X > -414.152771 && X < 1888 && Y < 1930.014771 == before greasel labs + // 4856.000000, 3515.999512, -1816.000000 == crew quarters door + break; + + case "15_area51_entrance": + keys_rules[i].item_name = 'Factory'; + keys_rules[i].min_pos = vect(-816, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + } } function revision_keys_rules() { local int i; - keys_rules[i].map = "01_NYC_UNATCOISLAND"; - keys_rules[i].item_name = 'UNhatchdoor'; - keys_rules[i].min_pos = vect(-999999, -999999, -999999); - keys_rules[i].max_pos = vect(999999, 999999, 999999); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "03_NYC_AIRFIELD"; - keys_rules[i].item_name = 'eastgate'; - keys_rules[i].min_pos = vect(1915, 2332, -999999); - keys_rules[i].max_pos = vect(5579, 4031, 999999); - keys_rules[i].allow = false; - i++; - - keys_rules[i].map = "03_NYC_AIRFIELD"; - keys_rules[i].item_name = 'eastgate'; - keys_rules[i].min_pos = vect(-999999, -999999, -999999); - keys_rules[i].max_pos = vect(999999, 999999, 999999); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "03_NYC_747"; - keys_rules[i].item_name = 'lebedevdoor'; - keys_rules[i].min_pos = vect(340, -312, 140);//ban the annoying spot behind the crates - keys_rules[i].max_pos = vect(380, -280, 185); - keys_rules[i].allow = false; - i++; - - keys_rules[i].map = "03_NYC_747"; - keys_rules[i].item_name = 'lebedevdoor'; - keys_rules[i].min_pos = vect(166, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = true; - i++; - - // in DXRFixup we spawn an extra one anyways - keys_rules[i].map = "06_HONGKONG_WANCHAI_STREET"; - keys_rules[i].item_name = 'JocksKey'; - keys_rules[i].min_pos = vect(-99999, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "10_Paris_Chateau"; - keys_rules[i].item_name = 'duclare_chateau'; - keys_rules[i].min_pos = vect(-99999, -99999, -125); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "11_Paris_Cathedral"; - keys_rules[i].item_name = 'cath_maindoors'; - keys_rules[i].min_pos = vect(-99999, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "11_Paris_Cathedral"; - keys_rules[i].item_name = 'cathedralgatekey'; - keys_rules[i].min_pos = vect(-99999, -2664, -99999); - keys_rules[i].max_pos = vect(-3614, 99999, 99999); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "11_Paris_Cathedral"; - keys_rules[i].item_name = 'cathedralgatekey'; - keys_rules[i].min_pos = vect(-99999, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = false; - i++; - - //disallow this weird locked room under water - keys_rules[i].map = "12_Vandenberg_Tunnels"; - keys_rules[i].item_name = 'control_room'; - keys_rules[i].min_pos = vect(-3521.955078, 3413.110352, -3246.771729); - keys_rules[i].max_pos = vect(-2844.053467, 3756.776855, -3097.210205); - keys_rules[i].allow = false; - i++; - - keys_rules[i].map = "12_Vandenberg_Tunnels"; - keys_rules[i].item_name = 'maintenancekey'; - keys_rules[i].min_pos = vect(-3521.955078, 3413.110352, -3246.771729); - keys_rules[i].max_pos = vect(-2844.053467, 3756.776855, -3097.210205); - keys_rules[i].allow = false; - i++; - - keys_rules[i].map = "12_Vandenberg_Tunnels"; - keys_rules[i].item_name = 'control_room'; - keys_rules[i].min_pos = vect(-99999, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "12_Vandenberg_Tunnels"; - keys_rules[i].item_name = 'maintenancekey'; - keys_rules[i].min_pos = vect(-99999, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = true; - i++; - - //disallow the crew quarters - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'crewkey'; - keys_rules[i].min_pos = vect(-99999, 3034, -99999); - keys_rules[i].max_pos = vect(3633, 99999, 99999); - keys_rules[i].allow = false; - i++; - - //disallow west (smaller X) of the flooded area - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'crewkey'; - keys_rules[i].min_pos = vect(-99999, -99999, -99999); - keys_rules[i].max_pos = vect(-414.152771, 99999, 99999); - keys_rules[i].allow = false; - i++; - - //allow anything to the west (smaller X) of the crew quarters, except for the flooded area - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'crewkey'; - keys_rules[i].min_pos = vect(-414.152771, -99999, -99999); - keys_rules[i].max_pos = vect(4856, 99999, 99999); - keys_rules[i].allow = true; - i++; - - //disallow flooded area - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'olstorage'; - keys_rules[i].min_pos = vect(-99999, -99999, -99999); - keys_rules[i].max_pos = vect(-414.152771, 99999, 99999); - keys_rules[i].allow = false; - i++; - - //disallow flooded area - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'Glab'; - keys_rules[i].min_pos = vect(-99999, -99999, -99999); - keys_rules[i].max_pos = vect(-414.152771, 99999, 99999); - keys_rules[i].allow = false; - i++; - - //disallow storage closet - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'olstorage'; - keys_rules[i].min_pos = vect(528.007446, -99999, -1653.906006); - keys_rules[i].max_pos = vect(1047.852173, 436.867401, 99999); - keys_rules[i].allow = false; - i++; - - //disallow below storage - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'Glab'; - keys_rules[i].min_pos = vect(500, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, -1644.895142); - keys_rules[i].allow = false; - i++; - - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'olstorage'; - keys_rules[i].min_pos = vect(500, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, -1644.895142); - keys_rules[i].allow = false; - i++; - - //disallow east of greasel lab door - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'Glab'; - keys_rules[i].min_pos = vect(1879, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = false; - i++; - - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'olstorage'; - keys_rules[i].min_pos = vect(1879, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = false; - i++; - - //allow before greasel lab - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'olstorage'; - keys_rules[i].min_pos = vect(-414.152771, -99999, -99999); - keys_rules[i].max_pos = vect(1888, 1930.014771, 99999); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'NeoCarcharod'; - keys_rules[i].min_pos = vect(-99999, -99999, -2400); - keys_rules[i].max_pos = vect(512, 0, 99999); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "14_oceanlab_lab"; - keys_rules[i].item_name = 'NeoCarcharod'; - keys_rules[i].min_pos = vect(-99999, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = false; - i++; - - keys_rules[i].map = "15_area51_entrance"; - keys_rules[i].item_name = 'Factory'; - keys_rules[i].min_pos = vect(-3000, -99999, -99999); - keys_rules[i].max_pos = vect(99999, 99999, 99999); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "15_Area51_Page"; - keys_rules[i].item_name = 'exit_doors'; - keys_rules[i].min_pos = vect(-605.78,1140.87,212.5); - keys_rules[i].max_pos = vect(2059.9,-1583.3,313); - keys_rules[i].allow = true; - i++; - - keys_rules[i].map = "15_Area51_Page"; - keys_rules[i].item_name = 'exit_doors'; - keys_rules[i].min_pos = vect(-192,-1810,202); - keys_rules[i].max_pos = vect(-2392.5,22.78,350); - keys_rules[i].allow = true; - i++; + switch(dxr.localURL) { + case "01_NYC_UNATCOISLAND": + keys_rules[i].item_name = 'UNhatchdoor'; + keys_rules[i].min_pos = vect(-999999, -999999, -999999); + keys_rules[i].max_pos = vect(999999, 999999, 999999); + keys_rules[i].allow = true; + i++; + break; + + case "03_NYC_AIRFIELD": + keys_rules[i].item_name = 'eastgate'; + keys_rules[i].min_pos = vect(1915, 2332, -999999); + keys_rules[i].max_pos = vect(5579, 4031, 999999); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'eastgate'; + keys_rules[i].min_pos = vect(-999999, -999999, -999999); + keys_rules[i].max_pos = vect(999999, 999999, 999999); + keys_rules[i].allow = true; + i++; + break; + + case "03_NYC_747": + keys_rules[i].item_name = 'lebedevdoor'; + keys_rules[i].min_pos = vect(340, -312, 140);//ban the annoying spot behind the crates + keys_rules[i].max_pos = vect(380, -280, 185); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'lebedevdoor'; + keys_rules[i].min_pos = vect(166, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "06_HONGKONG_WANCHAI_STREET": + // in DXRFixup we spawn an extra one anyways + keys_rules[i].item_name = 'JocksKey'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "10_Paris_Chateau": + keys_rules[i].item_name = 'duclare_chateau'; + keys_rules[i].min_pos = vect(-99999, -99999, -125); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "11_Paris_Cathedral": + keys_rules[i].item_name = 'cath_maindoors'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + + keys_rules[i].item_name = 'cathedralgatekey'; + keys_rules[i].min_pos = vect(-99999, -2664, -99999); + keys_rules[i].max_pos = vect(-3614, 99999, 99999); + keys_rules[i].allow = true; + i++; + + keys_rules[i].item_name = 'cathedralgatekey'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = false; + i++; + break; + + case "12_Vandenberg_Tunnels": + //disallow this weird locked room under water + keys_rules[i].item_name = 'control_room'; + keys_rules[i].min_pos = vect(-3521.955078, 3413.110352, -3246.771729); + keys_rules[i].max_pos = vect(-2844.053467, 3756.776855, -3097.210205); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'maintenancekey'; + keys_rules[i].min_pos = vect(-3521.955078, 3413.110352, -3246.771729); + keys_rules[i].max_pos = vect(-2844.053467, 3756.776855, -3097.210205); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'control_room'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + + keys_rules[i].item_name = 'maintenancekey'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "14_oceanlab_lab": + //disallow the crew quarters + keys_rules[i].item_name = 'crewkey'; + keys_rules[i].min_pos = vect(-99999, 3034, -99999); + keys_rules[i].max_pos = vect(3633, 99999, 99999); + keys_rules[i].allow = false; + i++; + + //disallow west (smaller X) of the flooded area + keys_rules[i].item_name = 'crewkey'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(-414.152771, 99999, 99999); + keys_rules[i].allow = false; + i++; + + //allow anything to the west (smaller X) of the crew quarters, except for the flooded area + keys_rules[i].item_name = 'crewkey'; + keys_rules[i].min_pos = vect(-414.152771, -99999, -99999); + keys_rules[i].max_pos = vect(4856, 99999, 99999); + keys_rules[i].allow = true; + i++; + + //disallow flooded area + keys_rules[i].item_name = 'olstorage'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(-414.152771, 99999, 99999); + keys_rules[i].allow = false; + i++; + + //disallow flooded area + keys_rules[i].item_name = 'Glab'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(-414.152771, 99999, 99999); + keys_rules[i].allow = false; + i++; + + //disallow storage closet + keys_rules[i].item_name = 'olstorage'; + keys_rules[i].min_pos = vect(528.007446, -99999, -1653.906006); + keys_rules[i].max_pos = vect(1047.852173, 436.867401, 99999); + keys_rules[i].allow = false; + i++; + + //disallow below storage + keys_rules[i].item_name = 'Glab'; + keys_rules[i].min_pos = vect(500, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, -1644.895142); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'olstorage'; + keys_rules[i].min_pos = vect(500, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, -1644.895142); + keys_rules[i].allow = false; + i++; + + //disallow east of greasel lab door + keys_rules[i].item_name = 'Glab'; + keys_rules[i].min_pos = vect(1879, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = false; + i++; + + keys_rules[i].item_name = 'olstorage'; + keys_rules[i].min_pos = vect(1879, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = false; + i++; + + //allow before greasel lab + keys_rules[i].item_name = 'olstorage'; + keys_rules[i].min_pos = vect(-414.152771, -99999, -99999); + keys_rules[i].max_pos = vect(1888, 1930.014771, 99999); + keys_rules[i].allow = true; + i++; + + keys_rules[i].item_name = 'NeoCarcharod'; + keys_rules[i].min_pos = vect(-99999, -99999, -2400); + keys_rules[i].max_pos = vect(512, 0, 99999); + keys_rules[i].allow = true; + i++; + + keys_rules[i].item_name = 'NeoCarcharod'; + keys_rules[i].min_pos = vect(-99999, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = false; + i++; + break; + + case "15_area51_entrance": + keys_rules[i].item_name = 'Factory'; + keys_rules[i].min_pos = vect(-3000, -99999, -99999); + keys_rules[i].max_pos = vect(99999, 99999, 99999); + keys_rules[i].allow = true; + i++; + break; + + case "15_Area51_Page": + keys_rules[i].item_name = 'exit_doors'; + keys_rules[i].min_pos = vect(-605.78,1140.87,212.5); + keys_rules[i].max_pos = vect(2059.9,-1583.3,313); + keys_rules[i].allow = true; + i++; + + keys_rules[i].item_name = 'exit_doors'; + keys_rules[i].min_pos = vect(-192,-1810,202); + keys_rules[i].max_pos = vect(-2392.5,22.78,350); + keys_rules[i].allow = true; + i++; + break; + } } function FirstEntry() { - local int i; Super.FirstEntry(); - for(i=0; i= 1 && dxr.dxInfo.missionNumber <= 15 && list == None) err(dxr.localURL $ " list == None"); // old class for backwards compatibility foreach AllActors(class'#var(prefix)Businessman3', oldnpc, bindname) { diff --git a/DXRModules/DeusEx/Classes/DXRReplaceActors.uc b/DXRModules/DeusEx/Classes/DXRReplaceActors.uc index 0f39dad4b..9930e4cd0 100644 --- a/DXRModules/DeusEx/Classes/DXRReplaceActors.uc +++ b/DXRModules/DeusEx/Classes/DXRReplaceActors.uc @@ -87,6 +87,9 @@ function ReplaceActors() else if( #var(prefix)BarrelFire(a) != None ) { ReplaceGenericDecoration(a,class'DXRBarrelFire'); } + else if( a.class==class'#var(prefix)VendingMachine' ) { + ReplaceVendingMachine(#var(prefix)VendingMachine(a)); + } #ifdef gmdx else if( WeaponGEPGun(a) != None ) { ReplaceGepGun(WeaponGEPGun(a)); @@ -310,6 +313,21 @@ function ReplaceWaterCooler(#var(prefix)WaterCooler a) a.Destroy(); } +function ReplaceVendingMachine(#var(prefix)VendingMachine a) +{ + local DXRVendingMachine n; + n = DXRVendingMachine(SpawnReplacement(a, class'DXRVendingMachine')); + if(n == None) + return; + + n.SkinColor=a.SkinColor; + n.BeginPlay(); + // probably doesn't need this since it's all defaults + //ReplaceDecoration(a, n); + + a.Destroy(); +} + function ReplacePoolball(#var(prefix)Poolball a) { local DXRPoolball n; diff --git a/DXRModules/DeusEx/Classes/DXRStartMap.uc b/DXRModules/DeusEx/Classes/DXRStartMap.uc index e056fd42f..c2f8ad09e 100644 --- a/DXRModules/DeusEx/Classes/DXRStartMap.uc +++ b/DXRModules/DeusEx/Classes/DXRStartMap.uc @@ -358,10 +358,10 @@ static function bool BingoGoalImpossible(string bingo_event, int start_map, int case "FordSchick_Dead": return start_map>=20; case "M07MeetJaime_Played": - if (end_mission < 80){ + if (end_mission < 8){ return True; } - return start_map>=60; //Have to have told Jaime to meet you in Hong Kong in mission 50a + return start_map>=60; //Have to have told Jaime to meet you in Hong Kong in mission 5 case "VialAmbrosia_Activated": return start_map>=96; //Have to have started before the superfreighter upper decks (Arbitrarily chose 96 as that point) case "Terrorist_ClassDead": @@ -383,6 +383,12 @@ static function bool BingoGoalImpossible(string bingo_event, int start_map, int case "LouisBerates": case "IcarusCalls_Played": return start_map>100; //All these early Paris things - if we were to add a "Streets" starting location, this would need to be split more accurately + case "PresentForManderley": + //Have to be able to get Juan from mission 3 and bring him to the start of mission 4 + if (end_mission < 4){ + return True; + } + return start_map>40; default: return False; } diff --git a/DXRModules/DeusEx/Classes/DXRStats.uc b/DXRModules/DeusEx/Classes/DXRStats.uc index e400e5a3e..303d8f953 100644 --- a/DXRModules/DeusEx/Classes/DXRStats.uc +++ b/DXRModules/DeusEx/Classes/DXRStats.uc @@ -13,33 +13,32 @@ struct RunInfo }; var RunInfo runs[20]; +var int missions_times[16]; +var int missions_menu_times[16]; function AnyEntry() { - local int i, missions[16], total; - local string msg; + local int i; + local DeusExRootWindow root; + local HUDSpeedrunSplits splits; + Super.AnyEntry(); - if(dxr.flags.IsSpeedrunMode()) { - for(i=1; i 0 && dxr.dxInfo.MissionNumber <= 15) { - msg = "Total IGT: " $ fmtTimeToString(total); - for(i=dxr.dxInfo.MissionNumber; i>0; i--) { - if(missions[i] > 0) { - msg = msg $ ", Mission " $i$ ": " $ fmtTimeToString(missions[i]); - break; - } - } - msg = msg $ ", Deaths: " $ GetDataStorageStat(dxr, "DXRStats_deaths"); - player().ClientMessage(msg); - } - } else { - l("Total time so far: "$GetTotalTimeString()$", deaths so far: "$GetDataStorageStat(dxr, "DXRStats_deaths")); + root = DeusExRootWindow(player().rootWindow); + if(root != None) { +#ifdef injections + splits = root.splits; +#elseif hx + // do nothing +#else + splits = DXRandoRootWindow(root).splits; +#endif + if(splits != None) splits.InitStats(self); } SetTimer(0.1, True); @@ -53,39 +52,12 @@ simulated function ReEntry(bool IsTravel) } } -//Returns true when you aren't in a menu, or in the intro, etc. -function bool InGame() { -#ifdef hx - return true; -#endif - - if( player() == None ) - return false; - - if (player().InConversation()) { - return True; - } - - if (None == DeusExRootWindow(player().rootWindow)) { - return False; - } - - if (None == DeusExRootWindow(player().rootWindow).hud) { - return False; - } - - if (!DeusExRootWindow(player().rootWindow).hud.isVisible()){ - return False; - } - - return True; -} - function IncMissionTimer(int mission) { local string flagname, dataname; local name flag; local int time, ftime; + local bool bInGame; local DataStorage datastorage; @@ -97,7 +69,8 @@ function IncMissionTimer(int mission) //Track both the "success path" time (via flags) and //the complete time (via datastorage) - if (InGame()) { + bInGame = InGame(); + if (bInGame) { flagname = "DXRando_Mission"$mission$"_Timer"; dataname = "DXRando_Mission"$mission$"_Complete_Timer"; } else { @@ -112,6 +85,14 @@ function IncMissionTimer(int mission) time = int(datastorage.GetConfigKey(dataname)); time = Max(time, ftime); datastorage.SetConfig(dataname, time+1, 3600*24*366); + + if(mission >= 0 && mission < ArrayCount(missions_times)) { + if(bInGame) { + missions_times[mission] = time; + } else { + missions_menu_times[mission] = time; + } + } } function int GetCompleteMissionTime(int mission) @@ -193,7 +174,7 @@ function Timer() } -static function string fmtTimeToString(int time) +static function string fmtTimeToString(int time, optional bool hidehours, optional bool hidetenths, optional bool bShort) { local int hours,minutes,seconds,tenths,remain; local string timestr; @@ -207,10 +188,12 @@ static function string fmtTimeToString(int time) hours = (remain - minutes)/60; - if (hours < 10) { - timestr="0"; + if(hours>0 || !hidehours) { + if (hours < 10 && !bShort) { + timestr="0"; + } + timestr=timestr$hours$":"; } - timestr=timestr$hours$":"; if (minutes < 10) { timestr=timestr$"0"; @@ -220,7 +203,10 @@ static function string fmtTimeToString(int time) if (seconds < 10) { timestr=timestr$"0"; } - timestr=timestr$seconds$"."$tenths; + timestr=timestr$seconds; + if(!hidetenths) { + timestr = timestr $ "."$tenths; + } return timestr; } diff --git a/DXRModules/DeusEx/Classes/DXRWeapons.uc b/DXRModules/DeusEx/Classes/DXRWeapons.uc index cc355c679..2a90f9145 100644 --- a/DXRModules/DeusEx/Classes/DXRWeapons.uc +++ b/DXRModules/DeusEx/Classes/DXRWeapons.uc @@ -159,25 +159,3 @@ simulated function bool RandoProjectile(DeusExWeapon w, out class p, return true; } - -simulated function RemoveRandomWeapon(#var(PlayerPawn) p) -{ - local Inventory i; - local Weapon weaps[64]; - local int numWeaps, slot; - - for( i = p.Inventory; i != None; i = i.Inventory ) { - if( Weapon(i) == None ) continue; - weaps[numWeaps++] = Weapon(i); - } - - // don't take the player's only weapon - if( numWeaps <= 1 ) return; - - SetSeed( "RemoveRandomWeapon " $ numWeaps ); - - slot = rng(numWeaps); - info("RemoveRandomWeapon("$p$") Removing weapon "$weaps[slot]$", numWeaps was "$numWeaps); - p.DeleteInventory(weaps[slot]); - weaps[slot].Destroy(); -} diff --git a/DXRNonVanilla/DeusEx/Classes/DXRandoHUD.uc b/DXRNonVanilla/DeusEx/Classes/DXRandoHUD.uc index acf07986e..b6b90e5d7 100644 --- a/DXRNonVanilla/DeusEx/Classes/DXRandoHUD.uc +++ b/DXRNonVanilla/DeusEx/Classes/DXRandoHUD.uc @@ -10,7 +10,7 @@ event InitWindow() #ifndef vmd frobDisplay.Destroy(); - frobDisplay = FrobDisplayWindow(NewChild(Class'DXRFrobDisplayWindow')); - frobDisplay.SetWindowAlignments(HALIGN_Full, VALIGN_Full); + frobDisplay = FrobDisplayWindow(NewChild(Class'DXRFrobDisplayWindow')); + frobDisplay.SetWindowAlignments(HALIGN_Full, VALIGN_Full); #endif } diff --git a/DXRNonVanilla/DeusEx/Classes/DXRandoRootWindow.uc b/DXRNonVanilla/DeusEx/Classes/DXRandoRootWindow.uc index e82eb3e5c..b2b1e6476 100644 --- a/DXRNonVanilla/DeusEx/Classes/DXRandoRootWindow.uc +++ b/DXRNonVanilla/DeusEx/Classes/DXRandoRootWindow.uc @@ -4,6 +4,8 @@ class DXRandoRootWindow extends RevRootWindow; class DXRandoRootWindow extends DeusExRootWindow; #endif +var HUDSpeedrunSplits splits; + event InitWindow() { Super.InitWindow(); @@ -12,8 +14,17 @@ event InitWindow() hud = DeusExHUD(NewChild(Class'DXRandoHUD')); hud.UpdateSettings(DeusExPlayer(parentPawn)); hud.SetWindowAlignments(HALIGN_Full, VALIGN_Full, 0, 0); + + splits = HUDSpeedrunSplits(NewChild(Class'HUDSpeedrunSplits')); + splits.SetWindowAlignments(HALIGN_Full, VALIGN_Full, 0, 0); } +event DescendantRemoved(Window descendant) +{ + Super.DescendantRemoved(descendant); + if ( descendant == splits ) + splits = None; +} function bool GetNoPause(bool bNoPause) { local DXRFlags flags; diff --git a/DXRVanilla/DeusEx/Classes/DeusExHUD.uc b/DXRVanilla/DeusEx/Classes/DeusExHUD.uc index 671180b98..21265372a 100644 --- a/DXRVanilla/DeusEx/Classes/DeusExHUD.uc +++ b/DXRVanilla/DeusEx/Classes/DeusExHUD.uc @@ -20,20 +20,19 @@ event DescendantRemoved(Window descendant) function ConfigurationChanged() { - local float energyWidth, energyHeight; - local float width,hitHeight,compassHeight; + local float energyWidth, energyHeight; + local float width, hitHeight, compassHeight; _ConfigurationChanged(); //Put the energy display right under the compass if (energy != None) { - energy.QueryPreferredSize(energyWidth, energyHeight); + energy.QueryPreferredSize(energyWidth, energyHeight); if (compass!=None){compass.QueryPreferredSize(width,compassHeight);} if (hit!=None){hit.QueryPreferredSize(width,hitHeight);} - energy.ConfigureChild(0, hitHeight + compassHeight + 8, energyWidth, energyHeight); - + energy.ConfigureChild(0, hitHeight + compassHeight + 8, energyWidth, energyHeight); } } diff --git a/DXRVanilla/DeusEx/Classes/RootWindow.uc b/DXRVanilla/DeusEx/Classes/RootWindow.uc index 38829ab26..1dd285565 100644 --- a/DXRVanilla/DeusEx/Classes/RootWindow.uc +++ b/DXRVanilla/DeusEx/Classes/RootWindow.uc @@ -1,6 +1,23 @@ class DXRandoRootWindow merges DeusExRootWindow; // merges because DeusExRootWindow depends on int entries +var HUDSpeedrunSplits splits; + +event InitWindow() +{ + _InitWindow(); + + splits = HUDSpeedrunSplits(NewChild(Class'HUDSpeedrunSplits')); + splits.SetWindowAlignments(HALIGN_Full, VALIGN_Full, 0, 0); +} + +event DescendantRemoved(Window descendant) +{ + _DescendantRemoved(descendant); + if ( descendant == splits ) + splits = None; +} + function bool GetNoPause(bool bNoPause) { local DXRFlags flags; diff --git a/DXRando/DeusEx/Classes/DXRBinoculars.uc b/DXRando/DeusEx/Classes/DXRBinoculars.uc index 60da4c7c8..fab72a172 100644 --- a/DXRando/DeusEx/Classes/DXRBinoculars.uc +++ b/DXRando/DeusEx/Classes/DXRBinoculars.uc @@ -27,7 +27,8 @@ state DeActivated simulated function PreTravel() { - GoToState('DeActivated'); + if(Owner != None && GetStateName() == 'Activated') + GoToState('DeActivated'); } simulated function Timer() diff --git a/DXRando/DeusEx/Classes/DXRMissionIntro.uc b/DXRando/DeusEx/Classes/DXRMissionIntro.uc index d6a7842a5..db1cecae2 100644 --- a/DXRando/DeusEx/Classes/DXRMissionIntro.uc +++ b/DXRando/DeusEx/Classes/DXRMissionIntro.uc @@ -29,7 +29,7 @@ local DXRando dxr; } } else { if (!player.CanStartConversation()){ - log("ERROR: "$Self$": Unable to start intro conversation... State: "$player.GetStateName()$" Physics: "$player.Physics); + log("WARNING: "$Self$": Unable to start intro conversation... State: "$player.GetStateName()$" Physics: "$player.Physics); } } #endif diff --git a/DXRando/DeusEx/Classes/DXRVendingMachine.uc b/DXRando/DeusEx/Classes/DXRVendingMachine.uc new file mode 100644 index 000000000..1b9771d78 --- /dev/null +++ b/DXRando/DeusEx/Classes/DXRVendingMachine.uc @@ -0,0 +1,34 @@ +class DXRVendingMachine injects #var(prefix)VendingMachine; + +function Frob(actor Frobber, Inventory frobWith) +{ + local int usesBefore; + local DXRando dxr; + local String vendType; + + usesBefore = numUses; + + Super.Frob(Frobber,frobWith); + + if (usesBefore==0){ + return; + } + + //If you actually succeeded in buying something, mark purchase for the specific type and in general + foreach AllActors(class'DXRando', dxr) { + if (usesBefore!=0 && numUses!=usesBefore){ + if (SkinColor==SC_Drink){ + vendType="Drink"; + } else if (SkinColor==SC_Snack){ + vendType="Candy"; + } + class'DXREvents'.static.MarkBingo(dxr,"VendingMachineDispense_"$vendType); + class'DXREvents'.static.MarkBingo(dxr,"VendingMachineDispense"); + //Mark if you actually empty a machine + if (numUses==0){ + class'DXREvents'.static.MarkBingo(dxr,"VendingMachineEmpty_"$vendType); + class'DXREvents'.static.MarkBingo(dxr,"VendingMachineEmpty"); + } + } + } +} diff --git a/DXRando/DeusEx/Classes/TriggerEnable.uc b/DXRando/DeusEx/Classes/TriggerEnable.uc index b385bca50..7c49b2cb3 100644 --- a/DXRando/DeusEx/Classes/TriggerEnable.uc +++ b/DXRando/DeusEx/Classes/TriggerEnable.uc @@ -31,6 +31,9 @@ static function DXRTriggerEnable Create(Actor a, Name tag, Name Event) foreach t.AllActors(class'Actor', a, Event) { a.SetCollision(false, a.bBlockActors, a.bBlockPlayers); } + if(#defined(debug)) { + t.Trigger(None, None); + } return t; } diff --git a/DeusEx.u b/DeusEx.u index fcc257928..a7b0594fb 100644 Binary files a/DeusEx.u and b/DeusEx.u differ diff --git a/GMDXRandomizer.u b/GMDXRandomizer.u index bb0f0c535..7cd4fa81b 100644 Binary files a/GMDXRandomizer.u and b/GMDXRandomizer.u differ diff --git a/GUI/DeusEx/Classes/DXRMenuScreenNewGame.uc b/GUI/DeusEx/Classes/DXRMenuScreenNewGame.uc index 2f4627f0d..549f6fa0d 100644 --- a/GUI/DeusEx/Classes/DXRMenuScreenNewGame.uc +++ b/GUI/DeusEx/Classes/DXRMenuScreenNewGame.uc @@ -167,6 +167,7 @@ function SaveSettings() i.Destroy(); } player.Inventory = None; + dxr.flags.ClearInHand(#var(PlayerPawn)(player)); player.RestoreAllHealth(); if (DeusExRootWindow(player.rootWindow) != None) DeusExRootWindow(player.rootWindow).ResetFlags(); @@ -235,7 +236,7 @@ function ProcessAction(String actionKey) else { SaveSettings(); - AddTimer(0.1, false, 0, 'Timer');// timer to wait for items to be destroyed (issue #426) + AddTimer(0.11, false, 0, 'Timer');// timer to wait for items to be destroyed (issue #426), deletes happen every 100ms? probably don't need this anymore with our new ClearInHand() function } } else diff --git a/GUI/DeusEx/Classes/HUDSpeedrunSplits.uc b/GUI/DeusEx/Classes/HUDSpeedrunSplits.uc new file mode 100644 index 000000000..e0ca8043c --- /dev/null +++ b/GUI/DeusEx/Classes/HUDSpeedrunSplits.uc @@ -0,0 +1,340 @@ +//============================================================================= +// HUDEnergyDisplay +//============================================================================= +class HUDSpeedrunSplits expands HUDBaseWindow config(DXRSplits); + +var DeusExPlayer player; +var DXRStats stats; + +var config Font textfont; +var config int windowWidth, windowHeight; +var config Color colorBackground, colorText, colorBehind, colorBehindLosingTime, colorBehindGainingTime, colorAhead, colorAheadLosingTime, colorAheadGainingTime, colorBest, colorBestBehind, colorBestAhead; + +var config bool enabled, minimal; + +var config int PB[16]; +var config int Golds[16]; + +var int balanced_splits[16], balanced_splits_totals[16]; +var int PB_total, sum_of_bests; +var float left_col, center_col, text_height, x_pos, y_pos; + +// ---------------------------------------------------------------------- +// InitWindow() +// ---------------------------------------------------------------------- + +event InitWindow() +{ + Super.InitWindow(); + + player = DeusExPlayer(DeusExRootWindow(GetRootWindow()).parentPawn); + Hide(); +} + +function InitStats(DXRStats newstats) +{ + local int i, t, total, curMission, time; + local string msg; + local bool bNewPB; + + stats = newstats; + + if(stats == None || !stats.dxr.flags.IsSpeedrunMode()) { + Hide(); + return; + } + + curMission = stats.dxr.dxInfo.MissionNumber; + + for(i=1; i<=15; i++) { + PB_total += PB[i]; + if(Golds[i] == 0) Golds[i] = PB[i]; + if(Golds[i] > PB[i] && PB[i] != 0) Golds[i] = PB[i]; + sum_of_bests += Golds[i]; + } + for(i=1; i<=15; i++) { + balanced_splits[i] = BalancedSplit(i); + for(t=i; t0; i--) { + time = stats.missions_times[i]; + time += stats.missions_menu_times[i]; + if(time > 0) { + msg = msg $ ", Mission " $i$ ": " $ stats.fmtTimeToString(time); + break; + } + } + msg = msg $ ", Deaths: " $ stats.GetDataStorageStat(stats.dxr, "DXRStats_deaths"); + player.ClientMessage(msg); + + if(!enabled) { + Hide(); + return; + } + + if(curMission == 99) { + // write back new PBs and Golds + bNewPB = PB_total == 0 || total < PB_total; + for(i=1; i<=15; i++) { + time = stats.missions_times[i]; + time += stats.missions_menu_times[i]; + if(Golds[i] == 0 || time < Golds[i]) { + Golds[i] = time; + } + if(bNewPB) { + PB[i] = time; + } + } + } + SaveConfig(); + + if(curMission < 1 || curMission > 15) { + Hide(); + return; + } + + Show(); + StyleChanged(); +} + +function UpdatePos() +{ + local float hudWidth, hudHeight, beltWidth, beltHeight; + +#ifdef injections + local DeusExHUD hud; + hud = DeusExRootWindow(GetRootWindow()).hud; +#else + local DXRandoHUD hud; + hud = DXRandoHUD(DeusExRootWindow(GetRootWindow()).hud); +#endif + + if (hud != None) { + if(hud.belt != None) beltHeight = hud.belt.height; + } + x_pos = 0; + y_pos = GetRootWindow().height - beltHeight - windowHeight - 8; +} + +function DrawWindow(GC gc) +{ + local int i, t, prev, prevTime, prevDiff, cur, curTime, next, nextTime, time, total; + local float x, y, w, h, f, w2; + local int cur_totals[16]; + local string msg, s; + + if(stats == None) return; + + UpdatePos(); + Super.DrawWindow(gc); + gc.SetFont(textfont); + + total = TotalTime(); + cur = stats.dxr.dxInfo.MissionNumber; + curTime = stats.missions_times[cur]; + curTime += stats.missions_menu_times[cur]; + + for(i=1; i=1; i--) { + time = stats.missions_times[i]; + time += stats.missions_menu_times[i]; + if(time > 0) { + prev = i; + prevTime = time; + break; + } + } + + for(i=cur+1; i<=15; i++) { + if(balanced_splits[i] > 0) { + next = i; + nextTime = balanced_splits[next]; + break; + } + } + + // drawing text + x = 8; + y = 4; + if(left_col == 0) { + gc.GetTextExtent(0, left_col, text_height, "MXXii"); + gc.GetTextExtent(0, center_col, text_height, "+00:00"); + } + h = text_height; + + // previous split + if(prev > 0 && !minimal) { + time = cur_totals[prev] - balanced_splits_totals[prev]; + t = prevTime - balanced_splits[prev]; + msg = fmtTimeDiff(time); + + s = fmtTime(cur_totals[prev]); + DrawTextLine(gc, MissionName(prev), msg, GetCmpColor(time, t, prevTime, Golds[prev]), x, y, s); + } + y += h; + + // current/upcoming split, showing balanced PB time + if(!minimal) { + msg = fmtTime(balanced_splits_totals[cur]); + DrawTextLine(gc, MissionName(cur), msg, colorText, x, y); + } + y += h; + + // next split + if(next > 0 && !minimal) { + msg = fmtTime(balanced_splits_totals[next]); + DrawTextLine(gc, MissionName(next), msg, colorText, x, y); + } + y += h; + + if(minimal) y += h; + // current segment time with comparison + msg = fmtTimeSeg(curTime); + s = "/ " $ fmtTimeSeg(balanced_splits[cur]); + t = curTime - balanced_splits[cur]; + DrawTextLine(gc, "SEG:", msg, GetCmpColor(t, t), x, y, s); + y += h; + + // current overall time + time = cur_totals[prev] - balanced_splits_totals[prev]; + msg = fmtTime(total); + DrawTextLine(gc, "CUR:", msg, GetCmpColor(time, t), x, y); + y += h; + + // PB time + if(!minimal) { + msg = fmtTime(PB_total); + DrawTextLine(gc, "PB:", msg, colorText, x, y); + } + y += h; +} + +function string MissionName(int mission) +{ + if(mission < 10) return "M0" $ mission; + else return "M" $ mission; +} + +function DrawTextLine(GC gc, string header, string msg, Color c, int x, int y, optional string extra) +{ + local float w, h; + + x += x_pos; + y += y_pos; + + gc.SetTextColor(colorText); + gc.DrawText(x, y, windowWidth - x, text_height, header); + gc.SetTextColor(c); + + gc.GetTextExtent(0, w, h, header); + left_col = FMax(left_col, w); + text_height = FMax(text_height, h); + x += left_col; + + gc.DrawText(x, y, windowWidth - x, text_height, msg); + + if(extra == "") return; + + gc.GetTextExtent(0, w, h, msg); + center_col = FMax(center_col, w); + text_height = FMax(text_height, h); + x += center_col; + + gc.DrawText(x, y, windowWidth - x, text_height, " " $ extra); +} + +function Color GetCmpColor(int overall_diff, int diff, optional int segtime, optional int gold) +{ + if(overall_diff <= 0) { + if(gold > 0 && segtime < gold) return colorBestAhead; + else if(diff < 0) return colorAheadGainingTime; + else if(diff > 0) return colorAheadLosingTime; + else return colorAhead; + } else { + if(gold > 0 && segtime < gold) return colorBestBehind; + else if(diff < 0) return colorBehindGainingTime; + else if(diff > 0) return colorBehindLosingTime; + else return colorBehind; + } + return colorText; +} + +function string fmtTimeSeg(int time) +{ + return stats.fmtTimeToString(time, true, true, true); +} + +function string fmtTime(int time) +{ + return stats.fmtTimeToString(time, false, true, true); +} + +function string fmtTimeDiff(int diff) +{ + if(diff <= 0) return "-" $ stats.fmtTimeToString(-diff, true, true); + return "+" $ stats.fmtTimeToString(diff, true, true); +} + +function int BalancedSplit(int m) +{ + local int balanced_split_time; + local float ratio_of_game; + + ratio_of_game = float(Golds[m]) / float(sum_of_bests); + balanced_split_time = ratio_of_game * float(PB_total); + return balanced_split_time; +} + +function int TotalTime() +{ + local int i, total; + for(i=1; i int: v = BooleanVar(master=self.frame, value=True) @@ -84,8 +107,6 @@ def InitFlavorSettings(self, f: str, row, pad) -> int: row+=1 exe = StringVar(master=self.frame, value='Kentie') - if not IsWindows(): - exe.set('Launch') settings = { 'install': v, 'exe': exe } @@ -93,7 +114,7 @@ def InitFlavorSettings(self, f: str, row, pad) -> int: v = BooleanVar(master=self.frame, value=True) settings['mirrors'] = v c = Checkbutton(self.frame, text="Download mirrored maps for "+f, variable=v) - Hovertip(c, "Time to get lost again.") + Hovertip(c, "Time to get lost again. (This will check if you already have them.)") c.grid(column=1,row=row, sticky='SW', padx=pad*4, pady=pad) self.FixColors(c) row+=1 @@ -158,7 +179,9 @@ def _Install(self): } speedupfix = self.speedupfixval.get() - flavors = Install.Install(self.exe, flavors, speedupfix) + dxvk = self.dxvkval.get() + ogl2 = self.ogl2val.get() + flavors = Install.Install(self.exe, flavors, speedupfix, dxvk, ogl2) flavorstext = ', '.join(flavors.keys()) extra = '' if 'Vanilla' in flavors and IsWindows(): diff --git a/installer/Install/Install.py b/installer/Install/Install.py index d246173f6..0d72402ba 100644 --- a/installer/Install/Install.py +++ b/installer/Install/Install.py @@ -29,7 +29,9 @@ def UnattendedInstall(installpath:str, downloadmirrors): if downloadmirrors and 'Vanilla' in settings: settings['Vanilla']['mirrors'] = True - ret = Install(p, settings, True) + dxvk_default = IsWindows() and CheckVulkan() + + ret = Install(p, settings, speedupfix=True, dxvk=dxvk_default, ogl2=dxvk_default) def DetectFlavors(exe:Path) -> list: @@ -41,7 +43,7 @@ def DetectFlavors(exe:Path) -> list: return _DetectFlavors(system) -def Install(exe:Path, flavors:dict, speedupfix:bool) -> dict: +def Install(exe:Path, flavors:dict, speedupfix:bool, dxvk:bool, OGL2:bool=False) -> dict: assert exe.exists(), str(exe) assert exe.name.lower() == 'deusex.exe' system:Path = exe.parent @@ -52,7 +54,7 @@ def Install(exe:Path, flavors:dict, speedupfix:bool) -> dict: for(f, settings) in flavors.items(): ret={} if 'Vanilla'==f: - ret = InstallVanilla(system, settings, speedupfix) + ret = InstallVanilla(system, settings, speedupfix, Vulkan=dxvk, OGL2=OGL2) if 'Vanilla? Madder.'==f: ret = CreateModConfigs(system, settings, 'VMD', 'VMDSim') if 'GMDX v9'==f: @@ -71,12 +73,15 @@ def Install(exe:Path, flavors:dict, speedupfix:bool) -> dict: if speedupfix: EngineDllFix(system) + CopyDXVK(system, dxvk) + InstallOGL2(system, OGL2) + debug("Install returning", flavors) return flavors -def InstallVanilla(system:Path, settings:dict, speedupfix:bool): +def InstallVanilla(system:Path, settings:dict, speedupfix:bool, Vulkan:bool, OGL2:bool): gameroot = system.parent if settings.get('LDDP'): @@ -105,7 +110,7 @@ def InstallVanilla(system:Path, settings:dict, speedupfix:bool): CopyTo(ini, defini_dest) if kentie: - configs_dest = GetDocumentsDir() / 'Deus Ex' / 'System' + configs_dest = GetDocumentsDir(system) / 'Deus Ex' / 'System' Mkdir(configs_dest.parent /'SaveDXRando', exist_ok=True, parents=True) else: configs_dest = system @@ -131,9 +136,21 @@ def InstallVanilla(system:Path, settings:dict, speedupfix:bool): if 'D3D10Drv.D3D10RenderDevice' not in changes: changes['D3D10Drv.D3D10RenderDevice'] = {} changes['D3D10Drv.D3D10RenderDevice'].update({'FPSLimit': '120', 'VSync': 'True'}) - - if not IsWindows(): - changes['Engine.Engine'] = {'GameRenderDevice': 'D3DDrv.D3DRenderDevice'} + elif exename == 'DeusEx': # ensure we don't retain bad settings from old vanilla configs since we use the same exe file name? + if 'DeusExe' not in changes: + changes['DeusExe'] = {} + changes['DeusExe'].update({'FPSLimit': '0'}) + if 'D3D10Drv.D3D10RenderDevice' not in changes: + changes['D3D10Drv.D3D10RenderDevice'] = {} + changes['D3D10Drv.D3D10RenderDevice'].update({'FPSLimit': '0', 'VSync': 'False'}) + + if IsWindows() and not Vulkan: + changes['Engine.Engine'] = {'GameRenderDevice': 'D3D9Drv.D3D9RenderDevice'} + elif not IsWindows(): + if OGL2: + changes['Engine.Engine'] = {'GameRenderDevice': 'OpenGLDrv.OpenGLRenderDevice'} + else: + changes['Engine.Engine'] = {'GameRenderDevice': 'D3D9Drv.D3D9RenderDevice'} if 'WinDrv.WindowsClient' not in changes: changes['WinDrv.WindowsClient'] = {'StartupFullscreen': 'True'} @@ -150,7 +167,7 @@ def InstallVanilla(system:Path, settings:dict, speedupfix:bool): Mkdir((dxrroot / 'Maps'), exist_ok=True, parents=True) Mkdir((dxrroot / 'System'), exist_ok=True, parents=True) CopyPackageFiles('vanilla', gameroot, ['DeusEx.u']) - CopyD3D10Renderer(system) + CopyD3DRenderers(system) FemJCu = GetSourcePath() / '3rdParty' / "FemJC.u" CopyTo(FemJCu, dxrroot / 'System' / 'FemJC.u') @@ -203,7 +220,7 @@ def InstallGMDX(system:Path, settings:dict, exename:str): Mkdir(game/'SaveGMDXRando', exist_ok=True) # GMDX uses absolute path shortcuts with ini files in their arguments, so it's not as simple to copy their exe - confpath = GetDocumentsDir() / 'Deus Ex' / exename / 'System' / 'gmdx.ini' + confpath = GetDocumentsDir(system) / 'Deus Ex' / exename / 'System' / 'gmdx.ini' if confpath.exists(): b = confpath.read_bytes() b = Config.ModifyConfig(b, changes, additions) diff --git a/installer/Install/MapVariants.py b/installer/Install/MapVariants.py index c2bc0c9a0..aced214b5 100644 --- a/installer/Install/MapVariants.py +++ b/installer/Install/MapVariants.py @@ -13,9 +13,11 @@ '8d06331fdc7fcc6904c316bbb94a4598': 'v0.8', '5551a03906a0f5470e2f9bd8724d59a6': 'v0.9', '4a2b4cb284de0799ce0f111cfd8170fc': 'v0.9.1', - '85578e974c9b98ea45885e3c18e902ce': 'v0.9.2', + '3aee6ad2d9f88286b9ab105fb18109e2': 'v0.9.2', # after changing Md5Maps function + 'c6bd4612828f025bdcd4cfd25655b4a3': 'v0.9.3', + '3b678c5b4f4b7fcd26f2c30ffec14770': 'v0.9.4', } -latest_maps = 'v0.9.2' +latest_maps = 'v0.9.4' assert latest_maps in maps_versions.values() def InstallMirrors(mapsdir: Path, callback: callable, flavor:str): @@ -27,10 +29,10 @@ def InstallMirrors(mapsdir: Path, callback: callable, flavor:str): version = maps_versions.get(totalmd5) if version == latest_maps: - info('already have mirrored maps', version) + info('already have mirrored maps', version, totalmd5) return elif version: - info('overwriting mirrored maps', version) + info('overwriting mirrored maps', version, totalmd5) else: info('unknown existing maps MD5:', totalmd5) @@ -66,8 +68,13 @@ def InstallMirrors(mapsdir: Path, callback: callable, flavor:str): temp.unlink() def Md5Maps(mapsdir: Path) -> str: - md5s = '' - for f in mapsdir.glob('*_-1_1_1.dx'): + Md5sArr = [] + for f in mapsdir.glob('*.dx'): data = f.read_bytes() - md5s += MD5(data) - return MD5(md5s.encode('utf-8')) + t = MD5(data) + debug('MD5 of map', f.name, t) + Md5sArr.append(t) + Md5sArr.sort() + Md5sStr = ','.join(Md5sArr) + debug('Md5Maps all hashes', Md5sStr) + return MD5(Md5sStr.encode('utf-8')) diff --git a/installer/Install/__init__.py b/installer/Install/__init__.py index 1e3805f70..74bdc549c 100644 --- a/installer/Install/__init__.py +++ b/installer/Install/__init__.py @@ -7,7 +7,7 @@ def debug(*args): global outfile - if GetDryrun(): + if GetVerbose(): print(*args) if outfile: print(*args, file=outfile) @@ -28,6 +28,7 @@ def info(*args): import urllib.request import certifi import ssl + import subprocess except Exception as e: info('ERROR: importing', e) raise @@ -53,6 +54,21 @@ def GetDryrun() -> bool: def IsWindows() -> bool: return os.name == 'nt' +def CheckVulkan() -> bool: + if not IsWindows(): + return False # no easy way to detect Vulkan on Linux, they don't need DXVK anyways + try: + info('CheckVulkan') + ret = subprocess.run(['vulkaninfo', '--summary'], text=True, capture_output=True, timeout=30) + debug(ret.stdout) + debug(ret.stderr) + info('CheckVulkan got:', not ret.returncode) + return not ret.returncode + except Exception as e: + info(e) + return False + + def GetConfChanges(modname): changes = { 'Engine.Engine': { @@ -87,11 +103,18 @@ def GetSourcePath() -> Path: raise RuntimeError('failed to GetSourcePath()', p) -def GetDocumentsDir() -> Path: +def GetDocumentsDir(system:Path) -> Path: if not IsWindows(): - p = Path.home() - info('GetDocumentsDir() == ', p) - assert p.exists() + if 'Steam' in system.parts: + idx = system.parts.index('Steam') + p = system.parents[idx] + info('GetDocumentsDir() == ', p) + p = p /'steamapps'/'compatdata'/'6910'/'pfx'/'drive_c'/'users'/'steamuser'/'Documents' + info('GetDocumentsDir() == ', p) + Mkdir(p, True, True) + else: + p = Path.home() + assert p.exists(), str(p) return p try: import ctypes.wintypes @@ -240,9 +263,13 @@ def ModifyConfig(defconfig:Path, config:Path, outdefconfig:Path, outconfig:Path, WriteBytes(outconfig, bytes) -def CopyD3D10Renderer(system:Path): +def CopyD3DRenderers(system:Path): thirdparty = GetSourcePath() / '3rdParty' - info('CopyD3D10Renderer from', thirdparty, ' to ', system) + info('CopyD3DRenderers from', thirdparty, ' to ', system) + + CopyTo(thirdparty/'D3D9Drv.dll', system/'D3D9Drv.dll', True) + CopyTo(thirdparty/'D3D9Drv.hut', system/'D3D9Drv.hut', True) + CopyTo(thirdparty/'D3D9Drv.int', system/'D3D9Drv.int', True) CopyTo(thirdparty/'d3d10drv.dll', system/'d3d10drv.dll', True) CopyTo(thirdparty/'D3D10Drv.int', system/'D3D10Drv.int', True) @@ -254,6 +281,35 @@ def CopyD3D10Renderer(system:Path): CopyTo(f, drvdir_dest / f.name, True) +def CopyDXVK(system:Path, install:bool): + dir = GetSourcePath() / '3rdParty' / 'dxvk' + info('CopyDXVK from', dir, ' to ', system) + num = 0 + for f in dir.glob('*'): + dest = system / f.name + if install: + CopyTo(f, dest) + elif dest.exists(): + dest.unlink(True) + num += 1 + assert num > 0, 'Found '+str(num)+' DXVK files' + +def InstallOGL2(system:Path, install:bool): + Ogl = system/'OpenGLDrv.dll' + backupOgl = system/'OpenGLDrv.orig.dll' + if install: + if Ogl.exists() and not backupOgl.exists(): + Ogl.rename(backupOgl) + CopyTo(GetSourcePath() / '3rdParty' /'OpenGLDrv.dll', Ogl) + elif backupOgl.exists(): + currMd5 = '' + if Ogl.exists(): + currMd5 = MD5(Ogl.read_bytes()) + backupMd5 = MD5(backupOgl.read_bytes()) + info('reverting', Ogl, currMd5, 'to', backupOgl, backupMd5) + CopyTo(backupOgl, Ogl) + + def Mkdir(dir:Path, parents=False, exist_ok=False): if GetDryrun(): info("dryrun would've created folder", dir) diff --git a/installer/installer.py b/installer/installer.py index 71bd1c053..bdcb2c9ad 100644 --- a/installer/installer.py +++ b/installer/installer.py @@ -1,4 +1,4 @@ -from Install import SetDryrun, SetVerbose, info +from Install import GetSourcePath, SetDryrun, SetVerbose, info, debug try: import argparse import sys @@ -20,12 +20,7 @@ args = parser.parse_args() def GetVersion(): - return 'v0.3' - -if args.version: - info('DXRando Installer version:', GetVersion()) - info('Python version:', sys.version_info) - sys.exit(0) + return 'v0.4' if args.verbose: SetVerbose(True) @@ -33,6 +28,14 @@ def GetVersion(): if args.dryrun: SetDryrun(True) +if args.version: + thirdparty = GetSourcePath() / '3rdParty' + for f in thirdparty.rglob('*'): + debug(f) + info('DXRando Installer version:', GetVersion()) + info('Python version:', sys.version_info) + sys.exit(0) + if args.unattended: try: UnattendedInstall(args.path, args.downloadmirrors) diff --git a/installer/installer.spec b/installer/installer.spec index c8f808e96..1b1d31fa7 100644 --- a/installer/installer.spec +++ b/installer/installer.spec @@ -33,7 +33,8 @@ a_installer = Analysis( ('../LICENSE', '.'), ('Configs/*', 'Configs'), ('3rdParty/*.*', '3rdParty'), - ('3rdParty/d3d10drv/*', '3rdParty/d3d10drv') + ('3rdParty/d3d10drv/*', '3rdParty/d3d10drv'), + ('3rdParty/dxvk/*', '3rdParty/dxvk') ], hiddenimports=[], hookspath=[], diff --git a/installer/tests.py b/installer/tests.py index 74d64c1b6..6a1d28061 100644 --- a/installer/tests.py +++ b/installer/tests.py @@ -1,3 +1,4 @@ +from pathlib import Path import unittest from unittest import case from typeguard import typechecked, install_import_hook @@ -17,7 +18,7 @@ @typechecked class DXRTestCase(unittest.TestCase): def test_documents(self): - d = Install.GetDocumentsDir() + d = Install.GetDocumentsDir(Path.home()) self.assertTrue(d.exists(), str(d) + ' exists')