diff --git a/.github/release_template.md b/.github/release_template.md index dd6d32175..792eaaa6f 100644 --- a/.github/release_template.md +++ b/.github/release_template.md @@ -26,6 +26,7 @@ If you don't know what Deus Ex Randomizer is, then here's our old trailer which
Click to expand Minor Changes + *
diff --git a/.github/run-tests/action.yml b/.github/run-tests/action.yml index 90622d995..54f5f8fd3 100644 --- a/.github/run-tests/action.yml +++ b/.github/run-tests/action.yml @@ -141,7 +141,7 @@ runs: shell: bash - name: Build - run: pyinstaller installer/installer.spec + run: pyinstaller --clean installer/installer.spec shell: bash - run: du -ha dist shell: bash diff --git a/DXRBalance/DeusEx/Classes/BalanceHacking.uc b/DXRBalance/DeusEx/Classes/BalanceHacking.uc index 842b6d270..22d6991f3 100644 --- a/DXRBalance/DeusEx/Classes/BalanceHacking.uc +++ b/DXRBalance/DeusEx/Classes/BalanceHacking.uc @@ -8,12 +8,12 @@ var int energyMeterTimer; function Tick(float deltaTime) { - local DeusExPlayer p; + local Human p; if (bHacking) { - p = DeusExPlayer(winTerm.compOwner.Owner); - if( p == None ) p = Player;// ATMs don't set the Owner - if( p != None ) { + p = Human(winTerm.compOwner.Owner); + if( p == None ) p = Human(Player);// ATMs don't set the Owner + if( p != None && !p.bZeroRando ) { p.Energy -= deltaTime * 5.0; if( p.Energy <= 0 ) { p.Energy = 0; @@ -32,6 +32,8 @@ function Tick(float deltaTime) function CreateHackMessageWindow() { + local Human p; + p = Human(winTerm.compOwner.Owner); hackBackground = PersonaHeaderTextWindow(NewChild(Class'PersonaHeaderTextWindow')); hackBackground.SetPos(22, 19); @@ -52,6 +54,10 @@ function CreateHackMessageWindow() energyMeter.SetSize(168, 47); energyMeter.SetTextAlignments(HALIGN_Center, VALIGN_Center); + if(p == None || p.bZeroRando) { + energyMeter.Hide(); + } + UpdateEnergyMeter(); if(energyMeterTimer == -1) energyMeterTimer = AddTimer(0.1, true, 0, 'UpdateEnergyMeterTimer'); @@ -75,14 +81,14 @@ event DestroyWindow() function UpdateEnergyMeter() { - local DeusExPlayer p; + local Human p; local int energy,energydec,req,reqdec; local float reqEnergy; local string msg; - p = player; + p = Human(player); - if (p==None) return; + if (p==None || p.bZeroRando) return; //Keep it to one decimal point energy = int(p.Energy); diff --git a/DXRBalance/DeusEx/Classes/BalancePS40.uc b/DXRBalance/DeusEx/Classes/BalancePS40.uc index 3648f0e58..564588cc0 100644 --- a/DXRBalance/DeusEx/Classes/BalancePS40.uc +++ b/DXRBalance/DeusEx/Classes/BalancePS40.uc @@ -13,30 +13,28 @@ function Projectile ProjectileFire(class ProjClass, float ProjSpeed, class(ProjClass).default.mpDamage = HitDamage; proj = PlasmaBolt(Super.ProjectileFire(ProjClass, ProjSpeed, bWarn)); - if( proj != None ) { + if( proj != None && HitDamage > default.HitDamage ) { proj.LightBrightness = 250; proj.LightRadius = 20; } ProjClass.default.Damage = oldDamage; class(ProjClass).default.mpDamage = oldmpDamage; - return proj; + return proj; } -function PostBeginPlay() +function int UpgradeToPS40() { - Super.PostBeginPlay(); ItemName="PS40"; Description="The PS40 is a disposable, plasma-based weapon developed by an unknown security organization as a next generation stealth pistol." $ " Unfortunately, the necessity of maintaining a small physical profile restricts the weapon to a single shot." $ " Despite its limited functionality, the PS40 can be lethal."; beltDescription="PS40"; + HitDamage=100; + return HitDamage; } defaultproperties { - HitDamage=100 - ItemName="PS40" - Description="The PS40 is a disposable, plasma-based weapon developed by an unknown security organization as a next generation stealth pistol. Unfortunately, the necessity of maintaining a small physical profile restricts the weapon to a single shot. Despite its limited functionality, the PS40 can be lethal." - beltDescription="PS40" + HitDamage=15// in between the 8 damage bugged vanilla, and the 25 or 40 damage the code implies vanilla intended } diff --git a/DXRBalance/DeusEx/Classes/BalancePlayer.uc b/DXRBalance/DeusEx/Classes/BalancePlayer.uc index 1ce7abbe3..bd8c313cc 100644 --- a/DXRBalance/DeusEx/Classes/BalancePlayer.uc +++ b/DXRBalance/DeusEx/Classes/BalancePlayer.uc @@ -1,13 +1,13 @@ class BalancePlayer injects Human; +var travel bool bZeroRando, bReducedRando; + function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType) { - if(damageType == 'NanoVirus') { RandomizeAugStates(); } Super.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType); - } function RandomizeAugStates() diff --git a/DXRBalance/DeusEx/Classes/ChargedPickup.uc b/DXRBalance/DeusEx/Classes/ChargedPickup.uc index 464ef340e..593ca2e3d 100644 --- a/DXRBalance/DeusEx/Classes/ChargedPickup.uc +++ b/DXRBalance/DeusEx/Classes/ChargedPickup.uc @@ -36,3 +36,9 @@ function bool HandlePickupQuery( inventory Item ) return false; return Super.HandlePickupQuery(Item); } + +// default Charge was 2000, which is used for hazmats and rebreathers +defaultproperties +{ + Charge=1500 +} diff --git a/DXRBalance/DeusEx/Classes/DartFlare.uc b/DXRBalance/DeusEx/Classes/DartFlare.uc index 39d6cb5ab..58fdf3a90 100644 --- a/DXRBalance/DeusEx/Classes/DartFlare.uc +++ b/DXRBalance/DeusEx/Classes/DartFlare.uc @@ -1,6 +1,15 @@ class DXRDartFlare injects DartFlare; -defaultproperties +auto simulated state Flying { - DamageType=FlareFlamed + //It's goofy that this is in a function called Explode despite not exploding, but oh well + simulated function Explode(vector HitLocation, vector HitNormal) + { + if (Pawn(damagee)!=None){ + DamageType='FlareFlamed'; //Special damage type that only burns for a short period of time + } else { + DamageType='Burned'; //Default flare dart damage type + } + Super.Explode(HitLocation,HitNormal); + } } diff --git a/DXRBalance/DeusEx/Classes/RobotBalance.uc b/DXRBalance/DeusEx/Classes/RobotBalance.uc index ffee1cd02..38603d53a 100644 --- a/DXRBalance/DeusEx/Classes/RobotBalance.uc +++ b/DXRBalance/DeusEx/Classes/RobotBalance.uc @@ -7,6 +7,17 @@ function TakeDamageBase(int Damage, Pawn instigatedBy, Vector hitlocation, Vecto Super.TakeDamageBase(Damage, instigatedBy, hitLocation, momentum, damageType, bPlayAnim); } +//Copied from the various Human classes. Makes it more possible to filter out +//accidentally walking on one vs actively jumping on one +function bool WillTakeStompDamage(actor stomper) +{ + // This blows chunks! + if (stomper.IsA('PlayerPawn') && (GetPawnAllianceType(Pawn(stomper)) != ALLIANCE_Hostile)) + return false; + else + return true; +} + defaultproperties { BaseAccuracy=0.1 diff --git a/DXRBalance/DeusEx/Classes/TearGas.uc b/DXRBalance/DeusEx/Classes/TearGas.uc index a68dc44f6..7a5853673 100644 --- a/DXRBalance/DeusEx/Classes/TearGas.uc +++ b/DXRBalance/DeusEx/Classes/TearGas.uc @@ -2,5 +2,5 @@ class DXRTearGas injects TearGas; defaultproperties { - Damage=3// vanilla is 1 + Damage=2// vanilla is 1 } diff --git a/DXRBalance/DeusEx/Classes/ThrownProjectile.uc b/DXRBalance/DeusEx/Classes/ThrownProjectile.uc index 68498739c..fb8a8de69 100644 --- a/DXRBalance/DeusEx/Classes/ThrownProjectile.uc +++ b/DXRBalance/DeusEx/Classes/ThrownProjectile.uc @@ -18,24 +18,26 @@ simulated function Tick(float deltaTime) } else if(oldSkillTime == 0 && skillTime > 0 && player == None) { // faster than vanilla, unfortunately vanilla doesn't save the triggering actor but they already factored in the skill value - skillTime *= 0.6; - skillTime += 0.2; + skillTime = loge(skillTime); + skillTime += 0.85; + log("DXRThrownProjectile Tick " $ self @ skillTime); } } -// scale arming time with skill and fuse length for thrown grenades (fuseLength is used for thrown grenades, but also the SetTimer call for arming attached grenades) + simulated function PreBeginPlay() { + // based on the player's skill: scale arming time for player attached grenades, and fuse length for thrown grenades (fuseLength is used for thrown grenades, but also the SetTimer call for arming attached grenades) local #var(PlayerPawn) player; Super.PreBeginPlay(); player = #var(PlayerPawn)(Owner); if(player != None) { - // high skill gives the player shorter fuses + // high skill gives the player faster arming time for attached grenades, and fuse time for thrown grenades fuseLength += 3.0 * player.SkillSystem.GetSkillLevelValue(class'SkillDemolition'); fuseLength = FClamp(fuseLength, 0.2, 6); - } else { - // higher skill gives the enemies longer fuses + } else if(!bProximityTriggered) { + // higher skill gives the enemies longer fuses for thrown grenades player = #var(PlayerPawn)(GetPlayerPawn()); if(player != None) { fuseLength -= player.SkillSystem.GetSkillLevelValue(class'SkillDemolition') * 2.0 + 0.5; @@ -56,7 +58,7 @@ function SpawnTearGas() if ( Role < ROLE_Authority ) return; - for (i=0; i c, vector loc, rotator rot return d; } +//DO NOT ASSUME THIS LIST IS COMPLETE! +//THIS IS UPDATED AS WE ENCOUNTER ISSUES! +//IF SOMETHING ISN'T LISTED HERE AND ISN'T ROTATING CORRECTLY, GET IN THE EDITOR AND DOUBLE CHECK! +//YOU CAN ALSO CHECK THE LIST IN THE unreal-map-flipper REPOSITORY INSIDE MapLibs/actor.py AND LOOK AT THE classes_rot_offsets DICTIONARY! static function int GetRotationOffset(class c) { if(ClassIsChildOf(c, class'Pawn')) return 16384; - if(ClassIsChildOf(c, class'#var(prefix)SecurityCamera')) + if(ClassIsChildOf(c, class'#var(prefix)HackableDevices')) return 16384; if(ClassIsChildOf(c, class'#var(prefix)Vehicles')) return 16384; + if(ClassIsChildOf(c, class'#var(prefix)ThrownProjectile')) + return 16384; if(ClassIsChildOf(c, class'Brush')) { log("WARNING: GetRotationOffset for "$c$", Brushes/Movers have negative scaling so they don't need rotation adjustments!"); return -1; @@ -915,6 +921,29 @@ static function SetActorScale(Actor a, float scale) a.DrawScale = scale; } +function bool CheckFreeSpace(out vector loc, float radius, float height) +{ + local bool success; + + SetCollisionSize(radius, height); + SetCollision(true, true, true); + bCollideWhenPlacing = true; + bCollideWorld = true; + + success = SetLocation(loc); + + SetCollision(false, false, false); + bCollideWhenPlacing = false; + bCollideWorld = false; + + if(!success || VSize(Location - loc) > 128) { + l("CheckFreeSpace failed for " $ loc); + return false; + } + loc = Location; + return true; +} + function vector GetRandomPosition(optional vector target, optional float mindist, optional float maxdist, optional bool allowWater, optional bool allowPain) { local PathNode temp[4096]; diff --git a/DXRCore/DeusEx/Classes/DXRBase.uc b/DXRCore/DeusEx/Classes/DXRBase.uc index 56dfe3b90..f93f6dff9 100644 --- a/DXRCore/DeusEx/Classes/DXRBase.uc +++ b/DXRCore/DeusEx/Classes/DXRBase.uc @@ -107,6 +107,16 @@ simulated function int SetGlobalSeed(coerce string name) return dxr.SetSeed( dxr.seed + dxr.Crc(name) ); } +simulated function int BranchSeed(coerce string name) +{ + return dxr.SetSeed( dxr.Crc(dxr.seed $ name $ dxr.tseed) ); +} + +simulated function int ReapplySeed(int oldSeed) +{ + return dxr.SetSeed(oldSeed); +} + simulated function int rng(int max) { return dxr.rng(max); @@ -162,12 +172,12 @@ simulated function float rngrangeseeded(float val, float min, float max, coerce { local float mult, r, ret; local int oldseed; - oldseed = dxr.SetSeed( dxr.seed + dxr.Crc(classname) );//manually set the seed to avoid using the level name in the seed + oldseed = SetGlobalSeed(classname); mult = max - min; r = rngf(); ret = val * (r * mult + min); //l("rngrange r: "$r$", mult: "$mult$", min: "$min$", max: "$max$", val: "$val$", return: "$ret); - dxr.SetSeed(oldseed); + ReapplySeed(oldseed); return ret; } @@ -266,7 +276,7 @@ simulated function bool RandoLevelValues(Actor a, float min, float max, float we s = "(DXRando) " $ word $ ":|n " $ s; info("RandoLevelValues "$a$" = "$s); - dxr.SetSeed( oldseed ); + ReapplySeed( oldseed ); if(add_desc != "") { s = s $ "|n|n" $ add_desc; @@ -491,29 +501,39 @@ simulated function MessageBoxClicked(int button, int callbackId) { //Returns true when you aren't in a menu, or in the intro, etc. function bool InGame() { + local #var(PlayerPawn) p; + local DeusExRootWindow root; #ifdef hx return true; #endif - if( player() == None ) + p = player(); + + if( p == None ) return false; - if (player().InConversation()) { + if (p.InConversation()) { return True; } - if (None == DeusExRootWindow(player().rootWindow)) { + root = DeusExRootWindow(p.rootWindow); + + if (None == root) { return False; } - if (None == DeusExRootWindow(player().rootWindow).hud) { + if (root.GetTopWindow() != None) { return False; } - if (!DeusExRootWindow(player().rootWindow).hud.isVisible()){ + if (root.bUIPaused) { return False; } + if (root.hud.conWindow != None && root.hud.conWindow.IsVisible()) { + return false; + } + return True; } diff --git a/DXRCore/DeusEx/Classes/DXRMenuSetupRando.uc b/DXRCore/DeusEx/Classes/DXRMenuSetupRando.uc index 2a1dfeb49..fec828d11 100644 --- a/DXRCore/DeusEx/Classes/DXRMenuSetupRando.uc +++ b/DXRCore/DeusEx/Classes/DXRMenuSetupRando.uc @@ -246,6 +246,9 @@ function BindControls(optional string action) EnumOption("Unchanged Non-human Stats", 0, f.settings.bot_stats); EnumOption("Random Non-human Stats", 100, f.settings.bot_stats); + NewMenuItem("Paris Chill %", "Removes MJ12 from the Champs-Elysees."); + Slider(f.moresettings.remove_paris_mj12, 0, 100); + NewGroup("Skills"); @@ -307,6 +310,9 @@ function BindControls(optional string action) NewMenuItem("Swap Containers %", "The chance for container positions to be swapped."); Slider(f.settings.swapcontainers, 0, 100); + NewMenuItem("Swap Grenades %", "The chance for grenades on walls to have their type randomized."); + Slider(f.moresettings.grenadeswap, 0, 100); + BreakLine(); NewMenuItem("Min Weapon Damage %", "The minmum damage for weapons."); Slider(f.settings.min_weapon_dmg, 0, 500); diff --git a/DXRCore/DeusEx/Classes/DXRVersion.uc b/DXRCore/DeusEx/Classes/DXRVersion.uc index 4f3cc39ad..ab6f91e8b 100644 --- a/DXRCore/DeusEx/Classes/DXRVersion.uc +++ b/DXRCore/DeusEx/Classes/DXRVersion.uc @@ -4,8 +4,8 @@ simulated static function CurrentVersion(optional out int major, optional out in { major=2; minor=5; - patch=3; - build=11;//build can't be higher than 99 + patch=4; + build=4;//build can't be higher than 99 } simulated static function string VersionString(optional bool full) @@ -13,7 +13,7 @@ simulated static function string VersionString(optional bool full) local int major,minor,patch,build; local string status; - status = ""; + status = "Alpha"; if(status!="") { status = " " $ status; diff --git a/DXRCore/DeusEx/Classes/DXRando.uc b/DXRCore/DeusEx/Classes/DXRando.uc index 546369b6f..7fbf3d253 100644 --- a/DXRCore/DeusEx/Classes/DXRando.uc +++ b/DXRCore/DeusEx/Classes/DXRando.uc @@ -110,7 +110,7 @@ function CheckConfig() { local int i; - if( VersionOlderThan(config_version, 2,5,2,0) ) { + if( VersionOlderThan(config_version, 2,5,4,2) ) { for(i=0; i < ArrayCount(modules_to_load); i++) { modules_to_load[i] = ""; } @@ -165,14 +165,15 @@ function vanilla_modules() modules_to_load[i++] = "DXRNPCs"; modules_to_load[i++] = "DXRFashion"; modules_to_load[i++] = "DXRHints"; + modules_to_load[i++] = "DXRStartMap"; modules_to_load[i++] = "DXREvents"; //modules_to_load[i++] = "DXRMapInfo"; modules_to_load[i++] = "DXRMusic"; modules_to_load[i++] = "DXRMusicPlayer"; modules_to_load[i++] = "DXRPlayerStats"; modules_to_load[i++] = "DXRMapVariants"; - modules_to_load[i++] = "DXRStartMap"; modules_to_load[i++] = "DXRWeaponMods"; + modules_to_load[i++] = "DXRGrenades"; } function hx_modules() @@ -201,11 +202,12 @@ function hx_modules() modules_to_load[i++] = "DXRStats"; modules_to_load[i++] = "DXRHints"; modules_to_load[i++] = "DXRReplaceActors"; + modules_to_load[i++] = "DXRStartMap"; modules_to_load[i++] = "DXREvents"; modules_to_load[i++] = "DXRPlayerStats"; modules_to_load[i++] = "DXRMapVariants"; - modules_to_load[i++] = "DXRStartMap"; modules_to_load[i++] = "DXRWeaponMods"; + modules_to_load[i++] = "DXRGrenades"; } function gmdx_modules() @@ -236,13 +238,14 @@ function gmdx_modules() modules_to_load[i++] = "DXRReplaceActors"; modules_to_load[i++] = "DXRNPCs"; modules_to_load[i++] = "DXRFashion"; + modules_to_load[i++] = "DXRStartMap"; modules_to_load[i++] = "DXREvents"; modules_to_load[i++] = "DXRMusic"; modules_to_load[i++] = "DXRMusicPlayer"; modules_to_load[i++] = "DXRPlayerStats"; modules_to_load[i++] = "DXRMapVariants"; - modules_to_load[i++] = "DXRStartMap"; modules_to_load[i++] = "DXRWeaponMods"; + modules_to_load[i++] = "DXRGrenades"; } function revision_modules() @@ -277,13 +280,14 @@ function vmd_modules() modules_to_load[i++] = "DXRHints"; modules_to_load[i++] = "DXRReplaceActors"; modules_to_load[i++] = "DXRNPCs"; + modules_to_load[i++] = "DXRStartMap"; modules_to_load[i++] = "DXREvents"; modules_to_load[i++] = "DXRMusic"; modules_to_load[i++] = "DXRMusicPlayer"; modules_to_load[i++] = "DXRPlayerStats"; modules_to_load[i++] = "DXRMapVariants"; - modules_to_load[i++] = "DXRStartMap"; modules_to_load[i++] = "DXRWeaponMods"; + modules_to_load[i++] = "DXRGrenades"; } function DXRFlags LoadFlagsModule() diff --git a/DXRFixes/DeusEx/Classes/ScriptedPawn.uc b/DXRFixes/DeusEx/Classes/ScriptedPawn.uc index 5506180c4..68a728cc0 100644 --- a/DXRFixes/DeusEx/Classes/ScriptedPawn.uc +++ b/DXRFixes/DeusEx/Classes/ScriptedPawn.uc @@ -547,7 +547,69 @@ Begin: Destroy(); } +function bool IsProjectileDangerous(DeusExProjectile projectile) +{ + //If they're mostly immune, don't consider it a threat + if (ShieldDamage(projectile.damageType)<0.25){ + return False; + } + + //Same deal here + if (ModifyDamage(100,self,vect(0,0,0),vect(0,0,0),projectile.damageType)<=25){ + return False; + } + + return Super.IsProjectileDangerous(projectile); +} + +function SupportActor(Actor standingActor) +{ + local float zVelocity; + local float baseMass; + local float standingMass; + local vector damagePoint; + local float damage; + local vector newVelocity; + local float angle; + + //Friendly stomp logic + if (WillTakeStompDamage(standingActor)==false && PlayerPawn(standingActor)!=None){ + if (!((Physics == PHYS_Swimming) && Region.Zone.bWaterZone)){ + standingMass = FMax(1, standingActor.Mass); + baseMass = FMax(1, Mass); + zVelocity = standingActor.Velocity.Z; + damagePoint = Location + vect(0,0,1)*(CollisionHeight-1); + damage = (1 - (standingMass/baseMass) * (zVelocity/100)); + + //Bouncing around on top of someone's head happens at around -55 zVelocity. Giving some slop, + // -75 seems like a reasonable cut off point for determining if it was an intentional stomp. + //Human mass (which determines the standingMass) is 150. 10000 is a nice number though, so we'll + //go with -66 being the cutoff point instead. + if ((zVelocity*standingMass <= -10000)){ + TakeDamage(damage, standingActor.Instigator, damagePoint, 0.2*standingActor.Velocity, 'stomped'); + } + } + } + + //ScriptedPawns can't stomp other ScriptedPawns + if (ScriptedPawn(standingActor)==None){ + Super.SupportActor(standingActor); + } else { + //Still bounce them off + angle = FRand()*Pi*2; + newVelocity.X = cos(angle); + newVelocity.Y = sin(angle); + newVelocity.Z = 0; + newVelocity *= FRand()*25 + 25; + newVelocity += standingActor.Velocity; + newVelocity.Z = 50; + standingActor.Velocity = newVelocity; + standingActor.SetPhysics(PHYS_Falling); + } +} + defaultproperties { EmpHealth=50 + FearSustainTime=15 } diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM01.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM01.uc index 0901967b2..185bc3e5b 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM01.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM01.uc @@ -111,3 +111,46 @@ function PreFirstEntryMapFixes() break; } } + +function AnyEntryMapFixes() +{ + local Conversation c; + local ConEvent ce,before,after; + local ConEventSpeech ces; + local name conName; + local string afterTextLine; + + DeleteConversationFlag(GetConversation('GuntherRescued'), 'GuntherFreed', true); + + //Cut out the dialog for Paul giving you equipment + c = GetConversation('MeetPaul'); + ce = c.eventList; + + //Should this actually be seeded? + if (rand(2)==0){ + afterTextLine="I get the idea. What's the first move?"; + } else { + afterTextLine="Great. What's the first move?"; + } + + while (ce!=None){ + if (ce.eventType==ET_Speech){ + ces = ConEventSpeech(ce); + if (InStr(ces.conSpeech.speech,"NSF took one of our agents hostage")!=-1){ + before = ce; + } + if (InStr(ces.conSpeech.speech,afterTextLine)!=-1){ + after = ce; + } + if (before!=None && after!=None){ + break; + } + } + ce = ce.nextEvent; + } + + //Just in case something went wrong + if (before!=None && after!=None){ + before.nextEvent = after; + } +} diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM02.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM02.uc index 7cd872900..296e44b6f 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM02.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM02.uc @@ -11,6 +11,7 @@ function PreFirstEntryMapFixes() local Terrorist nsf; local #var(prefix)BoxSmall bs; local #var(prefix)Keypad2 kp; + local #var(prefix)TAD tad; #ifdef injections local #var(prefix)Newspaper np; local class<#var(prefix)Newspaper> npClass; @@ -75,6 +76,19 @@ function PreFirstEntryMapFixes() break; case "02_NYC_HOTEL": Spawn(class'#var(prefix)Binoculars',,, vectm(-610.374573,-3221.998779,94.160065)); //Paul's bedside table + + //Restore answering machine message, but in mission 2 (conversation is in mission 3); + //tad=Spawn(class'#var(prefix)TAD',,, vectm(-290,-2380,115),rotm(0,0,0)); + //tad.BindName="AnsweringMachine"; + //CreateAnsweringMachineConversation(tad); + //tad.ConBindEvents(); + + + Spawn(class'PlaceholderItem',,, vectm(-732,-2628,75)); //Actual closet + Spawn(class'PlaceholderItem',,, vectm(-732,-2712,75)); //Actual closet + Spawn(class'PlaceholderItem',,, vectm(-129,-3038,127)); //Bathroom counter + Spawn(class'PlaceholderItem',,, vectm(15,-2972,123)); //Kitchen counter + Spawn(class'PlaceholderItem',,, vectm(-853,-3148,75)); //Crack next to Paul's bed break; #endif @@ -89,7 +103,60 @@ function PreFirstEntryMapFixes() d.bFrobbable = true; } break; + case "02_NYC_BAR": + Spawn(class'BarDancer',,,vectm(-1475,-580,48),rotm(0,25000,0)); + break; + } +} + +function CreateAnsweringMachineConversation(Actor tad) +{ + local Conversation con; + local ConEvent ce; + local ConEventSpeech ces; + local ConItem conItem; + local ConversationList list; + + con = new(level) class'Conversation'; + con.conName='AnsweringMachineMessage'; + con.CreatedBy="AnsweringMachineMessage"; + con.conOwnerName="AnsweringMachine"; + con.bGenerateAudioNames=false; + con.bInvokeFrob=true; + con.bFirstPerson=true; + con.bNonInteractive=true; + con.audioPackageName="Mission03"; + + ces = new(con) class'ConEventSpeech'; + con.eventList = ces; + ces.eventType=ET_Speech; + ces.conversation=con; + ces.speaker=tad; + ces.speakerName=""; + ces.speakingTo=tad; + ces.speechFont=SF_Normal; + ces.conSpeech = new(con) class'ConSpeech'; + ces.conSpeech.speech="Paul, I know you said no phone messages, but South Street's going up in smoke. We'll have to meet at the subway station."; + ces.conSpeech.soundID=69; //Yes, really + + player().ClientMessage("Speech audio: "$con.GetSpeechAudio(69)); + + ce = new(con) class'ConEventEnd'; + ce.eventType=ET_End; + ces.nextEvent=ce; + ce.conversation=con; + + conItem = new(Level) class'ConItem'; + conItem.conObject = con; + + foreach AllObjects(class'ConversationList', list) { + if( list.conversations != None ) { + conItem.next = list.conversations; + list.conversations = conItem; + break; + } } + } function PostFirstEntryMapFixes() diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM03.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM03.uc index 5b0dc122c..58d8348b4 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM03.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM03.uc @@ -1,5 +1,16 @@ class DXRFixupM03 extends DXRFixup; +function CheckConfig() +{ + local int i; + + add_datacubes[i].map = "03_NYC_UNATCOHQ"; + add_datacubes[i].text = "Note to self:|nUsername: JCD|nPassword: bionicman "; + i++; + + Super.CheckConfig(); +} + function PostFirstEntryMapFixes() { local Actor a; @@ -180,6 +191,17 @@ function PreFirstEntryMapFixes() break; } } + + Spawn(class'PlaceholderItem',,, vectm(-73,-497.98,42.3)); //Water supply + Spawn(class'PlaceholderItem',,, vectm(-486,206,26)); //Under ramps + Spawn(class'PlaceholderItem',,, vectm(461,206,26)); //Under Ramp 2 + Spawn(class'PlaceholderItem',,, vectm(395,830,74)); //Around Pillars + Spawn(class'PlaceholderItem',,, vectm(-2,633,74));//More pillars + Spawn(class'PlaceholderItem',,, vectm(-465,562,74));//Even more pillars + Spawn(class'PlaceholderItem',,, vectm(-659,990,107)); //Pillar stairs + Spawn(class'PlaceholderItem',,, vectm(661,1000,107)); //other side pillar stairs + Spawn(class'PlaceholderItem',,, vectm(-919,-94,11)); //Other side ramp + Spawn(class'PlaceholderItem',,, vectm(1222,88,11)); //Near start, but bad side break; #endif @@ -217,6 +239,7 @@ function PreFirstEntryMapFixes() break; case "03_NYC_UNATCOHQ": FixUNATCOCarterCloset(); + FixAlexsEmail(); //Move weapon mod out of Manderley's secret (inaccessible) safe foreach AllActors(class'#var(prefix)WeaponModRecoil',wmr){ @@ -247,10 +270,41 @@ function PreFirstEntryMapFixes() function AnyEntryMapFixes() { + local #var(prefix)Phone phone; + local Conversation c; + local ConEvent ce; + local ConEventSpeech ces; + switch(dxr.localURL) { case "03_NYC_747": SetTimer(1, true); break; + + case "03_NYC_AIRFIELDHELIBASE": + //Restore this cut phone conversation + c = GetConversation('OverhearLebedev'); + c.conOwnerName="LebedevPhone"; + c.bInvokeRadius=True; + c.radiusDistance=200; + foreach AllActors(class'#var(prefix)Phone',phone){ + if (phone.name=='Phone1'){ + phone.BindName="LebedevPhone"; + break; + } + } + + ce = c.eventList; + while (ce!=None){ + if (ce.eventType==ET_Speech){ + ces = ConEventSpeech(ce); + ces.speaker=phone; + ces.speakingTo=phone; + } + ce = ce.nextEvent; + } + + phone.ConBindEvents(); + break; } } diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM04.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM04.uc index 0bcad6098..16bce6c40 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM04.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM04.uc @@ -2,6 +2,17 @@ class DXRFixupM04 extends DXRFixup; var int old_pawns;// used for NYC_04_CheckPaulRaid() +function CheckConfig() +{ + local int i; + + add_datacubes[i].map = "04_NYC_UNATCOHQ"; + add_datacubes[i].text = "Note to self:|nUsername: JCD|nPassword: bionicman "; + i++; + + Super.CheckConfig(); +} + function PreTravelMapFixes() { switch(dxr.localURL) { @@ -27,6 +38,7 @@ function PreFirstEntryMapFixes() local #var(prefix)Karkian k; local AllianceTrigger at; local BlockPlayer bp; + local #var(DeusExPrefix)Mover door; switch (dxr.localURL) { @@ -53,6 +65,13 @@ function PreFirstEntryMapFixes() } Spawn(class'#var(prefix)Binoculars',,, vectm(-610.374573,-3221.998779,94.160065)); //Paul's bedside table + + SpawnDatacubeTextTag(vectm(-840,-2920,85), rotm(0,0,0), '02_Datacube07',False); //Paul's stash code, in closet + Spawn(class'PlaceholderItem',,, vectm(-732,-2628,75)); //Actual closet + Spawn(class'PlaceholderItem',,, vectm(-732,-2712,75)); //Actual closet + Spawn(class'PlaceholderItem',,, vectm(-129,-3038,127)); //Bathroom counter + Spawn(class'PlaceholderItem',,, vectm(15,-2972,123)); //Kitchen counter + Spawn(class'PlaceholderItem',,, vectm(-853,-3148,75)); //Crack next to Paul's bed break; #endif @@ -80,6 +99,11 @@ function PreFirstEntryMapFixes() foreach AllActors(class'#var(prefix)HackableDevices', hd) { hd.hackStrength /= 3.0; } + + foreach AllActors(class'#var(DeusExPrefix)Mover',door,'ExitDoor'){ + door.KeyIDNeeded='BasementDoor'; + } + if(!dxr.flags.IsReducedRando()) { k = Spawn(class'#var(prefix)Karkian',,, vectm(54.688416, 1208.957275, -237.351410), rotm(0,32768,0)); k.BindName="NSFMinotaur"; @@ -105,6 +129,8 @@ function PreFirstEntryMapFixes() break; case "04_NYC_UNATCOHQ": FixUNATCOCarterCloset(); + FixAlexsEmail(); + //Spawn some placeholders for new item locations Spawn(class'PlaceholderItem',,, vectm(363.284149, 344.847, 50.32)); //Womens bathroom counter Spawn(class'PlaceholderItem',,, vectm(211.227, 348.46, 50.32)); //Mens bathroom counter @@ -133,6 +159,9 @@ function PreFirstEntryMapFixes() bp.bBlockPlayers=false; } break; + case "04_NYC_BAR": + Spawn(class'BarDancer',,,vectm(-1440,340,48),rotm(0,-16348,0)); + break; } } diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM05.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM05.uc index 58ae9803a..27856d566 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM05.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM05.uc @@ -8,6 +8,10 @@ function CheckConfig() add_datacubes[i].text = "Agent Sherman, I've updated the demiurge password for Agent Navarre's killphrase to archon. Make sure you don't leave this datacube lying around."; i++; + add_datacubes[i].map = "05_NYC_UNATCOHQ"; + add_datacubes[i].text = "Note to self:|nUsername: JCD|nPassword: bionicman "; + i++; + Super.CheckConfig(); } @@ -29,6 +33,7 @@ function PreFirstEntryMapFixes() local #var(prefix)JaimeReyes j; local #var(prefix)MJ12Troop mj12; local #var(prefix)RatGenerator rg; + local #var(prefix)NanoKey k; local DXREnemies dxre; local int i; @@ -78,6 +83,8 @@ function PreFirstEntryMapFixes() #ifdef vanillamaps case "05_NYC_UNATCOHQ": + FixAlexsEmail(); + // Anna's dialog depends on this flag dxr.flagbase.SetBool('DL_Choice_Played', true,, 6); @@ -98,6 +105,12 @@ function PreFirstEntryMapFixes() RemoveFears(j); } + k = Spawn(class'#var(prefix)NanoKey',,, vectm(420,195,333)); + k.KeyID = 'UNOfficeDoorKey'; + k.Description = "UNATCO Office Door Key"; + if(dxr.flags.settings.keysrando > 0) + GlowUp(k); + //Spawn some placeholders for new item locations Spawn(class'PlaceholderItem',,, vectm(363.284149, 344.847, 50.32)); //Womens bathroom counter Spawn(class'PlaceholderItem',,, vectm(211.227, 348.46, 50.32)); //Mens bathroom counter diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM06.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM06.uc index 688ebb8ef..96542c90f 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM06.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM06.uc @@ -30,8 +30,7 @@ function PreFirstEntryMapFixes() local Button1 b; local ElevatorMover e; local #var(DeusExPrefix)Mover m; - local FlagTrigger ft; - local AllianceTrigger at; + local #var(prefix)AllianceTrigger at; local DeusExMover d; local DataLinkTrigger dt; local ComputerSecurity cs; @@ -40,6 +39,11 @@ function PreFirstEntryMapFixes() local #var(prefix)WeaponNanoSword dts; local #var(prefix)RatGenerator rg; local #var(prefix)Credits creds; + local #var(prefix)Greasel g; + local #var(prefix)FlagTrigger ft; + local #var(prefix)OrdersTrigger ot; + local #var(prefix)TriadRedArrow bouncer; + #ifdef injections local #var(prefix)DataCube dc; #else @@ -185,7 +189,7 @@ function PreFirstEntryMapFixes() foreach AllActors(class'#var(DeusExPrefix)Mover', m, 'elevator_door') { m.bIsDoor = true;// DXRDoors will pick this up later since we're in PreFirstEntry } - foreach AllActors(class'FlagTrigger', ft, 'MJ12Alert') { + foreach AllActors(class'#var(prefix)FlagTrigger', ft, 'MJ12Alert') { ft.Tag = 'TongHasRom'; } foreach AllActors(class'DataLinkTrigger', dt) { @@ -201,6 +205,13 @@ function PreFirstEntryMapFixes() pad.bHidden = False; } + foreach AllActors(class'#var(prefix)Greasel',g){ + g.bImportant = True; + g.BindName="JerryTheVentGreasel"; + g.FamiliarName = "Jerry the Vent Greasel"; + g.UnfamiliarName = "Jerry the Vent Greasel"; + } + SpawnDatacubeImage(vectm(-1194.700195,-789.460266,-750.628357), rotm(0,0,0),Class'DeusEx.Image15_GrayDisection'); Spawn(class'PlaceholderItem',,, vectm(-1.95,1223.1,810.3)); //Table over entrance @@ -235,7 +246,7 @@ function PreFirstEntryMapFixes() break; case "06_HONGKONG_WANCHAI_UNDERWORLD": #ifdef injections - foreach AllActors(class'AllianceTrigger',at,'StoreSafe') { + foreach AllActors(class'#var(prefix)AllianceTrigger',at,'StoreSafe') { at.bPlayerOnly = true; } #endif @@ -259,6 +270,62 @@ function PreFirstEntryMapFixes() //A switch inside the freezer to open it back up... just in case AddSwitch( vect(-1560.144409,-3166.475098,-315.504028), rot(0,16408,0), 'FreezerDoor'); + + + //Restore bouncer conversation.... + + //Remove the flag trigger that says you paid as soon as you walk in the door... + //The doorgirl conversation sets the flag appropriately + foreach AllActors(class'#var(prefix)FlagTrigger',ft){ + if (ft.bSetFlag==True && ft.FlagName=='PaidForLuckyMoney'){ + ft.Destroy(); + break; + } + } + + ft=Spawn(class'#var(prefix)FlagTrigger',,,vectm(-1024,-1019,-343)); + ft.bSetFlag=False; + ft.bTrigger=True; + ft.FlagName='PaidForLuckyMoney'; + ft.flagValue=False; + ft.Event='BouncerGonnaGetcha'; + + ft=Spawn(class'#var(prefix)FlagTrigger'); + ft.SetCollision(False,False,False); + ft.Tag='BouncerGonnaGetcha'; + ft.bSetFlag=True; + ft.bTrigger=False; + ft.FlagName='BouncerComing'; + ft.flagValue=True; + + ot=Spawn(class'#var(prefix)OrdersTrigger'); + ot.Tag='BouncerGonnaGetcha'; + ot.Event='ClubBouncer'; //Need to make sure one of these guys is actually labeled as such... + ot.SetCollision(False,False,False); + ot.Orders='RunningTo'; + + ot=Spawn(class'#var(prefix)OrdersTrigger'); + ot.Tag='BouncerStartAttacking'; + ot.Event='ClubBouncer'; //Need to make sure one of these guys is actually labeled as such... + ot.SetCollision(False,False,False); + ot.Orders='Attacking'; + + at = Spawn(class'#var(prefix)AllianceTrigger'); + at.Tag='BouncerStartAttacking'; + at.Event='ClubBouncer'; + at.SetCollision(False,False,False); + at.Alliance='RedArrow'; + at.Alliances[0].AllianceLevel=-1; + at.Alliances[0].AllianceName='Player'; + at.Alliances[0].bPermanent=False; + + foreach AllActors(class'#var(prefix)TriadRedArrow',bouncer){ + if (bouncer.BindName=="ClubBouncer" && bouncer.Location.Z > -150){ + bouncer.Tag = 'ClubBouncer'; + break; + } + } + break; case "06_HONGKONG_WANCHAI_GARAGE": @@ -268,7 +335,7 @@ function PreFirstEntryMapFixes() break; case "06_HONGKONG_VERSALIFE": - ft= Spawn(class'FlagTrigger',,, vectm(128.850372,635.855957,-123)); //In front of lower elevator + ft= Spawn(class'#var(prefix)FlagTrigger',,, vectm(128.850372,635.855957,-123)); //In front of lower elevator ft.Event='VL_OnAlert'; ft.FlagName='Have_ROM'; ft.bSetFlag=False; @@ -363,6 +430,8 @@ function AnyEntryMapFixes() local Conversation c; local ConEvent ce; local ConEventSpeech ces; + local ConEventSetFlag cesf; + local ConEventTrigger cet; // if flag Have_ROM, set flags Have_Evidence and KnowsAboutNanoSword? // or if flag Have_ROM, Gordon Quick should let you into the compound? requires Have_Evidence and MaxChenConvinced @@ -496,6 +565,41 @@ function AnyEntryMapFixes() } ce = ce.nextEvent; } + break; + case "06_HONGKONG_WANCHAI_UNDERWORLD": + c = GetConversation('BouncerPissed'); + c.bInvokeRadius=True; + c.radiusDistance=180; + + ce = c.eventList; + while (ce!=None){ + if (ce.eventType==ET_Speech){ + ces = ConEventSpeech(ce); + if (InStr(ces.conSpeech.speech,"Don't try that again")!=-1){ + //Spawn a ConEventSetFlag to set "PaidForLuckyMoney", insert it between this and it's next event + cesf = new(c) class'ConEventSetFlag'; + cesf.eventType=ET_SetFlag; + cesf.label="PaidForLuckyMoneyTrue"; + cesf.flagRef = new(c) class'ConFlagRef'; + cesf.flagRef.flagName='PaidForLuckyMoney'; + cesf.flagRef.value=True; + cesf.flagRef.expiration=7; + cesf.nextEvent = ces.nextEvent; + ces.nextEvent = cesf; + + } + if (InStr(ces.conSpeech.speech,"Your choice.")!=-1){ + //Spawn a ConEventTrigger to hit an alliance trigger or something so he starts attacking, insert between this and next event + cet = new(c) class'ConEventTrigger'; + cet.eventType=ET_Trigger; + cet.triggerTag = 'BouncerStartAttacking'; + cet.nextEvent = ces.nextEvent; + ces.nextEvent = cet; + } + } + ce = ce.nextEvent; + } + break; default: break; @@ -509,7 +613,7 @@ function HandleJohnSmithDeath() } if (!dxr.flagbase.GetBool('Supervisor01_Dead') && - dxr.flagbase.GetBool('HaveROM') && + dxr.flagbase.GetBool('Have_ROM') && !dxr.flagbase.GetBool('Disgruntled_Guy_Return_Played')) { dxr.flagbase.SetBool('Disgruntled_Guy_Dead',true); diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM08.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM08.uc index b5e8fb697..45d461203 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM08.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM08.uc @@ -70,9 +70,17 @@ function PreFirstEntryMapFixes() break; case "08_NYC_HOTEL": Spawn(class'#var(prefix)Binoculars',,, vectm(-610.374573,-3221.998779,94.160065)); //Paul's bedside table + SpawnDatacubeTextTag(vectm(-840,-2920,85), rotm(0,0,0), '02_Datacube07',False); //Paul's stash code, in closet + Spawn(class'PlaceholderItem',,, vectm(-732,-2628,75)); //Actual closet + Spawn(class'PlaceholderItem',,, vectm(-732,-2712,75)); //Actual closet + Spawn(class'PlaceholderItem',,, vectm(-129,-3038,127)); //Bathroom counter + Spawn(class'PlaceholderItem',,, vectm(15,-2972,123)); //Kitchen counter + Spawn(class'PlaceholderItem',,, vectm(-853,-3148,75)); //Crack next to Paul's bed break; case "08_NYC_BAR": npClass.static.SpawnInfoDevice(self,class'#var(prefix)NewspaperOpen',vectm(-1171.976440,250.575806,53.729687),rotm(0,0,0),'08_Newspaper01'); //Joe Greene article, table near where Harley is in Vanilla + Spawn(class'BarDancer',,,vectm(-2150,-500,48),rotm(0,0,0)); + break; } } diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM09.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM09.uc index 54ca08af3..880ee5beb 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM09.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM09.uc @@ -9,8 +9,7 @@ function CheckConfig() add_datacubes[i].map = "09_NYC_Dockyard"; add_datacubes[i].text = "Jenny I've got your number|nI need to make you mine|nJenny don't change your number|n 8675309";// DXRPasswords doesn't recognize |n as a wordstop i++; - add_datacubes[i].map = "09_NYC_Dockyard"; - add_datacubes[i].text = "Jenny I've got your number|nI need to make you mine|nJenny don't change your number|n 8675309";// DXRPasswords doesn't recognize |n as a wordstop + add_datacubes[i] = add_datacubes[i-1];// dupe i++; Super.CheckConfig(); @@ -30,10 +29,13 @@ function PreFirstEntryMapFixes() local #var(prefix)Containers c; local Rotator rot; local #var(prefix)LAM lam; + local #var(prefix)GasGrenade gasgren; local Switch1 s; local #var(prefix)Barrel1 barrel; local #var(prefix)RatGenerator rg; local #var(prefix)FlagTrigger ft; + local #var(prefix)NanoKey key; + local #var(prefix)BeamTrigger beam; switch(dxr.localURL) { @@ -140,6 +142,34 @@ function PreFirstEntryMapFixes() break; } +#ifdef vanillamaps + foreach AllActors(class'#var(prefix)GasGrenade',gasgren) { + //This one has falling physics normally, so just fix it + //It can fall out of position before the physics get fixed, so put it back + if (gasgren.name=='GasGrenade0'){ + gasgren.SetPhysics(PHYS_None); + gasgren.bCollideWorld = false; + gasgren.SetRotation(rotm(0,-16472,0,GetRotationOffset(gasgren.Class))); + gasgren.SetLocation(vectm(1602.174,2470.3386,-431.6885)); + gasgren.bCollideWorld = true; + break; + } + } + + foreach AllActors(class'#var(prefix)BeamTrigger',beam){ + if (beam.Event=='BotDrop'){ + beam.Tag='TunnelTrigger'; + } + } +#endif + + //They put the key ID in the tag for some reason + foreach AllActors(class'#var(prefix)NanoKey',key,'SupplyRoom'){ + if (key.keyID==''){ + key.keyID='SupplyRoom'; + } + } + //Button to open the sewer grate from the ship side AddSwitch( vect(1883.546753,6404.096191,-232.870697), rot(0, 0, 0), 'DrainGrate'); diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupM15.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupM15.uc index 9f8f497b2..e1cdf3bc2 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupM15.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupM15.uc @@ -140,6 +140,7 @@ function PreFirstEntryMapFixes_Final() { local DeusExMover d; local Switch1 s; + local Switch2 s2; local SpecialEvent se; local DataLinkTrigger dlt; @@ -159,12 +160,26 @@ function PreFirstEntryMapFixes_Final() //Fix the Tong Ending skip for real for real foreach AllActors(class'Switch1',s){ - if (s.Event=='destroy_generator'){ + if (s.Event=='destroy_generator'){ s.Tag='destroy_generator_switch'; class'DXRTriggerEnable'.static.Create(s,'Generator_overload','destroy_generator_switch'); } } + //but like... for REAL + foreach AllActors(class'Switch2',s2){ + if (s2.Event=='button_1'){ + s2.Tag='button_1_switch'; + class'DXRTriggerEnable'.static.Create(s2,'Generator_panel','button_1_switch'); + } else if (s2.Event=='button_2'){ + s2.Tag='button_2_switch'; + class'DXRTriggerEnable'.static.Create(s2,'injector2','button_2_switch'); + } else if (s2.Event=='button_3'){ + s2.Tag='button_3_switch'; + class'DXRTriggerEnable'.static.Create(s2,'injector3','button_3_switch'); + } + } + //Generator Failsafe buttons should spit out some sort of message if the coolant isn't cut //start_buzz1 and start_buzz2 are the tags that get hit when the coolant isn't cut diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupParis.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupParis.uc index 459366129..e01245fab 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupParis.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupParis.uc @@ -208,3 +208,12 @@ function AnyEntryMapFixes() break; } } + +function PostFirstEntryMapFixes() +{ + switch(dxr.localURL) { + case "11_PARIS_CATHEDRAL": + AddBox(class'#var(prefix)CrateUnbreakableSmall', vectm(-3570.950684, 2238.034668, -783.901367));// right at the start + break; + } +} diff --git a/DXRMapFixups/DeusEx/Classes/DXRFixupVandenberg.uc b/DXRMapFixups/DeusEx/Classes/DXRFixupVandenberg.uc index 1cafdd4b6..6865cbae2 100644 --- a/DXRMapFixups/DeusEx/Classes/DXRFixupVandenberg.uc +++ b/DXRMapFixups/DeusEx/Classes/DXRFixupVandenberg.uc @@ -20,6 +20,7 @@ function PreFirstEntryMapFixes() local #var(prefix)RatGenerator rg; local #var(prefix)OrdersTrigger ot; local #var(prefix)NanoKey key; + local #var(prefix)DamageTrigger dt; local Actor a; switch(dxr.localURL) @@ -50,6 +51,12 @@ function PreFirstEntryMapFixes() foreach RadiusActors(class'Actor', a, 250, vectm(-4350,3000,-5850)) { a.Destroy(); } + + foreach AllActors(class'#var(prefix)DamageTrigger',dt){ + if (dt.DamageType=='Shocked'){ + dt.Tag='lab_water'; + } + } #ifdef vanillamaps //Add a key to Tim's closet foreach AllActors(class'#var(DeusExPrefix)Mover',door){ @@ -95,6 +102,20 @@ function PreFirstEntryMapFixes() } rg=Spawn(class'#var(prefix)RatGenerator',,, vectm(737,4193,-426));//In shoreside shed rg.MaxCount=1; + + Spawn(class'PlaceholderItem',,, vectm(755,4183,-421)); //Shed + Spawn(class'PlaceholderItem',,, vectm(755,4101,-421)); //Shed + Spawn(class'PlaceholderItem',,, vectm(462,4042,-421)); //Shed + Spawn(class'PlaceholderItem',,, vectm(462,3986,-421)); //Shed + Spawn(class'PlaceholderItem',,, vectm(462,3939,-421)); //Shed + + foreach AllActors(class'#var(DeusExPrefix)Mover',door){ + if(door.KeyIDNeeded=='shed'){ + door.Tag='ShedDoor'; + } + } + AddSwitch( vect(654.545,3889.5397,-367.262), rot(0, 16384, 0), 'ShedDoor'); + break; case "14_OCEANLAB_LAB": @@ -207,6 +228,12 @@ function PreFirstEntryMapFixes() rg=Spawn(class'#var(prefix)RatGenerator',,, vectm(-2375,-644,-993));//Under trailer near Jock rg.MaxCount=1; + Spawn(class'PlaceholderItem',,, vectm(-366,-2276,-1553)); //Under collapsed bridge + Spawn(class'PlaceholderItem',,, vectm(-394,-1645,-1565)); //Near bridge pillar + Spawn(class'PlaceholderItem',,, vectm(-88,-2087,-1553)); //Collapsed bridge road surface + Spawn(class'PlaceholderItem',,, vectm(909,-2474,-1551)); //Wrecked car + Spawn(class'PlaceholderItem',,, vectm(-3152,-2780,-1364)); //Ledge near original key + break; #endif } diff --git a/DXRMissions/DeusEx/Classes/DXRMissionsM01.uc b/DXRMissions/DeusEx/Classes/DXRMissionsM01.uc index 562643a42..3512c1cec 100644 --- a/DXRMissions/DeusEx/Classes/DXRMissionsM01.uc +++ b/DXRMissions/DeusEx/Classes/DXRMissionsM01.uc @@ -71,8 +71,6 @@ function PreFirstEntryMapFixes() local #var(prefix)OrdersTrigger ot; if( dxr.localURL == "01_NYC_UNATCOISLAND" ) { - dxr.flags.f.SetBool('MeetPaul_Played', true,, 2); - dxr.flags.f.SetBool('FemJCMeetPaul_Played', true,, 2); dxr.flags.f.SetBool('PaulGaveWeapon', true,, 2); #ifdef revision dxr.flags.f.SetBool('PaulGiveWeapon_Played', true,, 2); @@ -109,8 +107,6 @@ function MissionTimer() function AddMissionGoals() { local DeusExGoal newGoal; - local Inventory item; - if(dxr.localURL != "01_NYC_UNATCOISLAND") return; //The MeetPaul conversation would normally give you several goals. @@ -123,12 +119,6 @@ function AddMissionGoals() newGoal=player().AddGoal('MeetFilben',False); newGoal.SetText("Meet UNATCO informant Harley Filben at the North Docks. He has a key to the Statue doors."); - - if (player().FindInventoryType(class'Image01_LibertyIsland')==None){ - item = Spawn(class'Image01_LibertyIsland'); - item.Frob(player(), None); - } - } function AfterMoveGoalToLocation(Goal g, GoalLocation Loc) diff --git a/DXRMissions/DeusEx/Classes/DXRMissionsM02.uc b/DXRMissions/DeusEx/Classes/DXRMissionsM02.uc index ee4d8bfe9..43dc75d82 100644 --- a/DXRMissions/DeusEx/Classes/DXRMissionsM02.uc +++ b/DXRMissions/DeusEx/Classes/DXRMissionsM02.uc @@ -39,10 +39,12 @@ function int InitGoals(int mission, string map) goal = AddGoal("02_NYC_WAREHOUSE", "Generator", GOAL_TYPE1, 'BreakableWall2', PHYS_MovingBrush); AddGoalActor(goal, 1, 'CrateExplosiveSmall0', PHYS_None); AddGoalActor(goal, 2, 'CrateExplosiveSmall6', PHYS_None); + AddGoalActor(goal, 3, 'AmbientSoundTriggered0', PHYS_None); + AddGoalActor(goal, 4, 'AmbientSoundTriggered1', PHYS_None); AddGoalLocation("02_NYC_WAREHOUSE", "Warehouse", GOAL_TYPE1 | VANILLA_GOAL, vect(576.000000, -512.000000, 71.999939), rot(32768, -16384, 0)); AddGoalLocation("02_NYC_WAREHOUSE", "Alley", GOAL_TYPE1, vect(-640.000000, 1760.000000, 128.000000), rot(0,32768,-16384)); AddGoalLocation("02_NYC_WAREHOUSE", "Apartment", GOAL_TYPE1, vect(368.000000, 1248.000000, 992.000000), rot(0,32768,-16384)); - AddGoalLocation("02_NYC_WAREHOUSE", "Basement", GOAL_TYPE1, vect(300, -560, -120), rot(0,-16384,-16384)); + AddGoalLocation("02_NYC_WAREHOUSE", "Basement", GOAL_TYPE1, vect(224, -512, -192), rot(0,-16384,-16384)); loc = AddGoalLocation("02_NYC_WAREHOUSE", "Sewer", GOAL_TYPE1, vect(-1600.000000, 784.000000, -256.000000), rot(32768,-32768,0)); AddMutualExclusion(loc, loc2);// can't put Jock and the generator both in the sewers // pawns run into these and break them diff --git a/DXRMissions/DeusEx/Classes/DXRMissionsM05.uc b/DXRMissions/DeusEx/Classes/DXRMissionsM05.uc index b1b5b0782..cbf81b794 100644 --- a/DXRMissions/DeusEx/Classes/DXRMissionsM05.uc +++ b/DXRMissions/DeusEx/Classes/DXRMissionsM05.uc @@ -242,6 +242,8 @@ function AddMissionGoals() //You might still have the image in a NG+ scenario if (player().FindInventoryType(class'Image05_NYC_MJ12Lab')==None){ item = Spawn(class'Image05_NYC_MJ12Lab'); + item.ItemName=class'Image05_NYC_MJ12Lab'.Default.imageDescription; + item.ItemArticle="-"; item.Frob(player(), None); } } diff --git a/DXRMissions/DeusEx/Classes/DXRMissionsM06.uc b/DXRMissions/DeusEx/Classes/DXRMissionsM06.uc index 41918b3e4..038b3a3c5 100644 --- a/DXRMissions/DeusEx/Classes/DXRMissionsM06.uc +++ b/DXRMissions/DeusEx/Classes/DXRMissionsM06.uc @@ -1,5 +1,7 @@ class DXRMissionsM06 extends DXRMissions; +var bool M08Briefing; + function int InitGoals(int mission, string map) { @@ -174,11 +176,31 @@ function int InitGoalsRev(int mission, string map) function MissionTimer() { + local #var(prefix)TracerTong tong; + switch(dxr.localURL) { case "06_HONGKONG_WANCHAI_MARKET": if(dxr.flags.settings.goals > 0) UpdateGoalWithRandoInfo('InvestigateMaggieChow', "The sword may not be in Maggie's apartment, instead there will be a Datacube with a hint."); break; + case "06_HONGKONG_TONGBASE": + //Immediately start M08Briefing after M07Briefing, if possible + if (!M08Briefing && dxr.flagbase.GetBool('M07Briefing_played') && !dxr.flagbase.GetBool('M08Briefing_played')){ + if (player().conPlay==None){ + foreach AllActors(class'#var(prefix)TracerTong',tong){ break;} + + //Ensure we have line of sight and are reasonably close, otherwise this looks really dumb + //(Particularly if you did this the expected way with multiple visits) + if (VSize(tong.Location-player().Location) <= 180 && FastTrace(tong.Location,player().Location)){ + if (dxr.FlagBase.GetBool('LDDPJCIsFemale')){ + M08Briefing = (player().StartConversationByName('FemJCM08Briefing',tong)); + } else { + M08Briefing = (player().StartConversationByName('M08Briefing',tong)); + } + } + } + } + break; } } diff --git a/DXRMissions/DeusEx/Classes/DXRMissionsM09.uc b/DXRMissions/DeusEx/Classes/DXRMissionsM09.uc index 3f52c989f..350f64704 100644 --- a/DXRMissions/DeusEx/Classes/DXRMissionsM09.uc +++ b/DXRMissions/DeusEx/Classes/DXRMissionsM09.uc @@ -65,16 +65,16 @@ function int InitGoals(int mission, string map) loc = AddGoalLocation("09_NYC_SHIPBELOW", "Bilge Pumps Hallway", NORMAL_GOAL, vect(-2480.000000, -448.000000, -144.000000), rot(0, 32768, 0)); AddActorLocation(loc, 2, vect(-2522, -464, -144), rot(0,0,0)); - loc = AddGoalLocation("09_NYC_SHIPBELOW", "SE Electrical Room", NORMAL_GOAL, vect(-3952.000000, 768.000000, -416.000000), rot(0, 0, 0)); + loc = AddGoalLocation("09_NYC_SHIPBELOW", "SE Electrical Room", NORMAL_GOAL, vect(-3950, 740, -352), rot(0, 0, 0)); AddActorLocation(loc, 2, vect(-3908, 766, -416), rot(0,0,0)); - loc = AddGoalLocation("09_NYC_SHIPBELOW", "South Helipad", NORMAL_GOAL, vect(-5664.000000, -928.000000, -432.000000), rot(0, 16384, 0)); + loc = AddGoalLocation("09_NYC_SHIPBELOW", "South Helipad", NORMAL_GOAL, vect(-5664, -926, -418), rot(0, 16384, 0)); AddActorLocation(loc, 2, vect(-5664, -889, -432), rot(0,0,0)); - loc = AddGoalLocation("09_NYC_SHIPBELOW", "Helipad Storage Room", NORMAL_GOAL, vect(-4080.000000, -816.000000, -128.000000), rot(0, 32768, 0)); + loc = AddGoalLocation("09_NYC_SHIPBELOW", "Helipad Storage Room", NORMAL_GOAL, vect(-4082, -816, -128), rot(0, 32768, 0)); AddActorLocation(loc, 2, vect(-4120, -816, -128), rot(0,0,0)); - loc = AddGoalLocation("09_NYC_SHIPBELOW", "Helipad Air Control", NORMAL_GOAL, vect(-4720.000000, 1536.000000, -144.000000), rot(0, -16384, 0)); + loc = AddGoalLocation("09_NYC_SHIPBELOW", "Helipad Air Control", NORMAL_GOAL, vect(-4752, 1536, -144), rot(0, -16384, 0)); AddActorLocation(loc, 2, vect(-4717, 1501, -144), rot(0,0,0)); loc = AddGoalLocation("09_NYC_SHIPBELOW", "Fan Room", NORMAL_GOAL, vect(-3200.000000, -48.000000, -96.000000), rot(0, 0, 0)); @@ -83,19 +83,19 @@ function int InitGoals(int mission, string map) loc = AddGoalLocation("09_NYC_SHIPBELOW", "Engine Control Room", NORMAL_GOAL, vect(-288.000000, -432.000000, 112.000000), rot(-16384, 16384, 0)); AddActorLocation(loc, 2, vect(-288, -426, 62), rot(0,0,0)); - loc = AddGoalLocation("09_NYC_SHIPBELOW", "NW Engine Room", NORMAL_GOAL | VANILLA_GOAL, vect(832.000000,1024.000000,-432.000000), rot(0,49152,0)); + loc = AddGoalLocation("09_NYC_SHIPBELOW", "NW Engine Room", NORMAL_GOAL | VANILLA_GOAL, vect(832, 1022, -430), rot(0,49152,0)); AddActorLocation(loc, 2, vect(833.449036, 993.195618, -490.899567), rot(0,0,0)); - loc = AddGoalLocation("09_NYC_SHIPBELOW", "NE Electical Room", NORMAL_GOAL | VANILLA_GOAL, vect(-3680.000000,1647.000000,-416.000000), rot(0,49152,0)); + loc = AddGoalLocation("09_NYC_SHIPBELOW", "NE Electical Room", NORMAL_GOAL | VANILLA_GOAL, vect(-3680, 1647, -402), rot(0,49152,0)); AddActorLocation(loc, 2, vect(-3680.022217, 1616.057861, -490.899567), rot(0,0,0)); - loc = AddGoalLocation("09_NYC_SHIPBELOW", "East Helipad", NORMAL_GOAL | VANILLA_GOAL, vect(-6528.000000,200.000000,-448.000000), rot(0,65536,0)); + loc = AddGoalLocation("09_NYC_SHIPBELOW", "East Helipad", NORMAL_GOAL | VANILLA_GOAL, vect(-6526, 200, -418), rot(0,65536,0)); AddActorLocation(loc, 2, vect(-6499.218750, 200.039917, -490.899567), rot(0,0,0)); loc = AddGoalLocation("09_NYC_SHIPBELOW", "Bilge Pumps", NORMAL_GOAL | VANILLA_GOAL, vect(-3296.000000,-1662.000000,-416.000000), rot(0,81920,0)); AddActorLocation(loc, 2, vect(-3296.133789, -1632.118652, -490.899567), rot(0,0,0)); - loc = AddGoalLocation("09_NYC_SHIPBELOW", "SW Engine Room", NORMAL_GOAL | VANILLA_GOAL, vect(832.000000,-1024.000000,-416.000000), rot(0,16384,0)); + loc = AddGoalLocation("09_NYC_SHIPBELOW", "SW Engine Room", NORMAL_GOAL | VANILLA_GOAL, vect(832, -1024, -400), rot(0,16384,0)); AddActorLocation(loc, 2, vect(831.944641, -996.442627, -490.899567), rot(0,0,0)); return 92; @@ -209,6 +209,7 @@ function PreFirstEntryMapFixes() { local #var(prefix)Barrel1 barrel; local DeusExMover dxm; + local Actor a; local name barrelName; @@ -258,6 +259,18 @@ function PreFirstEntryMapFixes() } } + } else { + foreach AllActors(class'DeusExMover',dxm){ + if (dxm.Name=='DeusExMover40' || + dxm.Name=='DeusExMover16' || + dxm.Name=='DeusExMover33' || + dxm.Name=='DeusExMover31' || + dxm.Name=='DeusExMover32'){ + dxm.PrePivot=vect(-3,-2.5,5); + } + } + a = Spawnm(class'DynamicBlockMonsters',,, vect(-3926.773438, 739.635742, -456.895294)); + a.SetCollisionSize(100, 64); } } } @@ -269,7 +282,7 @@ function AfterMoveGoalToLocation(Goal g, GoalLocation Loc) if (g.name=="Jammer") { //Add a keypad to disable the jammer - keypad=Spawn(class'#var(prefix)Keypad1',,,Loc.positions[7].pos,Loc.positions[7].rot); + keypad = #var(prefix)Keypad1(AddActor(class'#var(prefix)Keypad1',Loc.positions[7].pos,Loc.positions[7].rot)); keypad.Event='EMOff'; keypad.bHackable=True; keypad.hackStrength=0.05; diff --git a/DXRModules/DeusEx/Classes/DXRAugmentations.uc b/DXRModules/DeusEx/Classes/DXRAugmentations.uc index c1541a19e..0af835a06 100644 --- a/DXRModules/DeusEx/Classes/DXRAugmentations.uc +++ b/DXRModules/DeusEx/Classes/DXRAugmentations.uc @@ -355,6 +355,9 @@ simulated function string DescriptionLevel(Actor act, int i, out string word) } else if( a.Class == class'#var(prefix)AugBallistic' || a.Class == class'#var(prefix)AugEMP' || a.Class == class'#var(prefix)AugEnviro' || a.Class == class'#var(prefix)AugShield') { word = "Damage Reduction"; + if(a.LevelValues[i] < 0) { + a.LevelValues[i] = 0; + } return int( (1.0 - a.LevelValues[i]) * 100.0 ) $ "%"; } else if( a.Class == class'#var(prefix)AugCloak' || a.Class == class'#var(prefix)AugRadarTrans') { @@ -411,6 +414,9 @@ simulated function string DescriptionLevel(Actor act, int i, out string word) #ifdef gmdx else if( a.Class == class'AugBallisticPassive') { word = "Damage Reduction"; + if(a.LevelValues[i] < 0) { + a.LevelValues[i] = 0; + } return int( (1.0 - a.LevelValues[i]) * 100.0 ) $ "%"; } else if( a.Class == class'AugIcarus' || a.Class == class'AugEnergyTransfer' || a.Class.Name == 'AugMetabolism' || a.Class.Name == 'AugAimbot' ) { diff --git a/DXRModules/DeusEx/Classes/DXRAutosave.uc b/DXRModules/DeusEx/Classes/DXRAutosave.uc index c042c3762..400b33d67 100644 --- a/DXRModules/DeusEx/Classes/DXRAutosave.uc +++ b/DXRModules/DeusEx/Classes/DXRAutosave.uc @@ -67,6 +67,7 @@ function NeedSave() autosave_combat = 1;// we're all in on this autosave because of the player rotation if(!set_player_pos) { set_player_pos = true; + class'DynamicTeleporter'.static.CheckTeleport(player(), coords_mult); player_pos = player().Location; player_rot = player().ViewRotation; } diff --git a/DXRModules/DeusEx/Classes/DXRDatacubes.uc b/DXRModules/DeusEx/Classes/DXRDatacubes.uc index dc6a35dfe..a4656964f 100644 --- a/DXRModules/DeusEx/Classes/DXRDatacubes.uc +++ b/DXRModules/DeusEx/Classes/DXRDatacubes.uc @@ -51,6 +51,73 @@ function vanilla_datacubes_rules() i++; break; + case "01_NYC_UNATCOHQ": + // Manderley's password + datacubes_rules[i].item_name = '01_Datacube01'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + break; + + case "02_NYC_BATTERYPARK": + // Castle Clinton underground access code - needs to be above ground + datacubes_rules[i].item_name = '02_Datacube15'; + datacubes_rules[i].min_pos = vect(-99999, -99999, 320); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + + break; + + case "02_NYC_UNDERGROUND": + // East Hatch Code + datacubes_rules[i].item_name = '02_Datacube03'; + datacubes_rules[i].min_pos = vect(1960, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + + //Other door code + datacubes_rules[i].item_name = '02_Datacube06'; + datacubes_rules[i].min_pos = vect(1960, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + + //Security Code that lets you rotate the bridge + datacubes_rules[i].item_name = '02_Datacube11'; + datacubes_rules[i].min_pos = vect(-1350, -99999, -670); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + + //Computer login + datacubes_rules[i].item_name = '02_Datacube02'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(-1350, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + + break; + + case "02_NYC_HOTEL": + // The code to Paul's bookshelf stash + datacubes_rules[i].item_name = '02_Datacube07'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, -2540, 99999); + datacubes_rules[i].allow = true; + i++; + + //Paul's computer password + datacubes_rules[i].item_name = '02_Datacube05'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, -2300, 99999); + datacubes_rules[i].allow = true; + i++; + + break; + case "02_NYC_WAREHOUSE": // ramp code datacubes_rules[i].item_name = '02_Datacube09'; @@ -65,6 +132,13 @@ function vanilla_datacubes_rules() datacubes_rules[i].max_pos = vect(99999, 99999, 99999); datacubes_rules[i].allow = true; i++; + + // TFRASE ValleyForge + datacubes_rules[i].item_name = '02_Datacube18'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; break; case "03_NYC_BatteryPark": @@ -93,6 +167,13 @@ function vanilla_datacubes_rules() datacubes_rules[i].allow = false; i++; + // disallow below ground level, like the sewers or water area + datacubes_rules[i].item_name = '03_Datacube10'; + datacubes_rules[i].min_pos = vect(-999999, -999999, -999999); + datacubes_rules[i].max_pos = vect(999999, 999999, -55); + datacubes_rules[i].allow = false; + i++; + // allow anywhere else past the gate datacubes_rules[i].item_name = '03_Datacube10'; datacubes_rules[i].min_pos = vect(1700, 2400, -999999); @@ -101,6 +182,41 @@ function vanilla_datacubes_rules() i++; break; + case "03_NYC_AIRFIELDHELIBASE": + //etodd computer password + datacubes_rules[i].item_name = '03_Datacube12'; + datacubes_rules[i].min_pos = vect(-999999, -999999, -999999); + datacubes_rules[i].max_pos = vect(999999, 999999, 999999); + datacubes_rules[i].allow = true; + i++; + break; + + case "03_NYC_747": + //Suspension crate code + datacubes_rules[i].item_name = '03_Datacube13'; + datacubes_rules[i].min_pos = vect(-999999, -999999, -999999); + datacubes_rules[i].max_pos = vect(999999, 999999, 999999); + datacubes_rules[i].allow = true; + i++; + break; + + case "04_NYC_HOTEL": + //Paul's computer password + datacubes_rules[i].item_name = '02_Datacube05'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, -2300, 99999); + datacubes_rules[i].allow = true; + i++; + + // The code to Paul's bookshelf stash + datacubes_rules[i].item_name = '02_Datacube07'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, -2540, 99999); + datacubes_rules[i].allow = true; + i++; + + break; + case "04_NYC_NSFHQ": // datacube with the password for sending the signal datacubes_rules[i].item_name = '04_Datacube01'; @@ -161,6 +277,55 @@ function vanilla_datacubes_rules() i++; break; + case "06_HONGKONG_HELIBASE": + //Security login + datacubes_rules[i].item_name = '06_Datacube18'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + break; + + case "06_HONGKONG_WANCHAI_STREET": + //"Insurgent" + datacubes_rules[i].item_name = '06_Book16'; + datacubes_rules[i].min_pos = vect(-1336,-1910,1950); + datacubes_rules[i].max_pos = vect(-116,-447,2311); + datacubes_rules[i].allow = true; + i++; + + //Tai-Fun and Insurgent + datacubes_rules[i].item_name = '06_Datacube21'; + datacubes_rules[i].min_pos = vect(-1336,-1910,1950); + datacubes_rules[i].max_pos = vect(-116,-447,2311); + datacubes_rules[i].allow = true; + i++; + break; + case "08_NYC_HOTEL": + // The code to Paul's bookshelf stash + datacubes_rules[i].item_name = '02_Datacube07'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, -2540, 99999); + datacubes_rules[i].allow = true; + i++; + break; + + case "09_NYC_DOCKYARD": + //Walton Simons login + datacubes_rules[i].item_name = '09_Datacube11'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + + //USFEMA login + datacubes_rules[i].item_name = '09_Datacube12'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + break; + case "09_NYC_SHIP": //Ramp Code datacubes_rules[i].item_name = '09_Datacube14'; @@ -183,6 +348,38 @@ function vanilla_datacubes_rules() datacubes_rules[i].max_pos = vect(99999, 99999, 1956.809082); datacubes_rules[i].allow = false; i++; + + //Login for the security system in the bunker warehouse + datacubes_rules[i].item_name = '10_Datacube12'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + break; + + case "10_PARIS_CLUB": + // Storage room code - main area of the club + datacubes_rules[i].item_name = '10_Datacube07'; + datacubes_rules[i].min_pos = vect(-1350, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + + //back office + datacubes_rules[i].item_name = '10_Datacube07'; + datacubes_rules[i].min_pos = vect(-2100,-1290,-99999); + datacubes_rules[i].max_pos = vect(-1652,-820,99999); + datacubes_rules[i].allow = true; + i++; + break; + + case "11_PARIS_EVERETT": + // Lucius DeBeers login + datacubes_rules[i].item_name = '11_Datacube01'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; break; case "11_PARIS_CATHEDRAL": @@ -212,6 +409,81 @@ function vanilla_datacubes_rules() i++; break; + case "14_VANDENBERG_SUB": + //Code for URV Bay doors - anywhere offshore + datacubes_rules[i].item_name = '14_Datacube01'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 2000, 99999); + datacubes_rules[i].allow = true; + i++; + + //Upper area onshore + datacubes_rules[i].item_name = '14_Datacube01'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -150); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + break; + + case "14_OCEANLAB_SILO": + //Code for missile launch controls - anywhere above ground + datacubes_rules[i].item_name = '14_Datacube05'; + datacubes_rules[i].min_pos = vect(-99999, -99999, 1415); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + + //underground, outside of the actual silo + datacubes_rules[i].item_name = '14_Datacube05'; + datacubes_rules[i].min_pos = vect(-99999, -3610, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 1415); + datacubes_rules[i].allow = true; + i++; + break; + + case "14_OCEANLAB_LAB": + //OCEANGUARD code to open sub bay doors. This is SUPER IMPORTANT. This needs to not fuck up. + //Intent is for it to be able to spawn anywhere from the sub bay up to and including the storage room, but not the locked one + + //Exclude Locked storage room + datacubes_rules[i].item_name = '14_Datacube06'; + datacubes_rules[i].min_pos = vect(598,-178,-1635); + datacubes_rules[i].max_pos = vect(976,322,-1457); + datacubes_rules[i].allow = false; + i++; + + //Between Sub Bay and door to Greasel Lab, top floor + datacubes_rules[i].item_name = '14_Datacube06'; + datacubes_rules[i].min_pos = vect(-260, -240, -1637); + datacubes_rules[i].max_pos = vect(1879, 960, -1457); + datacubes_rules[i].allow = true; + i++; + + //All floors of that main central building with the sub bay + datacubes_rules[i].item_name = '14_Datacube06'; + datacubes_rules[i].min_pos = vect(-230,-221,-2535); + datacubes_rules[i].max_pos = vect(464,1431,-1448); + datacubes_rules[i].allow = true; + i++; + + //Explicitly exclude EVERYTHING else for safety + datacubes_rules[i].item_name = '14_Datacube06'; + datacubes_rules[i].min_pos = vect(-99999,-99999,-99999); + datacubes_rules[i].max_pos = vect(99999,99999,99999); + datacubes_rules[i].allow = false; + i++; + break; + + case "14_OCEANLAB_UC": + //Code for walkway security computer - could go anywhere except for across the bridge (which has no loose items) + datacubes_rules[i].item_name = '14_Datacube03'; + datacubes_rules[i].min_pos = vect(-99999, -99999, -99999); + datacubes_rules[i].max_pos = vect(99999, 99999, 99999); + datacubes_rules[i].allow = true; + i++; + + break; + case "15_AREA51_PAGE": datacubes_rules[i].item_name = '15_Datacube18';// LAB 12 / graytest datacubes_rules[i].min_pos = vect(4774.132813, -10507.679688, -5294.627441); @@ -225,6 +497,15 @@ function vanilla_datacubes_rules() datacubes_rules[i].allow = false; i++; break; + + case "15_AREA51_FINAL": + //Code for stairwell blastdoor + datacubes_rules[i].item_name = '15_Datacube08'; + datacubes_rules[i].min_pos = vect(-5655, -5190, -1700); + datacubes_rules[i].max_pos = vect(-2376, -2527, -534); + datacubes_rules[i].allow = true; + i++; + break; } } @@ -329,8 +610,8 @@ function _RandoInfoDev(#var(prefix)InformationDevices id, bool containers) } } - l("datacube "$id$" got num "$num$" with "$bads$" unsafe positions"); - slot=rng(num+1);//+1 for the vanilla location + l("DatacubePositionCheck datacube "$id$" got num "$num$" with "$bads$" unsafe positions in map "$dxr.localurl); + slot=rng(num+1);//+1 for the vanilla location, since we're not in the list if(slot==0) { l("not swapping infodevice "$ActorToString(id)); return; diff --git a/DXRModules/DeusEx/Classes/DXRDoors.uc b/DXRModules/DeusEx/Classes/DXRDoors.uc index bbaf8483f..454271e45 100644 --- a/DXRModules/DeusEx/Classes/DXRDoors.uc +++ b/DXRModules/DeusEx/Classes/DXRDoors.uc @@ -17,7 +17,7 @@ var config float min_lock_adjust, max_lock_adjust, min_door_adjust, max_door_adj function CheckConfig() { local int i; - if( ConfigOlderThan(2,5,2,3) ) { + if( ConfigOlderThan(2,5,4,3) ) { i=0; // SmugglersFrontDoor for all 3 maps door_fixes[i].map = "02_NYC_STREET"; @@ -176,6 +176,23 @@ function CheckConfig() door_fixes[i].bHighlight = true; i++; + // Always make smugglers stash highlightable and breakable + door_fixes[i].map = "02_NYC_SMUG"; + door_fixes[i].tag = 'mirrordoor'; + door_fixes[i].bBreakable = true; + door_fixes[i].minDamageThreshold = 5; + door_fixes[i].doorStrength = 0.6; + door_fixes[i].bHighlight = true; + i++; + + door_fixes[i]=door_fixes[i-1]; + door_fixes[i].map = "04_NYC_SMUG"; + i++; + + door_fixes[i]=door_fixes[i-1]; + door_fixes[i].map = "08_NYC_SMUG"; + i++; + min_lock_adjust=0.5; max_lock_adjust=1.5; min_door_adjust=0.5; diff --git a/DXRModules/DeusEx/Classes/DXREnemies.uc b/DXRModules/DeusEx/Classes/DXREnemies.uc index 7492bda15..2dead3e9d 100644 --- a/DXRModules/DeusEx/Classes/DXREnemies.uc +++ b/DXRModules/DeusEx/Classes/DXREnemies.uc @@ -134,8 +134,20 @@ function CheckConfig() function FirstEntry() { local PlaceholderEnemy placeholder; + local #var(prefix)ScriptedPawn sp; + Super.FirstEntry(); + if(dxr.localURL == "10_PARIS_METRO" && dxr.flags.moresettings.remove_paris_mj12 > 0) { + foreach AllActors(class'#var(prefix)ScriptedPawn', sp) { + if(sp.Alliance=='mj12' && chance_single(dxr.flags.moresettings.remove_paris_mj12)) { + sp.Event=''; + sp.bHidden=true; + sp.Destroy(); + } + } + } + SwapScriptedPawns(dxr.flags.settings.enemiesshuffled, true); // delete placeholders after doing swaps but before doing clones foreach AllActors(class'PlaceholderEnemy', placeholder) { diff --git a/DXRModules/DeusEx/Classes/DXREnemiesBase.uc b/DXRModules/DeusEx/Classes/DXREnemiesBase.uc index 728c413db..5f141be26 100644 --- a/DXRModules/DeusEx/Classes/DXREnemiesBase.uc +++ b/DXRModules/DeusEx/Classes/DXREnemiesBase.uc @@ -197,7 +197,7 @@ function ScriptedPawn RandomEnemy(ScriptedPawn base, int percent) { local class newclass; local ScriptedPawn n; - local int i, faction; + local int i, faction, oldSeed; local float r; faction = GetFactionId(base); r = initchance(); @@ -212,6 +212,7 @@ function ScriptedPawn RandomEnemy(ScriptedPawn base, int percent) if( newclass == None && IsHuman(base.class) == false && chance_single(dxr.flags.settings.enemies_nonhumans)==false ) return None; if( IsHuman(newclass) == false && chance_single(dxr.flags.settings.enemies_nonhumans)==false ) return None; + oldSeed = BranchSeed(base $ newclass); n = CloneScriptedPawn(base, newclass); l("new RandomEnemy("$base$", "$percent$") == "$n); if( n != None ) { @@ -219,6 +220,7 @@ function ScriptedPawn RandomEnemy(ScriptedPawn base, int percent) CheckHelmet(n); } //else RandomizeSize(n); + ReapplySeed(oldSeed); return n; } @@ -248,11 +250,18 @@ function ScriptedPawn CloneScriptedPawn(ScriptedPawn p, optional class= i) slot++; if( ! ShouldSwap(temp[i], temp[slot]) ) { continue; @@ -107,7 +117,7 @@ function SwapScriptedPawns(int percent, bool enemies) // TODO: swap non-weapons/ammo inventory, only need to swap nanokeys? SwapItems(temp[i], temp[slot]); - if( temp[i].Tag != 'enemy_bot' && temp[slot].Tag != 'enemy_bot' ) // 12_VANDENBERG_CMD fix, see Mission12.uc https://discord.com/channels/823629359931195394/823629360929046530/974454774034497567 + if( !keepTags && temp[i].Tag != keepTagName && temp[slot].Tag != keepTagName ) SwapNames(temp[i].Tag, temp[slot].Tag); SwapNames(temp[i].Event, temp[slot].Event); SwapNames(temp[i].AlarmTag, temp[slot].AlarmTag); diff --git a/DXRModules/DeusEx/Classes/DXREvents.uc b/DXRModules/DeusEx/Classes/DXREvents.uc index 97069c53b..2970c1dbf 100644 --- a/DXRModules/DeusEx/Classes/DXREvents.uc +++ b/DXRModules/DeusEx/Classes/DXREvents.uc @@ -1,97 +1,5 @@ -class DXREvents extends DXRActorsBase; - -var bool died; -var name watchflags[32]; -var int num_watchflags; -var int bingo_win_countdown; -var name rewatchflags[8]; -var int num_rewatchflags; -var float PoolBallHeight; -var int NumPoolTables, PoolTablesSunk; - -struct BingoOption { - var string event, desc; - var int max; - var int missions;// bit masks -}; -var BingoOption bingo_options[300]; - -struct MutualExclusion { - var string e1, e2; -}; -var MutualExclusion mutually_exclusive[64]; - -struct ActorWatchItem { - var Actor a; - var String BingoEvent; -}; -var ActorWatchItem actor_watch[75]; -var int num_watched_actors; - -function AddWatchedActor(Actor a,String eventName) -{ - if (num_watched_actors>=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 ArrayCount(watchflags)) - err("WatchFlag num_watchflags > ArrayCount(watchflags)"); -} - -//Only actually add the flag to the list if it isn't already set -function RewatchFlag(name flag, optional bool disallow_immediate){ - if (!dxr.flagbase.GetBool(flag)) { - WatchFlag(flag,disallow_immediate); - // rewatchflags will get checked in AnyEntry so they can be removed - rewatchflags[num_rewatchflags++] = flag; - } else { - l("RewatchFlag "$flag$" is already set!"); - } -} - -function ReportMissingFlag(name flag, string eventname) { - if( ! dxr.flagbase.GetBool(flag) ) { - SendFlagEvent(eventname, true); - } -} - -function Ending_FirstEntry() -{ - local int ending; - - ending = 0; - - switch(dxr.localURL) - { - //Make sure we actually are only running on the endgame levels - //Just in case we hit a custom level with mission 99 or something - case "ENDGAME1": //Tong - ending = 1; - break; - case "ENDGAME2": //Helios - ending = 2; - break; - case "ENDGAME3": //Everett - ending = 3; - break; - case "ENDGAME4": //Dance party - ending = 4; - break; - default: - //In case rando runs some player level or something with mission 99 - break; - } - - if (ending!=0){ - //Notify of game completion with correct ending number - BeatGame(dxr,ending); - } -} - -simulated function AnyEntry() -{ - local int r, w; - Super.AnyEntry(); - SetTimer(1, true); - - for(w=0; w className) -{ - local Actor a; - - foreach AllActors(className,a){ - return True; - }; - return False; -} - -simulated function int PoolBallsSunk() -{ - local #var(prefix)Poolball cue,ball; - local int ballsSunk,tablesSunk,freshSink,radius; - - radius=99999; - - //I think Airfield is the only map with more than one, - //Need to make sure we only count the balls for each individual table. - //Those pool tables don't have anywhere for the balls to fall out, so - //radius can be contained. Other tables have a hole for the balls to - //come out, so we want to be able to check a larger radius. - if (NumPoolTables>1){ - radius = 150; - } - - tablesSunk=0; - foreach AllActors(class'#var(prefix)Poolball',cue){ - if (cue.SkinColor==SC_Cue){ - ballsSunk=0; - foreach cue.RadiusActors(class'#var(prefix)Poolball',ball,radius){ - if (ball.Location.Z <= PoolBallHeight){ - ballsSunk++; - } - } - if (ballsSunk>=16){ - tablesSunk++; - } - } - } - - if (tablesSunk <= PoolTablesSunk){ - return 0; - } - freshSink = tablesSunk-PoolTablesSunk; - PoolTablesSunk = tablesSunk; - - return freshSink; -} - -simulated function InitPoolBalls() -{ - local #var(prefix)Poolball ball; - PoolBallHeight = 9999; - NumPoolTables=0; - PoolTablesSunk=0; - - foreach AllActors(class'#var(prefix)Poolball',ball){ - if (ball.Location.Z < PoolBallHeight){ - PoolBallHeight = ball.Location.Z; - } - if (ball.SkinColor==SC_Cue){ - NumPoolTables+=1; - } - } - - PoolBallHeight -= 1; -} - -simulated function bool WatchGuntherKillSwitch() -{ - local GuntherHermann gunther; - - foreach AllActors(class'GuntherHermann',gunther){ - if (gunther.GetStateName()=='KillswitchActivated'){ - return True; - } - }; - return False; -} - -simulated function bool CheckForNanoKey(String keyID) -{ - - local name keyName; - - if (player()==None){ - return False; - } - - if (player().KeyRing==None){ - return False; - } - - keyName = StringToName(keyID); - - return player().KeyRing.HasKey(keyName); -} - -simulated function Timer() -{ - local int i,j,num; - local bool FlagTriggered; - - if( dxr == None || dxr.flagbase == None ) { - return; - } - - CheckWatchedActors(); - - for(i=0; i0){ - for (j=0;j= NumPoolTables){ - num_watchflags--; - watchflags[i] = watchflags[num_watchflags]; - watchflags[num_watchflags]=''; - i--; - } - continue; - } - } else if( watchflags[i] == 'FlowersForTheLab' ) { - if (ClassInLevel(class'#var(prefix)Flowers')){ - FlagTriggered=True; - } - } else if (InStr(watchflags[i],"WatchKeys_")!=-1) { - if (CheckForNanoKey(Mid(watchflags[i],10))){ - FlagTriggered=True; - } - } - - if( dxr.flagbase.GetBool(watchflags[i]) || FlagTriggered ) { - SendFlagEvent(watchflags[i]); - num_watchflags--; - watchflags[i] = watchflags[num_watchflags]; - watchflags[num_watchflags]=''; - i--; - } - } - // for nonvanilla, because GameInfo.Died is called before the player's Dying state calls root.ClearWindowStack(); - if(died) { - class'DXRHints'.static.AddDeath(dxr, player()); - died = false; - } - - if (bingo_win_countdown>=0){ - HandleBingoWinCountdown(); - } -} - -function PreTravel() -{ - Super.PreTravel(); - SetTimer(0, false); -} - -function BingoWinScreen() -{ - local #var(PlayerPawn) p; - local bool showMsg; - - p = player(); - if ( Level.Netmode == NM_Standalone ) { - //Make it harder to get murdered during the countdown - Level.Game.SetGameSpeed(0.05); - SetTimer(0.1, true); //You would think this would be 0.05, but it's not - } - p.ReducedDamageType = 'All';// god mode - p.DesiredFlashScale = 0; - p.DesiredFlashFog = vect(0,0,0); - - showMsg=False; - if (InGame()){ - showMsg=True; - } else { - //Consider it in-game if a big message is up... - if (DeusExRootWindow(p.rootWindow).GetTopWindow()!=None){ - if (DeusExRootWindow(p.rootWindow).GetTopWindow().IsA('DXRBigMessage')){ - showMsg = True; - } - } - } - - if(showMsg) { - p.ShowHud(False); - //Show win message - class'DXRBigMessage'.static.CreateBigMessage(dxr.player,None,"Congratulations! You finished your bingo!","Game ending in "$bingo_win_countdown$" seconds"); - } - if (bingo_win_countdown == 2 && !#defined(vanilla)) { - //Give it 2 seconds to send the tweet - //This is still needed outside of vanilla - BeatGame(dxr,4); - } -} - -function HandleBingoWinCountdown() -{ - //Blocked in HX for now (Blocked at the check, but here for safety as well) - if(#defined(hx)) return; - - if (bingo_win_countdown > 0) { - BingoWinScreen(); - bingo_win_countdown--; - } else if (bingo_win_countdown == 0) { - if ( Level.Netmode == NM_Standalone ) { - Level.Game.SetGameSpeed(1); - SetTimer(1, true); - } - //Go to bingo win ending - Level.Game.SendPlayer(dxr.player,"99_EndGame4"); - } -} - -function bool SpecialTriggerHandling(Actor Other, Pawn Instigator) -{ - local #var(prefix)MapExit m; - if (tag == 'Boat_Exit'){ - dxr.flagbase.SetBool('DXREvents_LeftOnBoat', true,, 999); - - foreach AllActors(class'#var(prefix)MapExit',m,'Boat_Exit2'){ - m.Trigger(Other,Instigator); - } - return true; - } - - return false; -} - -function Trigger(Actor Other, Pawn Instigator) -{ - local string j; - local class js; - local name useTag; - - js = class'Json'; - - //Leave this variable for now, in case we need to massage tags - //again at some point in the future - useTag = tag; - - Super.Trigger(Other, Instigator); - l("Trigger("$Other$", "$instigator$")"); - - if (!SpecialTriggerHandling(Other,Instigator)){ - j = js.static.Start("Trigger"); - js.static.Add(j, "instigator", GetActorName(Instigator)); - js.static.Add(j, "tag", useTag); - js.static.add(j, "other", GetActorName(other)); - GeneralEventData(dxr, j); - js.static.End(j); - - class'DXRTelemetry'.static.SendEvent(dxr, Instigator, j); - _MarkBingo(useTag); - } -} - -function SendFlagEvent(coerce string eventname, optional bool immediate, optional string extra) -{ - local string j; - local class js; - js = class'Json'; - - l("SendFlagEvent " $ eventname @ immediate @ extra); - - if(eventname ~= "M02HostagesRescued") {// for the hotel, set by Mission02.uc - M02HotelHostagesRescued(); - return; - } - else if(eventname ~= "MS_DL_Played") {// this is a generic flag name used in a few of the mission scripts - if(dxr.localURL ~= "02_NYC_BATTERYPARK") { - BatteryParkHostages(); - } - return; - } - - j = js.static.Start("Flag"); - js.static.Add(j, "flag", eventname); - js.static.Add(j, "immediate", immediate); - js.static.Add(j, "location", vectclean(dxr.player.location)); - if(extra != "") - js.static.Add(j, "extra", extra); - GeneralEventData(dxr, j); - js.static.End(j); - - class'DXRTelemetry'.static.SendEvent(dxr, dxr.player, j); - _MarkBingo(eventname); -} - -function M02HotelHostagesRescued() -{ - local bool MaleHostage_Dead, FemaleHostage_Dead, GilbertRenton_Dead; - MaleHostage_Dead = dxr.flagbase.GetBool('MaleHostage_Dead'); - FemaleHostage_Dead = dxr.flagbase.GetBool('FemaleHostage_Dead'); - GilbertRenton_Dead = dxr.flagbase.GetBool('GilbertRenton_Dead'); - if( !MaleHostage_Dead && !FemaleHostage_Dead && !GilbertRenton_Dead) { - SendFlagEvent("HotelHostagesSaved"); - } -} - -function BatteryParkHostages() -{ - local bool SubTerroristsDead, EscapeSuccessful, SubHostageMale_Dead, SubHostageFemale_Dead; - SubTerroristsDead = dxr.flagbase.GetBool('SubTerroristsDead'); - EscapeSuccessful = dxr.flagbase.GetBool('EscapeSuccessful'); - SubHostageMale_Dead = dxr.flagbase.GetBool('SubHostageMale_Dead'); - SubHostageFemale_Dead = dxr.flagbase.GetBool('SubHostageFemale_Dead'); - - l("BatteryParkHostages() " $ SubTerroristsDead @ EscapeSuccessful @ SubHostageMale_Dead @ SubHostageFemale_Dead); - if( (SubTerroristsDead || EscapeSuccessful) && !SubHostageMale_Dead && !SubHostageFemale_Dead ) { - SendFlagEvent("SubwayHostagesSaved"); - } -} - -static function _DeathEvent(DXRando dxr, Actor victim, Actor Killer, coerce string damageType, vector HitLocation, string type) -{ - local string j; - local class js; - local bool unconcious; - js = class'Json'; - - j = js.static.Start(type); - js.static.Add(j, "victim", GetActorName(victim)); - js.static.Add(j, "victimBindName", victim.BindName); - js.static.Add(j, "victimRandomizedName", GetRandomizedName(victim)); - if(#var(prefix)ScriptedPawn(victim) != None) { - unconcious = #var(prefix)ScriptedPawn(victim).bStunned; - js.static.Add(j, "victimUnconcious", unconcious); - } - - if(Killer != None) { - js.static.Add(j, "killerclass", Killer.Class.Name); - js.static.Add(j, "killer", GetActorName(Killer)); - js.static.Add(j, "killerRandomizedName", GetRandomizedName(Killer)); - } - js.static.Add(j, "dmgtype", damageType); - GeneralEventData(dxr, j); - js.static.Add(j, "location", dxr.flags.vectclean(victim.Location)); - js.static.End(j); - class'DXRTelemetry'.static.SendEvent(dxr, victim, j); -} - -static function string GetRandomizedName(Actor a) -{ - local ScriptedPawn sp; - sp = ScriptedPawn(a); - if(sp == None || sp.bImportant) return ""; - return sp.FamiliarName; -} - -static function AddPlayerDeath(DXRando dxr, #var(PlayerPawn) player, optional Actor Killer, optional coerce string damageType, optional vector HitLocation) -{ - local DXREvents ev; - class'DXRStats'.static.AddDeath(player); - - if(#defined(injections)) - class'DXRHints'.static.AddDeath(dxr, player); - else { - // for nonvanilla, because GameInfo.Died is called before the player's Dying state calls root.ClearWindowStack(); - ev = DXREvents(dxr.FindModule(class'DXREvents')); - if(ev != None) - ev.died = true; - } - - if(Killer == None) { - if(player.myProjKiller != None) - Killer = player.myProjKiller; - if(player.myTurretKiller != None) - Killer = player.myTurretKiller; - if(player.myPoisoner != None) - Killer = player.myPoisoner; - if(player.myBurner != None) - Killer = player.myBurner; - // myKiller is only set in multiplayer - if(player.myKiller != None) - Killer = player.myKiller; - } - - if(damageType == "shot") { - if( !IsHuman(Killer.class) && Robot(Killer) == None ) { - // only humans and robots can shoot? karkians deal shot damage - damageType = ""; - } - } - - _DeathEvent(dxr, player, Killer, damageType, HitLocation, "DEATH"); -} - -static function AddPawnDeath(ScriptedPawn victim, optional Actor Killer, optional coerce string damageType, optional vector HitLocation) -{ - local DXRando dxr; - local DXREvents e; - foreach victim.AllActors(class'DXRando', dxr) break; - - if(dxr != None) - e = DXREvents(dxr.FindModule(class'DXREvents')); - log(e$".AddPawnDeath "$dxr$", "$victim); - if(e != None) - e._AddPawnDeath(victim, Killer, damageType, HitLocation); -} - -function bool checkInitialAlliance(ScriptedPawn p,name allianceName, float allianceLevel) -{ - local int i; - - for (i=0;i<8;i++){ - if (p.InitialAlliances[i].AllianceName==allianceName && - p.InitialAlliances[i].AllianceLevel~=allianceLevel){ - return True; - } - } - return False; -} - -function bool isInitialPlayerAlly(ScriptedPawn p) -{ - return checkInitialAlliance(p,'Player',1.0); -} - -function bool isInitialPlayerEnemy(ScriptedPawn p) -{ - return checkInitialAlliance(p,'Player',-1.0); -} - -function _AddPawnDeath(ScriptedPawn victim, optional Actor Killer, optional coerce string damageType, optional vector HitLocation) -{ - local string classname; - - _MarkBingo(victim.BindName$"_Dead"); - _MarkBingo(victim.BindName$"_DeadM" $ dxr.dxInfo.missionNumber); - if( Killer == None || #var(PlayerPawn)(Killer) != None ) { - classname = string(victim.class.name); - if(#defined(hx) && InStr(classname, "HX")==0) { - classname = Mid(classname, 2); - } - - if (IsHuman(victim.class) && ((damageType == "Stunned") || - (damageType == "KnockedOut") || - (damageType == "Poison") || - (damageType == "PoisonEffect"))){ - _MarkBingo(classname$"_ClassUnconscious"); - _MarkBingo(classname$"_ClassUnconsciousM" $ dxr.dxInfo.missionNumber); - } else { - _MarkBingo(classname$"_ClassDead"); - _MarkBingo(classname$"_ClassDeadM" $ dxr.dxInfo.missionNumber); - - //Were they an ally? Skip on NSF HQ, because that's kind of a bait - if (!isInitialPlayerEnemy(victim) && !IsCritter(victim) && //Must have not been an enemy initially - (dxr.localURL!="04_NYC_NSFHQ" || (dxr.localURL=="04_NYC_NSFHQ" && dxr.flagbase.GetBool('DL_SimonsPissed_Played')==False)) //Not on the NSF HQ map, or if it is, before you send the signal (kludgy) - ){ - _MarkBingo("AlliesKilled"); - } - - } - if (damageType=="stomped" && IsHuman(victim.class)){ //If you stomp a human to death... - _MarkBingo("HumanStompDeath"); - } - } - - if(!victim.bImportant) - return; - - if(victim.BindName == "PaulDenton") - dxr.flagbase.SetBool('DXREvents_PaulDead', true,, 999); - else if(victim.BindName == "AnnaNavarre" && dxr.flagbase.GetBool('annadies')) { - _MarkBingo("AnnaKillswitch"); - Killer = player(); - } - - _DeathEvent(dxr, victim, Killer, damageType, HitLocation, "PawnDeath"); -} + case "15_AREA51_ENTRANCE": + WatchFlag('PlayPool'); + RewatchFlag('WaltonBadass_Played'); + InitPoolBalls(); -static function AddDeath(Pawn victim, optional Actor Killer, optional coerce string damageType, optional vector HitLocation) -{ - local DXRando dxr; - local #var(PlayerPawn) player; - local #var(prefix)ScriptedPawn sp; - player = #var(PlayerPawn)(victim); - sp = #var(prefix)ScriptedPawn(victim); - if(player != None) { - foreach victim.AllActors(class'DXRando', dxr) break; - AddPlayerDeath(dxr, player, Killer, damageType, HitLocation); - } - else if(sp != None) - AddPawnDeath(sp, Killer, damageType, HitLocation); -} + foreach AllActors(class'#var(DeusExPrefix)Mover',dxm){ + if (dxm.tag=='chamber1'){ + dxm.event='sleeppod1'; + } else if (dxm.tag=='chamber3'){ + dxm.event='sleeppod2'; + } else if (dxm.tag=='chamber4'){ + dxm.event='sleeppod3'; + } else if (dxm.tag=='chamber5'){ + dxm.event='sleeppod4'; + } + } + bt = class'BingoTrigger'.static.Create(self,'sleeppod1',vectm(0,0,0)); + bt.bingoEvent = "Area51SleepingPod"; + bt.bDestroyOthers=False; -static function PaulDied(DXRando dxr) -{ - local string j; - local class js; - js = class'Json'; - - j = js.static.Start("PawnDeath"); - js.static.Add(j, "victim", "Paul Denton"); - js.static.Add(j, "victimBindName", "PaulDenton"); - js.static.Add(j, "dmgtype", ""); - GeneralEventData(dxr, j); - js.static.Add(j, "location", dxr.flags.vectclean(dxr.player.location)); - js.static.End(j); - dxr.flagbase.SetBool('DXREvents_PaulDead', true,, 999); - class'DXRTelemetry'.static.SendEvent(dxr, dxr.player, j); - MarkBingo(dxr, "PaulDenton_Dead"); -} + bt = class'BingoTrigger'.static.Create(self,'sleeppod2',vectm(0,0,0)); + bt.bingoEvent = "Area51SleepingPod"; + bt.bDestroyOthers=False; -static function SavedPaul(DXRando dxr, #var(PlayerPawn) player, optional int health) -{ - local string j; - local class js; - js = class'Json'; - - j = js.static.Start("SavedPaul"); - if(health > 0) - js.static.Add(j, "PaulHealth", health); - GeneralEventData(dxr, j); - js.static.End(j); - - class'DXRTelemetry'.static.SendEvent(dxr, dxr.player, j); - MarkBingo(dxr, "SavedPaul"); -} + bt = class'BingoTrigger'.static.Create(self,'sleeppod3',vectm(0,0,0)); + bt.bingoEvent = "Area51SleepingPod"; + bt.bDestroyOthers=False; -static function BeatGame(DXRando dxr, int ending) -{ - local PlayerDataItem data; - local DXRStats stats; - local string j; - local class js; - js = class'Json'; - - stats = DXRStats(dxr.FindModule(class'DXRStats')); - - j = js.static.Start("BeatGame"); - js.static.Add(j, "ending", ending); - js.static.Add(j, "SaveCount", dxr.player.saveCount); - js.static.Add(j, "Autosaves", stats.GetDataStorageStat(dxr, "DXRStats_autosaves")); - js.static.Add(j, "deaths", stats.GetDataStorageStat(dxr, "DXRStats_deaths")); - js.static.Add(j, "LoadCount", stats.GetDataStorageStat(dxr, "DXRStats_loads")); - js.static.Add(j, "maxrando", dxr.flags.maxrando); - js.static.Add(j, "bSetSeed", dxr.flags.bSetSeed); - data = class'PlayerDataItem'.static.GiveItem(dxr.player); - js.static.Add(j, "initial_version", data.initial_version); - js.static.Add(j, "combat_difficulty", dxr.player.CombatDifficulty); - js.static.Add(j, "rando_difficulty", dxr.flags.difficulty); - js.static.Add(j, "cheats", dxr.player.FlagBase.GetInt('DXRStats_cheats')); - - if (dxr.player.carriedDecoration!=None){ - js.static.Add(j, "carriedItem", dxr.player.carriedDecoration.Class); - } - else if(dxr.player.inHand.IsA('POVCorpse')){ - js.static.Add(j, "carriedItem", POVCorpse(dxr.player.inHand).carcClassString); - } + bt = class'BingoTrigger'.static.Create(self,'sleeppod4',vectm(0,0,0)); + bt.bingoEvent = "Area51SleepingPod"; + bt.bDestroyOthers=False; - GeneralEventData(dxr, j); - BingoEventData(dxr, j); - AugmentationData(dxr, j); - GameTimeEventData(dxr, j); - js.static.Add(j, "score", stats.ScoreRun()); - js.static.End(j); + bt = class'BingoTrigger'.static.Create(self,'steam1',vectm(0,0,0)); + bt.bingoEvent = "Area51SteamValve"; + bt.bDestroyOthers=False; - class'DXRTelemetry'.static.SendEvent(dxr, dxr.player, j); -} + bt = class'BingoTrigger'.static.Create(self,'Steam2',vectm(0,0,0)); + bt.bingoEvent = "Area51SteamValve"; + bt.bDestroyOthers=False; -static function ExtinguishFire(DXRando dxr, string extinguisher, DeusExPlayer player) -{ - local string j; - local class js; - js = class'Json'; - - j = js.static.Start("ExtinguishFire"); - js.static.Add(j, "extinguisher", extinguisher); - js.static.Add(j, "location", dxr.flags.vectclean(player.Location)); - GeneralEventData(dxr, j); - js.static.End(j); - - class'DXRTelemetry'.static.SendEvent(dxr, player, j); - MarkBingo(dxr, "ExtinguishFire"); -} + break; + case "15_AREA51_FINAL": + RewatchFlag('WaltonBadass_Played'); + foreach AllActors(class'#var(prefix)BookOpen', book) { + if (book.textTag == '15_Book01'){ //This copy of Jacob's Shadow is also in _BUNKER and _ENTRANCE + book.textTag = '15_Book02'; //Put that good Thursday man back where he (probably) belongs + } + } -static function GeneralEventData(DXRando dxr, out string j) -{ - local string loadout,lang; - local class js; - js = class'Json'; - - js.static.Add(j, "PlayerName", GetActorName(dxr.player)); - js.static.Add(j, "map", dxr.localURL); - js.static.Add(j, "mapname", dxr.dxInfo.MissionLocation); - js.static.Add(j, "mission", dxr.dxInfo.missionNumber); - js.static.Add(j, "TrueNorth", dxr.dxInfo.TrueNorth); - js.static.Add(j, "PlayerIsFemale", dxr.flagbase.GetBool('LDDPJCIsFemale')); - js.static.Add(j, "GameMode", dxr.flags.GameModeName(dxr.flags.gamemode)); - js.static.Add(j, "newgameplus_loops", dxr.flags.newgameplus_loops); - - loadout = GetLoadoutName(dxr); - if(loadout != "") - js.static.Add(j, "loadout", loadout); - - lang = GetConfig("Engine.Engine", "Language"); - if(lang != "") - js.static.Add(j, "language", lang); -} + bt = class'BingoTrigger'.static.Create(self,'HeliosControlArms',vectm(-3995,2458,128),250,40); + bt = class'BingoTrigger'.static.Create(self,'HeliosControlArms',vectm(-3995,2458,-123),250,40); + bt = class'BingoTrigger'.static.Create(self,'HeliosControlArms',vectm(-3995,2458,-392),250,40); + bt = class'BingoTrigger'.static.Create(self,'HeliosControlArms',vectm(-3995,2458,-902),250,40); + bt = class'BingoTrigger'.static.Create(self,'HeliosControlArms',vectm(-3995,2458,-1152),250,40); + bt = class'BingoTrigger'.static.Create(self,'HeliosControlArms',vectm(-3995,2458,-1413),250,40); -static function AugmentationData(DXRando dxr, out string j) -{ - local Augmentation anAug; - local string augId,augName,augInfo; - local int level; + bt = class'BingoTrigger'.static.Create(self,'A51ExplosiveLocker',vectm(-5845,-385,-1485),150,40); - anAug = dxr.player.AugmentationSystem.FirstAug; - while(anAug != None) - { - if (anAug.HotKeyNum <= 0){ //I think if you uninstall an aug it becomes -1? - anAug = anAug.next; - continue; + foreach AllActors(class'WaterZone', water) { + if (RevisionMaps && water.Name=='WaterZone3'){ //Revision has a second tank + water.ZonePlayerEvent = 'A51SeparationSwim'; + } else if (water.Name=='WaterZone2'){ + water.ZonePlayerEvent = 'A51SeparationSwim'; + } } - augId = "Aug-"$anAug.HotKeyNum; - augName = ""$anAug.Class.Name; - level = anAug.CurrentLevel; - if (anAug.bBoosted){ - level = level-1; + bt = class'BingoTrigger'.static.Create(self,'A51SeparationSwim',vectm(0,0,0)); + + break; + case "15_AREA51_PAGE": +#ifdef vanilla + foreach AllActors(class'WaterZone', water) { + if (water.Name=='WaterZone5'){// in GMDX v10 and Revision it's WaterZone0 + water.ZonePlayerEvent = 'unbirth'; + } } + bt = class'BingoTrigger'.static.Create(self,'unbirth',vectm(0,0,0)); +#endif + bt = class'BingoTrigger'.static.Create(self,'Set_flag_helios',vectm(0,0,0)); + bt = class'BingoTrigger'.static.Create(self,'coolant_switch',vectm(0,0,0)); - augInfo = "{\"name\":\"" $ augName $"\",\"level\":"$level$"}"; + bt = class'BingoTrigger'.static.Create(self,'BlueFusionReactors',vectm(0,0,0)); + bt.Tag='node1'; + bt.bDestroyOthers=False; + bt = class'BingoTrigger'.static.Create(self,'BlueFusionReactors',vectm(0,0,0)); + bt.Tag='node2'; + bt.bDestroyOthers=False; + bt = class'BingoTrigger'.static.Create(self,'BlueFusionReactors',vectm(0,0,0)); + bt.Tag='node3'; + bt.bDestroyOthers=False; + bt = class'BingoTrigger'.static.Create(self,'BlueFusionReactors',vectm(0,0,0)); + bt.Tag='node4'; + bt.bDestroyOthers=False; - j = j $",\"" $ augId $ "\":" $ augInfo; + bt = class'BingoTrigger'.static.Create(self,'A51UCBlocked',vectm(0,0,0)); + bt.Tag='UC_shutdoor1'; + bt.bDestroyOthers=False; + bt = class'BingoTrigger'.static.Create(self,'A51UCBlocked',vectm(0,0,0)); + bt.Tag='UC_shutdoor2'; + bt.bDestroyOthers=False; + bt = class'BingoTrigger'.static.Create(self,'A51UCBlocked',vectm(0,0,0)); + bt.Tag='UC_shutdoor3'; + bt.bDestroyOthers=False; + bt = class'BingoTrigger'.static.Create(self,'EnterUC',vectm(5743,-9272,-5578),40,40); + bt.bDestroyOthers=False; + bt = class'BingoTrigger'.static.Create(self,'EnterUC',vectm(8628,-7267,-5970),40,40); + bt.bDestroyOthers=False; + bt = class'BingoTrigger'.static.Create(self,'EnterUC',vectm(7235,-8823,-5134),40,40); + bt.bDestroyOthers=False; - anAug = anAug.next; + break; } - } -static function BingoEventData(DXRando dxr, out string j) -{ - local PlayerDataItem data; - local string event, desc; - local int x, y, progress, max; - local class js; - js = class'Json'; - - data = class'PlayerDataItem'.static.GiveItem(dxr.player); - js.static.Add(j, "NumberOfBingos", data.NumberOfBingos()); +function name GetKnicksTag() { + local #var(prefix)FlagTrigger ft; - for(x=0; x<5; x++) { - for(y=0; y<5; y++) { - data.GetBingoSpot(x, y, event, desc, progress, max); - j = j $ ",\"bingo-"$x$"-"$y $ "\":" - $ "{\"event\":\"" $ event $ "\",\"desc\":\"" $ desc $ "\",\"progress\":" $ progress $ ",\"max\":" $ max $ "}"; + foreach AllActors(class'#var(prefix)FlagTrigger',ft) { + if (ft.Event=='MadeBasketM' || ft.Event=='MadeBasketF') { + if (dxr.flagbase.GetBool('LDDPJCIsFemale')) { + return 'MadeBasketF'; + } else { + return 'MadeBasketM'; + } } } + return 'MadeBasket'; } -static function GameTimeEventData(DXRando dxr, out string j) -{ - local int time, realtime, time_without_menus, i, t; - local DXRStats stats; - local class js; - js = class'Json'; - - stats = DXRStats(dxr.FindModule(class'DXRStats')); - if(stats == None) return; - - for (i=1;i<=15;i++) { - t = stats.GetMissionTime(i); - js.static.Add(j, "mission-" $ i $ "-time", t); - time += t; - t = stats.GetCompleteMissionTime(i); - js.static.Add(j, "mission-" $ i $ "-realtime", t); - realtime += t; - time_without_menus += t; - t = stats.GetCompleteMissionMenuTime(i); - js.static.Add(j, "mission-" $ i $ "-menutime", t); - realtime += t; +function CheckPaul() { + if( dxr.flagbase.GetBool('PaulDenton_Dead') ) { + if( ! dxr.flagbase.GetBool('DXREvents_PaulDead')) + PaulDied(dxr); + } else if( ! #defined(vanilla)) { + SavedPaul(dxr, dxr.player); } - js.static.Add(j, "time", time); - js.static.Add(j, "timewithoutmenus", time_without_menus); - js.static.Add(j, "realtime", realtime); -} - -static function string GetLoadoutName(DXRando dxr) -{ - local DXRLoadouts loadout; - loadout = DXRLoadouts(dxr.FindModule(class'DXRLoadouts')); - if( loadout == None ) - return ""; - return loadout.GetName(loadout.loadout); } -// BINGO STUFF -simulated function PlayerAnyEntry(#var(PlayerPawn) player) +simulated function bool WatchGuntherKillSwitch() { - local PlayerDataItem data; - local string event, desc; - local int progress, max; - - data = class'PlayerDataItem'.static.GiveItem(player); - - //Update the exported bingo info in case this was a reload - data.ExportBingoState(); - - // don't overwrite existing bingo - data.GetBingoSpot(0, 0, event, desc, progress, max); - if( event != "" ) { - //Make sure bingo didn't get completed just before leaving a level - CheckBingoWin(dxr,data.NumberOfBingos()); - } else { - SetGlobalSeed("bingo"$dxr.flags.bingoBoardRoll); - _CreateBingoBoard(data); - } -} + local GuntherHermann gunther; -simulated function CreateBingoBoard() -{ - local PlayerDataItem data; - dxr.flags.bingoBoardRoll++; - dxr.flags.SaveFlags(); - SetGlobalSeed("bingo"$dxr.flags.bingoBoardRoll); - data = class'PlayerDataItem'.static.GiveItem(player()); - _CreateBingoBoard(data); + foreach AllActors(class'GuntherHermann',gunther){ + if (gunther.GetStateName()=='KillswitchActivated'){ + return True; + } + }; + return False; } //If there are any situational changes (Eg. Male/Female), adjust the description here @@ -1957,152 +1330,6 @@ simulated function string tweakBingoDescription(string event, string desc) } } -simulated function _CreateBingoBoard(PlayerDataItem data) -{ - local int x, y, i; - local string event, desc; - local int progress, max, missions, starting_mission_mask, starting_mission, end_mission_mask, end_mission, masked_missions; - local int options[ArrayCount(bingo_options)], num_options, slot, free_spaces; - local float f; - - starting_mission = class'DXRStartMap'.static.GetStartMapMission(dxr.flags.settings.starting_map); - starting_mission_mask = class'DXRStartMap'.static.GetStartingMissionMask(dxr.flags.settings.starting_map); - if (dxr.flags.bingo_duration!=0){ - end_mission = starting_mission+dxr.flags.bingo_duration-1; //The same mission is the first mission - - //Missions 7 and 13 don't exist, so don't count them - if (starting_mission<7 && end_mission>=7){ - end_mission+=1; - } - if (starting_mission<13 && end_mission>=13){ - end_mission+=1; - } - } else { - end_mission = 15; - } - end_mission_mask = class'DXRStartMap'.static.GetEndMissionMask(end_mission); - - num_options = 0; - for(x=0; x= 0 ) { - num_options--; - options[slot] = options[num_options]; - } - } - - l("_CreateBingoBoard have " $ num_options $ " options remaining after mutual exclusions"); - - //Clear out the board so it is ready to be repopulated - for(x=0; x<5; x++) { - for(y=0; y<5; y++) { - data.SetBingoSpot(x, y, "", "", 0, 0, 0); - } - } - - free_spaces = dxr.flags.settings.bingo_freespaces; - free_spaces = self.Max(free_spaces, (25+3) - num_options);// +3 to ensure some variety of goal selection - free_spaces = Min(free_spaces, 5); // max of 5 free spaces? - - //Prepopulate the board with free spaces - switch(free_spaces) { - case 5:// all fall through - data.SetBingoSpot(1, 4, "Free Space", "Free Space", 1, 1, 0);// column - case 4: - data.SetBingoSpot(4, 1, "Free Space", "Free Space", 1, 1, 0);// row - case 3: - data.SetBingoSpot(3, 0, "Free Space", "Free Space", 1, 1, 0);// column - case 2: - data.SetBingoSpot(0, 3, "Free Space", "Free Space", 1, 1, 0);// row - case 1: - data.SetBingoSpot(2, 2, "Free Space", "Free Space", 1, 1, 0);// center - case 0: - break; - } - - for(x=0; x<5; x++) { - for(y=0; y<5; y++) { - data.GetBingoSpot(x,y,event,desc,progress,max); - if(max > 0) { //Skip spaces that are already filled with something - continue; - } - - slot = rng(num_options); - i = options[slot]; - event = bingo_options[i].event; - desc = bingo_options[i].desc; - desc = tweakBingoDescription(event,desc); - missions = bingo_options[i].missions; - masked_missions = missions & end_mission_mask; //Pre-mask the bingo endpoint - max = bingo_options[i].max; - // dynamic scaling based on starting mission (not current mission due to leaderboard exploits) - if(max > 1 && InStr(desc, "%s") != -1) { - f = float(dxr.flags.bingo_scale)/100.0; - f = rngrange(f, 0.8, 1);// 80% to 100% - f *= MissionsMaskAvailability(starting_mission, masked_missions) ** 1.5; - max = Ceil(float(max) * f); - max = self.Max(max, 1); - desc = sprintf(desc, max); - } - - num_options--; - options[slot] = options[num_options]; - data.SetBingoSpot(x, y, event, desc, 0, max, missions); - } - } - - // TODO: we could handle bingo_freespaces>1 by randomly putting free spaces on the board, but this probably won't be a desired feature - data.ExportBingoState(); -} - -simulated function int HandleMutualExclusion(MutualExclusion m, int options[ArrayCount(bingo_options)], int num_options) { - local int a, b, overwrite; - - for(a=0; a= num_options ) return -1; - - for(b=0; b= num_options ) return -1; - - if(rngb()) { - return a; - } else { - return b; - } -} - -function bool CheckBingoWin(DXRando dxr, int numBingos) -{ - //Block this in HX for now - if(#defined(hx)) return false; - - if (dxr.flags.settings.bingo_win > 0){ - if (numBingos >= dxr.flags.settings.bingo_win && dxr.LocalURL!="ENDGAME4"){ - info("Number of bingos: "$numBingos$" has exceeded the bingo win threshold! "$dxr.flags.settings.bingo_win); - bingo_win_countdown = 5; - BingoWinScreen(); - return true; - } - } - return false; -} - function ReadText(name textTag) { local string eventname; @@ -2164,6 +1391,17 @@ function ReadText(name textTag) eventname="CloneCubes"; break; + case '01_Book01': + case '01_Book02': + case '01_Book03': + case '01_Book04': + case '01_Book05': + case '01_Book06': + case '01_Book07': + case '01_Book08': + eventname="UNATCOHandbook"; + break; + case '06_Datacube05':// Maggie Chow's bday eventname = "July 18th"; // don't break, fallthrough default: @@ -2393,6 +1631,7 @@ function string RemapBingoEvent(string eventname) case "Mole_Dead": case "JordanShea_Dead": case "Doctor1_Dead": + case "Doctor2_Dead": case "Veteran_Dead": case "jughead_Dead": case "drugdealer_Dead": @@ -2415,6 +1654,17 @@ function string RemapBingoEvent(string eventname) case "Kristi_Dead": case "HotelBartender_Dead": case "MetroTechnician_Dead": + case "lemerchant_Dead": + case "DXRNPCs1_Dead": + case "MarketWaiter_Dead": + case "Sally_Dead": + case "Pimp_Dead": + case "Bum_M_Dead": + case "Renault_Dead": + case "Louis_Dead": + case "Defoe_Dead": + case "Cassandra_Dead": + case "ClubBouncer_Dead": _MarkBingo("DestroyCapitalism"); //Split into another event, but still return this one as-is return eventname; case "MeetWalton_Played": @@ -2428,6 +1678,15 @@ function string RemapBingoEvent(string eventname) case "ScientistMale_ClassDead": case "ScientistFemale_ClassDead": return "ScienceIsForNerds"; + case "ShipNamePlate_B_peepedtex": + case "ShipNamePlate_C_peepedtex": + case "ShipNamePlate_D_peepedtex": + return "ShipNamePlate"; + case "UNATCOHQ_BulletinBoard_Cork_peepedtex": + return "un_bboard_peepedtex"; + case "SheaKnowsAboutDowd": + case "GreenKnowsAboutDowd": + return "SnitchDowd"; default: return eventname; } @@ -2435,61 +1694,6 @@ function string RemapBingoEvent(string eventname) } -function _MarkBingo(coerce string eventname) -{ - local int previousbingos, nowbingos, time; - local PlayerDataItem data; - local string j; - local class js; - js = class'Json'; - - // combine some events - eventname=RemapBingoEvent(eventname); - - //Remapping can also block an event from being marked - if (eventname==""){ - return; - } - - data = class'PlayerDataItem'.static.GiveItem(player()); - previousbingos = data.NumberOfBingos(); - l(self$"._MarkBingo("$eventname$") data: "$data$", previousbingos: "$previousbingos); - - if( ! data.IncrementBingoProgress(eventname)) return; - - nowbingos = data.NumberOfBingos(); - l(self$"._MarkBingo("$eventname$") previousbingos: "$previousbingos$", nowbingos: "$nowbingos); - - if( nowbingos > previousbingos ) { - time = class'DXRStats'.static.GetTotalTime(dxr); - player().ClientMessage("That's a bingo! Game time: " $ class'DXRStats'.static.fmtTimeToString(time),, true); - - j = js.static.Start("Bingo"); - js.static.Add(j, "newevent", eventname); - js.static.Add(j, "location", vectclean(player().Location)); - GeneralEventData(dxr, j); - BingoEventData(dxr, j); - GameTimeEventData(dxr, j); - js.static.End(j); - - class'DXRTelemetry'.static.SendEvent(dxr, player(), j); - - CheckBingoWin(dxr,nowbingos); - } else { - player().ClientMessage("Completed bingo goal: " $ data.GetBingoDescription(eventname)); - } -} - -static function MarkBingo(DXRando dxr, coerce string eventname) -{ - local DXREvents e; - e = DXREvents(dxr.FindModule(class'DXREvents')); - log(e$".MarkBingo "$dxr$", "$eventname); - if(e != None) { - e._MarkBingo(eventname); - } -} - static simulated function string GetBingoGoalHelpText(string event,int mission) { local string msg; @@ -2525,7 +1729,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) } else if (mission<=4){ msg=msg$"She can be found somewhere in New York after sending the NSF signal"; } else if (mission<=5){ - msg=msg$"She can be found at the exit to UNATCO HQ"; + msg=msg$"She can be found at the exit of UNATCO HQ"; } case "WarehouseEntered": return "Enter the underground warehouse in Paris. This warehouse is located in the building across the street from the entrance to the Catacombs."; @@ -2540,7 +1744,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "Chad_Dead": return "Kill Chad Dumier. He can be found in the Silhouette hideout in the Paris catacombs"; case "paris_hostage_Dead": - return "Kill both of the hostages in the Paris catacombs. They can be found locked in the centre of the catacombs bunker occupied by MJ12."; + return "Let both of the hostages in the Paris catacombs die (whether you do it yourself or not). They can be found locked in the centre of the catacombs bunker occupied by MJ12."; case "Hela_Dead": return "Kill Hela, the woman in black leading the MJ12 force in the Paris catacombs"; case "Renault_Dead": @@ -2582,7 +1786,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "M02BillyDone": return "Give Billy some soy food or a candy bar. Billy is a kid located in the kiosk of Castle Clinton."; case "FordSchickRescued": - return "Rescue Ford Schick from the MJ12 lab in the sewers under New York, on your first visit to Hell's Kitchen. The key to the sewers can be gotten from Smuggler"; + return "Rescue Ford Schick from the MJ12 lab in the sewers under New York on your first visit to Hell's Kitchen. The key to the sewers can be gotten from Smuggler"; case "NiceTerrorist_Dead": return "Kill a friendly NSF trooper in the LaGuardia hangar."; case "M10EnteredBakery": @@ -2590,7 +1794,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "FreshWaterOpened": return "Fix the fresh water supply in Brooklyn Bridge Station. The water valves are behind some collapsed rubble."; case "assassinapartment": - return "Visit the apartment in Paris that has Starr the dog inside. This apartment is over top of the media store, but is accessed from the opposite side of the building."; + return "Visit the apartment in Paris that has Starr the dog inside. This apartment is over top of the media store, but is accessed from the opposite side of the building near where Jock picks you up."; case "GaveRentonGun": return "Give Gilbert Renton a gun when he is trying to protect his daughter from JoJo Fine, before the ambush."; case "DXREvents_LeftOnBoat": @@ -2610,7 +1814,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "MeetAI4_Played": return "Talk to Morpheus, the prototype AI locked away in Everett's house."; case "DL_Flooded_Played": - return "Swim outside of the Ocean Lab and enter the flooded section through the hole blasted in the underside of the structure."; + return "Swim outside of the Ocean Lab on the ocean floor and enter the flooded section through the hole blasted in the underside of the structure. There is a flickering light above the hole you need to enter."; case "JockSecondStory": return "Buy two beers from Jordan Shea and give them to Jock in the Underworld bar."; case "M07ChenSecondGive_Played": @@ -2620,7 +1824,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "StantonAmbushDefeated": return "Defend Stanton Dowd from the MJ12 ambush after talking to him."; case "SmugglerDied": - return "Let Smuggler die by not warning him of the UNATCO raid."; + return "Let Smuggler die by not warning him of the UNATCO raid. This can be done either by not talking to him at all, or not warning him of the raid if you talk to him after talking to Dowd."; case "GaveDowdAmbrosia": return "Find a vial of ambrosia on the upper decks of the superfreighter and bring to to Stanton Dowd in the graveyard."; case "JockBlewUp": @@ -2634,7 +1838,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "BoughtClinicPlan": return "On your first visit to Hell's Kitchen, go to the free clinic and buy the full treatment plan from the doctors."; case "ExtinguishFire": - return "When you're on fire, put yourself out by using a shower, sink, toilet, or urinal."; + return "Put yourself out by using a shower, sink, toilet, or urinal while on fire. You can light yourself on fire with WP Rockets or by jumping onto a burning barrel."; case "SubwayHostagesSaved": return "Ensure both hostages in the Battery Park subway station escape on the train."; case "HotelHostagesSaved": @@ -2676,13 +1880,13 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) if (mission<=1){ msg=msg$"There is a ships wheel on the wall of the hut Harley Filben is in."; }else if (mission<=6){ - msg=msg$"There is a ships wheel on the smugglers ship in the Wan Chai canals, as well as on the wall of the Boat Persons house (off the side of the canal)"; + msg=msg$"There is a ships wheel on the smuggler's ship in the Wan Chai canals, as well as on the wall of the Boat Persons house (off the side of the canal)"; }else if (mission<=9){ msg=msg$"There is a ships wheel on the bridge of the Superfreighter."; } return msg; case "ActivateVandenbergBots": - return "Activate both military bots in Vandenberg. The two generator keypads must be activated before you can enter the building."; + return "Activate both military bots in Vandenberg. The two generator keypads must be activated before you can enter the building that the milbots are inside."; case "TongsHotTub": return "Jump into the tub of water in Tracer Tong's hideout."; case "JocksToilet": @@ -2706,7 +1910,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "VandenbergToilet": return "Use the one toilet in Vandenberg. It is located inside the Comm building outside."; case "BoatEngineRoom": - return "Enter the small room at the back of the smugglers boat in the Hong Kong canals. The room can be accessed by using one of the hanging lanterns near the back of the boat."; + return "Enter the small room at the back of the smuggler's boat in the Hong Kong canals. The room can be accessed by using one of the hanging lanterns near the back of the boat."; case "SecurityBot2_ClassDead": return "Destroy enough of the two legged walking security bots. You must destroy them yourself and disabling them with EMP does not count."; case "SecurityBotSmall_ClassDead": @@ -2916,7 +2120,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "botorders2": return "Use the security computer in the upper floor of the MJ12 Robot Maintenance facility to alter the AI of the security bots."; case "BathroomFlags": - return "Place a flag in Manderley's bathroom enough times. This can only be done once per visit."; + return "Place a flag in Manderley's bathroom enough times. This can only be done once per visit. I'm sure this is how you get to the secret ending!"; case "SiloSlide": return "When entering the missile silo, open the vent in the floor and go down the slide that drops you into the water underneath the missile."; case "SiloWaterTower": @@ -2928,7 +2132,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "coolant_switch": return "Flush the reactor coolant in the coolant area on the bottom floor of Sector 4 of Area 51."; case "BlueFusionReactors": - return "Deactivate all four of the blue fusion reactors in Sector 4 of Area 51. Alex will give you three of the four digits of the code and you have to guess the last one."; + return "Deactivate blue fusion reactors in Sector 4 of Area 51. Alex will give you three of the four digits of the code and you have to guess the last one."; case "A51UCBlocked": return "Close the doors to enough of the UCs in Sector 4 of Area 51."; case "VandenbergReactorRoom": @@ -2940,9 +2144,9 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "Cremation": return "Kill (or knock out) a chef in Paris, then throw his body either into a fireplace or onto a stovetop."; case "OceanLabGreenBeacon": - return "Swim to the green beacon on top of the Ocean Lab crew module. The green beacon can be seen out the window of the sub bay of the lab itself."; + return "Swim to the green beacon on top of the Ocean Lab crew module. The green beacon can be seen out the window of the sub bay on the ocean floor."; case "PageTaunt_Played": - return "After recovering the schematics for the Universal Constructor at the bottom of the Ocean Lab, talk to Bob Page on the communicator on the way out."; + return "After recovering the schematics for the Universal Constructor below the Ocean Lab, talk to Bob Page on the communicator before leaving."; case "JerryTheVentGreasel_Dead": return "Kill the greasel in the vents over the main hall of the MJ12 Lab in Hong Kong. His name is Jerry and he is a good boy."; case "BiggestFan": @@ -3028,7 +2232,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "MJ12Commando_peeptime": return "Watch MJ12 Commandos through binoculars for enough time. Note that this will only count in full second increments, so you need to keep the crosshairs centered!"; case "PawnState_Dancing": - return "Watch someone dance through a pair of binoculars."; + return "Watch someone dance through a pair of binoculars. There should be someone vibing in a bar or club."; case "BirdWatching": return "Watch birds through binoculars for enough time. Note that this will only count in full second increments, so you need to keep the crosshairs centered!"; case "NYEagleStatue_peeped": @@ -3046,7 +2250,13 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "ViewSchematics": return "Find and view enough schematics. These include the schematic of the Universal Contructor and the schematic of the blue fusion reactors."; case "ViewMaps": - return "Find and view enough maps of different areas."; + msg = "Find and view enough maps of different areas."; + + if (mission<=1){ + msg = msg $ "|n|nPaul has a map of Liberty Island available for you before you find the terrorist commander."; + } + + return msg; case "ViewDissection": return "Find and view enough images of dissections. This includes the images of a greasel and a gray being dissected."; case "ViewTouristPics": @@ -3072,7 +2282,7 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "dumbwaiter": return "Use the DuClare dumbwaiter between the kitchen and Beth's room."; case "secretdoor01": - return "Twist the flickering light in the Cathedral and open the secret door."; + return "Twist the pulsating light in the Cathedral and open the secret door."; case "CathedralLibrary": return "Enter the library in the Cathedral."; case "DuClareKeys": @@ -3092,11 +2302,11 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "Shannon_Dead": return "Kill Shannon in UNATCO HQ as retribution for her thieving ways."; case "DestroyCapitalism": - msg = "Kill enough people willing to sell you goods in exchange for money. "; + msg = "Kill enough people willing to sell you goods in exchange for money.|nThe Merchant may be elusive, but he must be eliminated when spotted.|n|n"; if (mission<=1){ msg=msg$"Tech Sergeant Kaplan and the woman in the hut on the North Dock both absolutely deserve it."; } else if (mission<=2){ - msg=msg$"Jordan Shea in the bar and the doctor in the Free Clinic both deserve it."; + msg=msg$"Jordan Shea and Sally in the bar, the doctors in the Free Clinic, and the pimp in the alleys deserve it."; } else if (mission<=3){ msg=msg$"There is a veteran in Battery Park, El Rey and Rock in Brooklyn Bridge Station, and Harold in the hangar. They all deserve it."; } else if (mission<=4){ @@ -3104,19 +2314,23 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) } else if (mission<=5){ msg=msg$"Sven the mechanic and Shannon both deserve it."; } else if (mission<=6){ - //msg=msg$"Hong Kong is overflowing with capitalist pigs. "; - msg=msg$"In the VersaLife offices, you can eliminate Mr. Hundley. "; - msg=msg$"In the canals, you must end the life of the Old China Hand bartender, the man selling maps there, and the smuggler on the boat. "; - msg=msg$"In the Lucky Money, you must eliminate the bartender, the mamasan selling escorts, and the doorgirl."; + msg=msg$"Hong Kong is overflowing with capitalist pigs:|n"; + msg=msg$" - The tea house waiter in the market needs to go.|n"; + msg=msg$" - In the VersaLife offices, you can eliminate Mr. Hundley.|n"; + msg=msg$" - In the canals, you must end the life of the Old China Hand bartender, the man selling maps there, and the smuggler on the boat.|n"; + msg=msg$" - In the Lucky Money, you must eliminate the bartender, the bouncer, the mamasan selling escorts, and the doorgirl."; } else if (mission<=8){ msg=msg$"Jordan Shea needs to go."; } else if (mission<=10){ - //msg=msg$"Paris is filled with filthy capitalists. "; - msg=msg$"In the catacombs, the man in Vault 2 needs to go. "; - msg=msg$"In the Champs D'Elysees streets, you must end the hostel bartender and Kristi in the cafe. "; - msg=msg$"In the club, you can annihilate Camille the dancer, Jean the male bartender, Michelle the female bartender, Antoine the biocell seller, and Jocques the worker in the back room. "; + msg=msg$"Paris is filled with filthy capitalists:|n"; + msg=msg$" - Before the catacombs, you must eliminate Le Merchant and Defoe the arms dealer.|n"; + msg=msg$" - In the catacombs, the man in Vault 2 needs to go.|n"; + msg=msg$" - In the Champs D'Elysees streets, you must end the hostel bartender, Renault the drug dealer, and Kristi in the cafe.|n"; + msg=msg$" - In the club, you can annihilate Camille the dancer, Jean the male bartender, Michelle the female bartender, Antoine the biocell seller, Louis the doorman, Cassandra the woman offering to sell information, and Jocques the worker in the back room. "; } else if (mission<=11){ msg=msg$"The technician in the metro station needs to be stopped."; + } else if (mission<=12){ + msg=msg$"The bum living at the Vandenberg gas station deserves it."; } return msg; case "Canal_Cop_Dead": @@ -3124,11 +2338,53 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) case "LightVandalism": return "Destroy enough lamps throughout the game. This might be chandeliers, desk lamps, hanging lights, pool table lights, standing lamps, or table lamps"; case "FightSkeletons": - return "Destroy enough femurs or skulls. Don't let the skeletons rise up!"; + msg = "Destroy enough femurs or skulls. Don't let the skeletons rise up! "; + if (mission<=4){ + msg=msg$"A skull can be found in the NSF HQ."; + } else if (mission<=6){ + msg=msg$"A skull can be found in the Hong Kong VersaLife level 1 labs, as well as in Tracer Tong's hideout and in the Wan Chai Market."; + } else if (mission<=10){ + msg=msg$"The Paris catacombs are just completely loaded with skulls and femurs."; + } else if (mission<=11){ + msg=msg$"A skull can be found underwater at the Cathedral."; + } else if (mission<=14){ + msg=msg$"Several skulls and femurs can be found in the OceanLab on the ocean floor."; + } + return msg; case "TrophyHunter": - return "Destroy enough trophies."; + msg = "Destroy enough trophies. "; + if (mission<=1){ + msg=msg$"Multiple trophies can be found in UNATCO HQ (in the offices and above ground)."; + } else if (mission<=3){ + msg=msg$"Multiple trophies can be found in UNATCO HQ (in the offices and above ground). Several can also be found in the LaGuardia Helibase."; + } else if (mission<=5){ //Mission 4 and 5 both only have trophies at HQ + msg=msg$"Multiple trophies can be found in UNATCO HQ (in the offices and above ground)."; + } else if (mission<=6){ + msg=msg$"There are many trophies in Hong Kong. One can be found in the Helibase, another one around the canals, and one on Tonnochi Road"; + } else if (mission<=10){ + msg=msg$"There is a trophy in Chateau DuClare."; + } + return msg; case "SlippingHazard": - return "Destroy enough 'Wet Floor' signs, leaving the area unmarked and dangerous."; + msg = "Destroy enough 'Wet Floor' signs, leaving the area unmarked and dangerous."; + if (mission<=1){ + msg = msg$" There are signs in UNATCO HQ."; + } else if (mission<=2){ + msg = msg$" There is a sign in the hotel."; + } else if (mission<=3){ + msg = msg$" There are signs in UNATCO HQ."; + } else if (mission<=4){ + msg = msg$" There are signs in UNATCO HQ, and another one in the hotel."; + } else if (mission<=5){ + msg = msg$" There are signs in UNATCO HQ."; + } else if (mission<=6){ + msg = msg$" There is a sign in the MJ12 Helibase and on Tonnochi road."; + } else if (mission<=8){ + msg = msg$" There is a sign in the hotel."; + } else if (mission<=9){ + msg = msg$" There are signs on the lower decks of the superfreighter."; + } + return msg; case "Dehydrated": return "Destroy enough water coolers or water fountains."; case "PresentForManderley": @@ -3177,110 +2433,146 @@ static simulated function string GetBingoGoalHelpText(string event,int mission) return "Scientists think they're so much smarter than you. Show them how smart your weapons are and kill enough of those nerds."; case "Chef_ClassDead": return "Do what needs to be done and kill a chef."; + case "un_PrezMeadPic_peepedtex": + return "Look closely at a picture of President Mead using a pair of binoculars. This can be found in UNATCO HQ (both above and below ground)."; + case "un_bboard_peepedtex": + return "Look at the bulletin board in the UNATCO HQ break room through a pair of binoculars."; + case "DrtyPriceSign_A_peepedtex": + return "Check the gas prices through a pair of binoculars at the abandoned Vandenberg Gas Station."; + case "GS_MedKit_01_peepedtex": + return "Use a pair of binoculars to find a representation of the Red Cross (A red cross on a white background) in the Vandenberg Gas Station. Improper use of the emblem is a violation of the Geneva Conventions."; + case "WatchKeys_cabinet": + return "Find the key that opens the filing cabinets in the back of the greasel lab in the MJ12 base underneath UNATCO. This is typically held by whoever is sitting at the desk in the back part of that lab."; + case "MiguelLeaving": + return "Tell Miguel that he can slip out on his own. He definitely can't, but he doesn't know that."; + case "KarkianDoorsBingo": + return "Open the doors to the karkian cage near the surgery ward in the MJ12 base underneath UNATCO."; + case "SuspensionCrate": + msg = "Open enough suspension crates. These are the square containers with force fields sealing them.|n|n"; + if (mission<=3){ + msg = msg $ "There is a suspension crate on Lebedev's plane."; + } else if (mission<=5){ + msg = msg $ "There is a suspension crate in the back of the greasel lab at the MJ12 base under UNATCO."; + } else if (mission<=10){ + msg = msg $ "There is a suspension crate in the basement of Chateau DuClare."; + } else if (mission<=11){ + msg = msg $ "There are two suspension crates in Everett's lab."; + } + return msg; + case "ScubaDiver_ClassDead": + return "Kill enough SCUBA divers in and around the Ocean Lab."; + case "ShipRamp": + return "Raise the ramp to get on board the superfreighter."; + case "SuperfreighterProp": + return "Dive to the propeller at the back of the superfreighter."; + case "ShipNamePlate": + return "Use binoculars to check the name marked on the side of the superfreighter"; + case "DL_SecondDoors_Played": + return "You need to open them.|n|nTry to leave the Ocean Lab while the sub-bay doors are closed."; + case "WhyContainIt": + return "Destroy a barrel of the gray death virus. Barrels can be found around the Vandenberg command building, in the Sub Base, and around the Universal Constructor under the Ocean Lab."; + case "MailModels": + return "Destroy enough mailboxes. They can be found in the streets of New York."; + case "UNATCOHandbook": + return "Find and read enough UNATCO Handbooks scattered around HQ."; + case "02_Book06": + return "Read a guide to basic firearm safety. Smuggler likes to keep a copy of this lying around somewhere."; + case "15_Email02": + return "Read an email discussing the true origin of the Grays. This can be found on a computer in Sector 3 of Area 51."; + case "ManderleyMail": + return "Check Manderley's holomail messages enough times on different visits."; + case "LetMeIn": + return "Try to enter the door below the UNATCO Medical office without authorization."; + case "08_Bulletin02": + return "Read your wanted poster on a public news terminal when returning to New York."; + case "SnitchDowd": + return "Ask Joe Greene or Jordan Shea about Stanton Dowd."; + case "SewerSurfin": + return "Throw Joe Greene's body into the water in the New York sewers, like the rat he is."; + case "SmokingKills": + return "Destroy enough cigarette vending machines. Smoking kills!"; + case "PhoneCall": + msg = "Make phone calls on enough different phones (Either desk phones or pay phones)."; + if (mission<=1){ + msg=msg$"|n|nThere is a desk phone on Janice's desk in UNATCO HQ."; + } else if (mission <=2){ + msg=msg$"|n|nThere is a desk phone and a pay phone in the Free Clinic. There are two payphones in the streets. There is a payphone in the back of the bar."; + } else if (mission <=3){ + msg=msg$"|n|nThere is a desk phone on Janice's desk in UNATCO HQ. There are two desk phones in offices in the LaGuardia Helibase."; + } else if (mission <=4){ + msg=msg$"|n|nThere is a desk phone on Janice's desk in UNATCO HQ. There are two payphones in the streets. There is a payphone in the back of the bar."; + } else if (mission <=5){ + msg=msg$"|n|nThere is a desk phone on Janice's desk in UNATCO HQ."; + } else if (mission <=6){ + msg=msg$"|n|nThere is a desk phone in the Luminous Path Compound in the Wan Chai Market."; + msg=msg$"|nThere is a desk phone at the front desk of Queen's Tower on Tonnochi Road."; + msg=msg$"|nThere is a desk phone on the conference table in the Lucky Money."; + msg=msg$"|nThere is a desk phone in the conference room on the first level of the MJ12 Lab under VersaLife."; + } else if (mission <=8){ + msg=msg$"|n|nThere is a desk phone and a pay phone in the Free Clinic. There are two payphones in the streets. There is a payphone in the back of the bar."; + } else if (mission <=9){ + msg=msg$"|n|nThere is a desk phone in an office in the dockyard."; + } else if (mission<=10){ + msg=msg$"|n|nThere is a desk phone in the office across the street from the entrance to the catacombs in Denfert-Rochereau."; + } + return msg; + case "Area51ElevatorPower": + return "Enter the main blast doors of the Area 51 bunker and turn on the power to the elevator."; + case "Area51SleepingPod": + return "Open enough of the sleeping pods in the entrance to the Area 51 bunker."; + case "Area51SteamValve": + return "Close the steam valves in the maintenance tunnels under the floors of the entrance to the Area 51 bunker."; + case "DockyardLaser": + return "Deactivate enough of the laser grids in the sewers underneath the dockyards"; + case "A51CommBuildingBasement": + return "Go into the hatch in the Command 24 building in Area 51 and enter the basement."; + case "FreighterHelipad": + return "Walk up onto the helipad in the lower decks of the superfreighter."; + case "11_Bulletin01": + return "Read about the cathedral on a public computer. These can be found on the streets near the metro, as well as inside the metro."; + case "A51ExplosiveLocker": + return "Enter the explosives locker in Area 51. This is the locked room on the staircase leading down from Helios towards Sector 4."; + case "A51SeparationSwim": + return "Go swimming in the tall cylindrical separation tank in Sector 3 of Area 51."; + case "09_Email08": + return "Read an email from Captain Zhao's daughter on his computer on the superfreighter."; + case "Titanic": + return "Stand on the rail at the front of the superfreighter and hold your arms out... It feels like you're flying!"; + case "MeetScaredSoldier_Played": + return "Talk to Xander, the sole surviving soldier hiding out in the building inside the hangar in Area 51."; + case "DockyardTrailer": + return "Enter one of the trailers parked in the dockyards. There is a key to open the trailers somewhere in the dockyards."; + case "CathedralDisplayCase": + return "Enter the store display case in the street leading up to the cathedral."; + case "WIB_ClassDeadM11": + return "Kill Adept 34501, the Woman in Black living in the cathedral."; + case "VandenbergAntenna": + return "Shoot the tip of the antenna on top of the command center at the Vandenberg Air Force Base."; + case "VandenbergHazLab": + return "Enter the Hazard Lab in Vandenberg and disable the electricity that is making the water hazardous."; + case "WatchKeys_maintenancekey": + return "Find the maintenance key in the tunnels underneath Vandenberg."; + case "EnterUC": + return "Step into enough Universal Constructors throughout the game. There are five available:|n - One in the computer section of Vandenberg|n - One in the bottom of the Ocean Lab|n - Three in the very bottom of Area 51"; + case "VandenbergComputerElec": + return "Disable enough electrical panels in the computer room of Vandenberg. There's very little risk!"; + case "VandenbergGasSwim": + return "Go swimming in the water around the base of the two gas tanks outside of the Vandenberg command center."; + case "SiloAttic": + return "Enter the attic in the building outside the fence at the silo."; + case "SubBaseSatellite": + return "Shoot one of the satellite dishes on the tower on top of the sub base on shore in California."; + case "UCVentilation": + return "Destroy enough ventilation fans near the Universal Contructor under the Ocean Lab."; + case "OceanLabFloodedStoreRoom": + return "Swim along the ocean floor to the locked and flooded storage room from in the Ocean Lab"; + case "OceanLabMedBay": + return "Enter the med bay in the Ocean Lab. This room is flooded and off the side of the Karkian Lab."; default: return "Unable to find help text for event '"$event$"'|nReport this to the developers!"; } } -function AddBingoScreen(CreditsWindow cw) -{ - local CreditsBingoWindow cbw; - cbw = CreditsBingoWindow(cw.winScroll.NewChild(Class'CreditsBingoWindow')); - cbw.FillBingoWindow(player()); -} - -function AddDXRCredits(CreditsWindow cw) -{ - cw.PrintLn(); - cw.PrintHeader("Bingo"); - AddBingoScreen(cw); - cw.PrintLn(); -} - -static function int BingoActiveMission(int currentMission, int missionsMask) -{ - local int missionAnded, minMission; - if(missionsMask == 0) return 1;// 1==maybe - missionAnded = (1 << currentMission) & missionsMask; - if(missionAnded != 0) return 2;// 2==true - minMission = currentMission; - -#ifdef backtracking - // check conjoined backtracking missions - switch(currentMission) { - case 10: - currentMission=11; - break; - case 11: - currentMission=10; - minMission=10; - break; - case 12: - currentMission=14; - break; - case 14: - currentMission=12; - minMission=12; - break; - } - missionAnded = (1 << currentMission) & missionsMask; - if(missionAnded != 0) return 2;// 2==true -#endif - - if(missionsMask < (1<=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 ArrayCount(watchflags)) + err("WatchFlag num_watchflags > ArrayCount(watchflags)"); +} + +//Only actually add the flag to the list if it isn't already set +function RewatchFlag(name flag, optional bool disallow_immediate){ + if (!dxr.flagbase.GetBool(flag)) { + WatchFlag(flag,disallow_immediate); + // rewatchflags will get checked in AnyEntry so they can be removed + rewatchflags[num_rewatchflags++] = flag; + } else { + l("RewatchFlag "$flag$" is already set!"); + } +} + +function ReportMissingFlag(name flag, string eventname) { + if( ! dxr.flagbase.GetBool(flag) ) { + SendFlagEvent(eventname, true); + } +} + +function Ending_FirstEntry() +{ + local int ending; + + ending = 0; + + switch(dxr.localURL) + { + //Make sure we actually are only running on the endgame levels + //Just in case we hit a custom level with mission 99 or something + case "ENDGAME1": //Tong + ending = 1; + break; + case "ENDGAME2": //Helios + ending = 2; + break; + case "ENDGAME3": //Everett + ending = 3; + break; + case "ENDGAME4": //Dance party + ending = 4; + break; + default: + //In case rando runs some player level or something with mission 99 + break; + } + + if (ending!=0){ + //Notify of game completion with correct ending number + BeatGame(dxr,ending); + } +} + +simulated function AnyEntry() +{ + local int r, w; + Super.AnyEntry(); + SetTimer(1, true); + + for(w=0; w className) +{ + local Actor a; + + foreach AllActors(className,a){ + return True; + }; + return False; +} + +simulated function int PoolBallsSunk() +{ + local #var(prefix)Poolball cue,ball; + local int ballsSunk,tablesSunk,freshSink,radius; + + radius=99999; + + //I think Airfield is the only map with more than one, + //Need to make sure we only count the balls for each individual table. + //Those pool tables don't have anywhere for the balls to fall out, so + //radius can be contained. Other tables have a hole for the balls to + //come out, so we want to be able to check a larger radius. + if (NumPoolTables>1){ + radius = 150; + } + + tablesSunk=0; + foreach AllActors(class'#var(prefix)Poolball',cue){ + if (cue.SkinColor==SC_Cue){ + ballsSunk=0; + foreach cue.RadiusActors(class'#var(prefix)Poolball',ball,radius){ + if (ball.Location.Z <= PoolBallHeight){ + ballsSunk++; + } + } + if (ballsSunk>=BallsPerTable){ + tablesSunk++; + } + } + } + + if (tablesSunk <= PoolTablesSunk){ + return 0; + } + freshSink = tablesSunk-PoolTablesSunk; + PoolTablesSunk = tablesSunk; + + return freshSink; +} + +simulated function InitPoolBalls() +{ + local #var(prefix)Poolball ball; + PoolBallHeight = 9999; + NumPoolTables=0; + PoolTablesSunk=0; + + foreach AllActors(class'#var(prefix)Poolball',ball){ + if (ball.Location.Z < PoolBallHeight){ + PoolBallHeight = ball.Location.Z; + } + if (ball.SkinColor==SC_Cue){ + NumPoolTables+=1; + } + } + + BallsPerTable = 16; //Default number - Lucky Money has less + + PoolBallHeight -= 1; +} + +simulated function bool CheckForNanoKey(String keyID) +{ + + local name keyName; + + if (player()==None){ + return False; + } + + if (player().KeyRing==None){ + return False; + } + + keyName = StringToName(keyID); + + return player().KeyRing.HasKey(keyName); +} + +simulated function Timer() +{ + local int i,j,num; + local bool FlagTriggered; + + if( dxr == None || dxr.flagbase == None ) { + return; + } + + CheckWatchedActors(); + + for(i=0; i0){ + for (j=0;j= NumPoolTables){ + num_watchflags--; + watchflags[i] = watchflags[num_watchflags]; + watchflags[num_watchflags]=''; + i--; + } + continue; + } + } else if( watchflags[i] == 'FlowersForTheLab' ) { + if (ClassInLevel(class'#var(prefix)Flowers')){ + FlagTriggered=True; + } + } else if (InStr(watchflags[i],"WatchKeys_")!=-1) { + if (CheckForNanoKey(Mid(watchflags[i],10))){ + FlagTriggered=True; + } + } + + if( FlagTriggered || dxr.flagbase.GetBool(watchflags[i]) ) { + SendFlagEvent(watchflags[i]); + num_watchflags--; + watchflags[i] = watchflags[num_watchflags]; + watchflags[num_watchflags]=''; + i--; + } + } + // for nonvanilla, because GameInfo.Died is called before the player's Dying state calls root.ClearWindowStack(); + if(died) { + class'DXRHints'.static.AddDeath(dxr, player()); + died = false; + } + + if (bingo_win_countdown>=0){ + HandleBingoWinCountdown(); + } +} + +function PreTravel() +{ + Super.PreTravel(); + SetTimer(0, false); +} + +function BingoWinScreen() +{ + local #var(PlayerPawn) p; + local bool showMsg; + + p = player(); + if ( Level.Netmode == NM_Standalone ) { + //Make it harder to get murdered during the countdown + Level.Game.SetGameSpeed(0.1); + SetTimer(0.1, true); + } + p.ReducedDamageType = 'All';// god mode + p.DesiredFlashScale = 0; + p.DesiredFlashFog = vect(0,0,0); + + showMsg=False; + if (InGame()){ + showMsg=True; + } else { + //Consider it in-game if a big message is up... + if (DXRBigMessage(DeusExRootWindow(p.rootWindow).GetTopWindow()) != None){ + showMsg = True; + } + } + + if(showMsg) { + p.ShowHud(False); + //Show win message + class'DXRBigMessage'.static.CreateBigMessage(dxr.player,None,"Congratulations! You finished your bingo!","Game ending in "$bingo_win_countdown$" seconds"); + } + if (bingo_win_countdown == 2 && !#defined(vanilla)) { + //Give it 2 seconds to send the tweet + //This is still needed outside of vanilla + BeatGame(dxr,4); + } +} + +function HandleBingoWinCountdown() +{ + //Blocked in HX for now (Blocked at the check, but here for safety as well) + if(#defined(hx)) return; + + if (bingo_win_countdown > 0) { + BingoWinScreen(); + bingo_win_countdown--; + } else if (bingo_win_countdown == 0) { + if ( Level.Netmode == NM_Standalone ) { + Level.Game.SetGameSpeed(1); + SetTimer(1, true); + } + //Go to bingo win ending + Level.Game.SendPlayer(dxr.player,"99_EndGame4"); + } +} + +function bool SpecialTriggerHandling(Actor Other, Pawn Instigator) +{ + local #var(prefix)MapExit m; + if (tag == 'Boat_Exit'){ + dxr.flagbase.SetBool('DXREvents_LeftOnBoat', true,, 999); + + foreach AllActors(class'#var(prefix)MapExit',m,'Boat_Exit2'){ + m.Trigger(Other,Instigator); + } + return true; + } + + return false; +} + +function Trigger(Actor Other, Pawn Instigator) +{ + local string j; + local class js; + local name useTag; + + js = class'Json'; + + //Leave this variable for now, in case we need to massage tags + //again at some point in the future + useTag = tag; + + Super.Trigger(Other, Instigator); + l("Trigger("$Other$", "$instigator$")"); + + if (!SpecialTriggerHandling(Other,Instigator)){ + j = js.static.Start("Trigger"); + js.static.Add(j, "instigator", GetActorName(Instigator)); + js.static.Add(j, "tag", useTag); + js.static.add(j, "other", GetActorName(other)); + GeneralEventData(dxr, j); + js.static.End(j); + + class'DXRTelemetry'.static.SendEvent(dxr, Instigator, j); + _MarkBingo(useTag); + } +} + +function SendFlagEvent(coerce string eventname, optional bool immediate, optional string extra) +{ + local string j; + local class js; + js = class'Json'; + + l("SendFlagEvent " $ eventname @ immediate @ extra); + + if(eventname ~= "M02HostagesRescued") {// for the hotel, set by Mission02.uc + M02HotelHostagesRescued(); + return; + } + else if(eventname ~= "MS_DL_Played") {// this is a generic flag name used in a few of the mission scripts + if(dxr.localURL ~= "02_NYC_BATTERYPARK") { + BatteryParkHostages(); + } + return; + } + + j = js.static.Start("Flag"); + js.static.Add(j, "flag", eventname); + js.static.Add(j, "immediate", immediate); + js.static.Add(j, "location", vectclean(dxr.player.location)); + if(extra != "") + js.static.Add(j, "extra", extra); + GeneralEventData(dxr, j); + js.static.End(j); + + class'DXRTelemetry'.static.SendEvent(dxr, dxr.player, j); + _MarkBingo(eventname); +} + +function M02HotelHostagesRescued() +{ + local bool MaleHostage_Dead, FemaleHostage_Dead, GilbertRenton_Dead; + MaleHostage_Dead = dxr.flagbase.GetBool('MaleHostage_Dead'); + FemaleHostage_Dead = dxr.flagbase.GetBool('FemaleHostage_Dead'); + GilbertRenton_Dead = dxr.flagbase.GetBool('GilbertRenton_Dead'); + if( !MaleHostage_Dead && !FemaleHostage_Dead && !GilbertRenton_Dead) { + SendFlagEvent("HotelHostagesSaved"); + } +} + +function BatteryParkHostages() +{ + local bool SubTerroristsDead, EscapeSuccessful, SubHostageMale_Dead, SubHostageFemale_Dead; + SubTerroristsDead = dxr.flagbase.GetBool('SubTerroristsDead'); + EscapeSuccessful = dxr.flagbase.GetBool('EscapeSuccessful'); + SubHostageMale_Dead = dxr.flagbase.GetBool('SubHostageMale_Dead'); + SubHostageFemale_Dead = dxr.flagbase.GetBool('SubHostageFemale_Dead'); + + l("BatteryParkHostages() " $ SubTerroristsDead @ EscapeSuccessful @ SubHostageMale_Dead @ SubHostageFemale_Dead); + if( (SubTerroristsDead || EscapeSuccessful) && !SubHostageMale_Dead && !SubHostageFemale_Dead ) { + SendFlagEvent("SubwayHostagesSaved"); + } +} + +static function _DeathEvent(DXRando dxr, Actor victim, Actor Killer, coerce string damageType, vector HitLocation, string type) +{ + local string j; + local class js; + local bool unconcious; + js = class'Json'; + + j = js.static.Start(type); + js.static.Add(j, "victim", GetActorName(victim)); + js.static.Add(j, "victimBindName", victim.BindName); + js.static.Add(j, "victimRandomizedName", GetRandomizedName(victim)); + if(#var(prefix)ScriptedPawn(victim) != None) { + unconcious = #var(prefix)ScriptedPawn(victim).bStunned; + js.static.Add(j, "victimUnconcious", unconcious); + } + + if(Killer != None) { + js.static.Add(j, "killerclass", Killer.Class.Name); + js.static.Add(j, "killer", GetActorName(Killer)); + js.static.Add(j, "killerRandomizedName", GetRandomizedName(Killer)); + } + js.static.Add(j, "dmgtype", damageType); + GeneralEventData(dxr, j); + js.static.Add(j, "location", dxr.flags.vectclean(victim.Location)); + js.static.End(j); + class'DXRTelemetry'.static.SendEvent(dxr, victim, j); +} + +static function string GetRandomizedName(Actor a) +{ + local ScriptedPawn sp; + sp = ScriptedPawn(a); + if(sp == None || sp.bImportant) return ""; + return sp.FamiliarName; +} + +static function AddPlayerDeath(DXRando dxr, #var(PlayerPawn) player, optional Actor Killer, optional coerce string damageType, optional vector HitLocation) +{ + local DXREvents ev; + class'DXRStats'.static.AddDeath(player); + + if(#defined(injections)) + class'DXRHints'.static.AddDeath(dxr, player); + else { + // for nonvanilla, because GameInfo.Died is called before the player's Dying state calls root.ClearWindowStack(); + ev = DXREvents(dxr.FindModule(class'DXREvents')); + if(ev != None) + ev.died = true; + } + + if(Killer == None) { + if(player.myProjKiller != None) + Killer = player.myProjKiller; + if(player.myTurretKiller != None) + Killer = player.myTurretKiller; + if(player.myPoisoner != None) + Killer = player.myPoisoner; + if(player.myBurner != None) + Killer = player.myBurner; + // myKiller is only set in multiplayer + if(player.myKiller != None) + Killer = player.myKiller; + } + + if(damageType == "shot") { + if( !IsHuman(Killer.class) && Robot(Killer) == None ) { + // only humans and robots can shoot? karkians deal shot damage + damageType = ""; + } + } + + _DeathEvent(dxr, player, Killer, damageType, HitLocation, "DEATH"); +} + +static function AddPawnDeath(ScriptedPawn victim, optional Actor Killer, optional coerce string damageType, optional vector HitLocation) +{ + local DXRando dxr; + local DXREvents e; + foreach victim.AllActors(class'DXRando', dxr) break; + + if(dxr != None) + e = DXREvents(dxr.FindModule(class'DXREvents')); + log(e$".AddPawnDeath "$dxr$", "$victim); + if(e != None) + e._AddPawnDeath(victim, Killer, damageType, HitLocation); +} + +function bool checkInitialAlliance(ScriptedPawn p,name allianceName, float allianceLevel) +{ + local int i; + + for (i=0;i<8;i++){ + if (p.InitialAlliances[i].AllianceName==allianceName && + p.InitialAlliances[i].AllianceLevel~=allianceLevel){ + return True; + } + } + return False; +} + +function bool isInitialPlayerAlly(ScriptedPawn p) +{ + return checkInitialAlliance(p,'Player',1.0); +} + +function bool isInitialPlayerEnemy(ScriptedPawn p) +{ + return checkInitialAlliance(p,'Player',-1.0); +} + +function _AddPawnDeath(ScriptedPawn victim, optional Actor Killer, optional coerce string damageType, optional vector HitLocation) +{ + local string classname; + + _MarkBingo(victim.BindName$"_Dead"); + _MarkBingo(victim.BindName$"_DeadM" $ dxr.dxInfo.missionNumber); + if( Killer == None || #var(PlayerPawn)(Killer) != None ) { + classname = string(victim.class.name); + if(#defined(hx) && InStr(classname, "HX")==0) { + classname = Mid(classname, 2); + } + + if (IsHuman(victim.class) && ((damageType == "Stunned") || + (damageType == "KnockedOut") || + (damageType == "Poison") || + (damageType == "PoisonEffect"))){ + _MarkBingo(classname$"_ClassUnconscious"); + _MarkBingo(classname$"_ClassUnconsciousM" $ dxr.dxInfo.missionNumber); + } else { + _MarkBingo(classname$"_ClassDead"); + _MarkBingo(classname$"_ClassDeadM" $ dxr.dxInfo.missionNumber); + + //Were they an ally? Skip on NSF HQ, because that's kind of a bait + if (!isInitialPlayerEnemy(victim) && !IsCritter(victim) && //Must have not been an enemy initially + (dxr.localURL!="04_NYC_NSFHQ" || (dxr.localURL=="04_NYC_NSFHQ" && dxr.flagbase.GetBool('DL_SimonsPissed_Played')==False)) //Not on the NSF HQ map, or if it is, before you send the signal (kludgy) + ){ + _MarkBingo("AlliesKilled"); + } + + } + if (damageType=="stomped" && IsHuman(victim.class)){ //If you stomp a human to death... + _MarkBingo("HumanStompDeath"); + } + } + + if(!victim.bImportant) + return; + + if(victim.BindName == "PaulDenton") + dxr.flagbase.SetBool('DXREvents_PaulDead', true,, 999); + else if(victim.BindName == "AnnaNavarre" && dxr.flagbase.GetBool('annadies')) { + _MarkBingo("AnnaKillswitch"); + Killer = player(); + } + + _DeathEvent(dxr, victim, Killer, damageType, HitLocation, "PawnDeath"); +} + +static function AddDeath(Pawn victim, optional Actor Killer, optional coerce string damageType, optional vector HitLocation) +{ + local DXRando dxr; + local #var(PlayerPawn) player; + local #var(prefix)ScriptedPawn sp; + player = #var(PlayerPawn)(victim); + sp = #var(prefix)ScriptedPawn(victim); + if(player != None) { + foreach victim.AllActors(class'DXRando', dxr) break; + AddPlayerDeath(dxr, player, Killer, damageType, HitLocation); + } + else if(sp != None) + AddPawnDeath(sp, Killer, damageType, HitLocation); +} + +static function PaulDied(DXRando dxr) +{ + local string j; + local class js; + js = class'Json'; + + j = js.static.Start("PawnDeath"); + js.static.Add(j, "victim", "Paul Denton"); + js.static.Add(j, "victimBindName", "PaulDenton"); + js.static.Add(j, "dmgtype", ""); + GeneralEventData(dxr, j); + js.static.Add(j, "location", dxr.flags.vectclean(dxr.player.location)); + js.static.End(j); + dxr.flagbase.SetBool('DXREvents_PaulDead', true,, 999); + class'DXRTelemetry'.static.SendEvent(dxr, dxr.player, j); + MarkBingo(dxr, "PaulDenton_Dead"); +} + +static function SavedPaul(DXRando dxr, #var(PlayerPawn) player, optional int health) +{ + local string j; + local class js; + js = class'Json'; + + j = js.static.Start("SavedPaul"); + if(health > 0) + js.static.Add(j, "PaulHealth", health); + GeneralEventData(dxr, j); + js.static.End(j); + + class'DXRTelemetry'.static.SendEvent(dxr, dxr.player, j); + MarkBingo(dxr, "SavedPaul"); +} + +static function BeatGame(DXRando dxr, int ending) +{ + local PlayerDataItem data; + local DXRStats stats; + local string j; + local class js; + js = class'Json'; + + stats = DXRStats(dxr.FindModule(class'DXRStats')); + + j = js.static.Start("BeatGame"); + js.static.Add(j, "ending", ending); + js.static.Add(j, "SaveCount", dxr.player.saveCount); + js.static.Add(j, "Autosaves", stats.GetDataStorageStat(dxr, "DXRStats_autosaves")); + js.static.Add(j, "deaths", stats.GetDataStorageStat(dxr, "DXRStats_deaths")); + js.static.Add(j, "LoadCount", stats.GetDataStorageStat(dxr, "DXRStats_loads")); + js.static.Add(j, "maxrando", dxr.flags.maxrando); + js.static.Add(j, "bSetSeed", dxr.flags.bSetSeed); + data = class'PlayerDataItem'.static.GiveItem(dxr.player); + js.static.Add(j, "initial_version", data.initial_version); + js.static.Add(j, "combat_difficulty", dxr.player.CombatDifficulty); + js.static.Add(j, "rando_difficulty", dxr.flags.difficulty); + js.static.Add(j, "cheats", dxr.player.FlagBase.GetInt('DXRStats_cheats')); + + if (dxr.player.carriedDecoration!=None){ + js.static.Add(j, "carriedItem", dxr.player.carriedDecoration.Class); + } + else if(dxr.player.inHand.IsA('POVCorpse')){ + js.static.Add(j, "carriedItem", POVCorpse(dxr.player.inHand).carcClassString); + } + + GeneralEventData(dxr, j); + BingoEventData(dxr, j); + AugmentationData(dxr, j); + GameTimeEventData(dxr, j); + + js.static.Add(j, "score", stats.ScoreRun()); + js.static.End(j); + + class'DXRTelemetry'.static.SendEvent(dxr, dxr.player, j); +} + +static function ExtinguishFire(DXRando dxr, string extinguisher, DeusExPlayer player) +{ + local string j; + local class js; + js = class'Json'; + + j = js.static.Start("ExtinguishFire"); + js.static.Add(j, "extinguisher", extinguisher); + js.static.Add(j, "location", dxr.flags.vectclean(player.Location)); + GeneralEventData(dxr, j); + js.static.End(j); + + class'DXRTelemetry'.static.SendEvent(dxr, player, j); + MarkBingo(dxr, "ExtinguishFire"); +} + +static function GeneralEventData(DXRando dxr, out string j) +{ + local string loadout,lang; + local class js; + js = class'Json'; + + js.static.Add(j, "PlayerName", GetActorName(dxr.player)); + js.static.Add(j, "map", dxr.localURL); + js.static.Add(j, "mapname", dxr.dxInfo.MissionLocation); + js.static.Add(j, "mission", dxr.dxInfo.missionNumber); + js.static.Add(j, "TrueNorth", dxr.dxInfo.TrueNorth); + js.static.Add(j, "PlayerIsFemale", dxr.flagbase.GetBool('LDDPJCIsFemale')); + js.static.Add(j, "GameMode", dxr.flags.GameModeName(dxr.flags.gamemode)); + js.static.Add(j, "newgameplus_loops", dxr.flags.newgameplus_loops); + + loadout = GetLoadoutName(dxr); + if(loadout != "") + js.static.Add(j, "loadout", loadout); + + lang = GetConfig("Engine.Engine", "Language"); + if(lang != "") + js.static.Add(j, "language", lang); +} + +static function AugmentationData(DXRando dxr, out string j) +{ + local Augmentation anAug; + local string augId,augName,augInfo; + local int level; + + anAug = dxr.player.AugmentationSystem.FirstAug; + while(anAug != None) + { + if (anAug.HotKeyNum <= 0){ //I think if you uninstall an aug it becomes -1? + anAug = anAug.next; + continue; + } + augId = "Aug-"$anAug.HotKeyNum; + augName = ""$anAug.Class.Name; + level = anAug.CurrentLevel; + if (anAug.bBoosted){ + level = level-1; + } + + augInfo = "{\"name\":\"" $ augName $"\",\"level\":"$level$"}"; + + j = j $",\"" $ augId $ "\":" $ augInfo; + + + anAug = anAug.next; + } + +} + +static function BingoEventData(DXRando dxr, out string j) +{ + local PlayerDataItem data; + local string event, desc; + local int x, y, progress, max; + local class js; + js = class'Json'; + + data = class'PlayerDataItem'.static.GiveItem(dxr.player); + js.static.Add(j, "NumberOfBingos", data.NumberOfBingos()); + + for(x=0; x<5; x++) { + for(y=0; y<5; y++) { + data.GetBingoSpot(x, y, event, desc, progress, max); + j = j $ ",\"bingo-"$x$"-"$y $ "\":" + $ "{\"event\":\"" $ event $ "\",\"desc\":\"" $ desc $ "\",\"progress\":" $ progress $ ",\"max\":" $ max $ "}"; + } + } +} + +static function GameTimeEventData(DXRando dxr, out string j) +{ + local int time, realtime, time_without_menus, i, t; + local DXRStats stats; + local class js; + js = class'Json'; + + stats = DXRStats(dxr.FindModule(class'DXRStats')); + if(stats == None) return; + + for (i=1;i<=15;i++) { + t = stats.GetMissionTime(i); + js.static.Add(j, "mission-" $ i $ "-time", t); + time += t; + t = stats.GetCompleteMissionTime(i); + js.static.Add(j, "mission-" $ i $ "-realtime", t); + realtime += t; + time_without_menus += t; + t = stats.GetCompleteMissionMenuTime(i); + js.static.Add(j, "mission-" $ i $ "-menutime", t); + realtime += t; + } + js.static.Add(j, "time", time); + js.static.Add(j, "timewithoutmenus", time_without_menus); + js.static.Add(j, "realtime", realtime); +} + +static function string GetLoadoutName(DXRando dxr) +{ + local DXRLoadouts loadout; + loadout = DXRLoadouts(dxr.FindModule(class'DXRLoadouts')); + if( loadout == None ) + return ""; + return loadout.GetName(loadout.loadout); +} + +// BINGO STUFF +simulated function PlayerAnyEntry(#var(PlayerPawn) player) +{ + local PlayerDataItem data; + local string event, desc; + local int progress, max; + + data = class'PlayerDataItem'.static.GiveItem(player); + + //Update the exported bingo info in case this was a reload + data.ExportBingoState(); + + // don't overwrite existing bingo + data.GetBingoSpot(0, 0, event, desc, progress, max); + if( event != "" ) { + //Make sure bingo didn't get completed just before leaving a level + CheckBingoWin(dxr,data.NumberOfBingos()); + } else { + SetGlobalSeed("bingo"$dxr.flags.bingoBoardRoll); + _CreateBingoBoard(data); + } +} + +simulated function CreateBingoBoard() +{ + local PlayerDataItem data; + dxr.flags.bingoBoardRoll++; + dxr.flags.SaveFlags(); + SetGlobalSeed("bingo"$dxr.flags.bingoBoardRoll); + data = class'PlayerDataItem'.static.GiveItem(player()); + _CreateBingoBoard(data); +} + +simulated function _CreateBingoBoard(PlayerDataItem data) +{ + local int x, y, i; + local string event, desc; + local int progress, max, missions, starting_mission_mask, starting_mission, end_mission_mask, end_mission, maybe_mission_mask, masked_missions; + local int options[ArrayCount(bingo_options)], num_options, slot, free_spaces; + local float f; + + starting_mission = class'DXRStartMap'.static.GetStartMapMission(dxr.flags.settings.starting_map); + starting_mission_mask = class'DXRStartMap'.static.GetStartingMissionMask(dxr.flags.settings.starting_map); + maybe_mission_mask = class'DXRStartMap'.static.GetMaybeMissionMask(dxr.flags.settings.starting_map); + if (dxr.flags.bingo_duration!=0){ + end_mission = starting_mission+dxr.flags.bingo_duration-1; //The same mission is the first mission + + //Missions 7 and 13 don't exist, so don't count them + if (starting_mission<7 && end_mission>=7){ + end_mission+=1; + } + if (starting_mission<13 && end_mission>=13){ + end_mission+=1; + } + } else { + end_mission = 15; + } + end_mission_mask = class'DXRStartMap'.static.GetEndMissionMask(end_mission); + + num_options = 0; + for(x=0; x= 0 ) { + num_options--; + options[slot] = options[num_options]; + } + } + + l("_CreateBingoBoard have " $ num_options $ " options remaining after mutual exclusions"); + + //Clear out the board so it is ready to be repopulated + for(x=0; x<5; x++) { + for(y=0; y<5; y++) { + data.SetBingoSpot(x, y, "", "", 0, 0, 0); + } + } + + free_spaces = dxr.flags.settings.bingo_freespaces; + free_spaces = self.Max(free_spaces, (25+3) - num_options);// +3 to ensure some variety of goal selection + free_spaces = Min(free_spaces, 5); // max of 5 free spaces? + + //Prepopulate the board with free spaces + switch(free_spaces) { + case 5:// all fall through + data.SetBingoSpot(1, 4, "Free Space", "Free Space", 1, 1, 0);// column + case 4: + data.SetBingoSpot(4, 1, "Free Space", "Free Space", 1, 1, 0);// row + case 3: + data.SetBingoSpot(3, 0, "Free Space", "Free Space", 1, 1, 0);// column + case 2: + data.SetBingoSpot(0, 3, "Free Space", "Free Space", 1, 1, 0);// row + case 1: + data.SetBingoSpot(2, 2, "Free Space", "Free Space", 1, 1, 0);// center + case 0: + break; + } + + for(x=0; x<5; x++) { + for(y=0; y<5; y++) { + data.GetBingoSpot(x,y,event,desc,progress,max); + if(max > 0) { //Skip spaces that are already filled with something + continue; + } + + slot = rng(num_options); + i = options[slot]; + event = bingo_options[i].event; + desc = bingo_options[i].desc; + desc = tweakBingoDescription(event,desc); + missions = bingo_options[i].missions; + masked_missions = missions & end_mission_mask; //Pre-mask the bingo endpoint + max = bingo_options[i].max; + // dynamic scaling based on starting mission (not current mission due to leaderboard exploits) + if(max > 1 && InStr(desc, "%s") != -1) { + f = float(dxr.flags.bingo_scale)/100.0; + f = rngrange(f, 0.8, 1);// 80% to 100% + f *= MissionsMaskAvailability(starting_mission, masked_missions) ** 1.5; + max = Ceil(float(max) * f); + max = self.Max(max, 1); + desc = sprintf(desc, max); + } + + num_options--; + options[slot] = options[num_options]; + data.SetBingoSpot(x, y, event, desc, 0, max, missions); + } + } + + // TODO: we could handle bingo_freespaces>1 by randomly putting free spaces on the board, but this probably won't be a desired feature + data.ExportBingoState(); +} + +simulated function int HandleMutualExclusion(MutualExclusion m, int options[ArrayCount(bingo_options)], int num_options) { + local int a, b, overwrite; + + for(a=0; a= num_options ) return -1; + + for(b=0; b= num_options ) return -1; + + if(rngb()) { + return a; + } else { + return b; + } +} + +function bool CheckBingoWin(DXRando dxr, int numBingos) +{ + //Block this in HX for now + if(#defined(hx)) return false; + + if (dxr.flags.settings.bingo_win > 0){ + if (numBingos >= dxr.flags.settings.bingo_win && dxr.LocalURL!="ENDGAME4"){ + info("Number of bingos: "$numBingos$" has exceeded the bingo win threshold! "$dxr.flags.settings.bingo_win); + bingo_win_countdown = 5; + BingoWinScreen(); + return true; + } + } + return false; +} + +function _MarkBingo(coerce string eventname) +{ + local int previousbingos, nowbingos, time; + local PlayerDataItem data; + local string j; + local class js; + js = class'Json'; + + // combine some events + eventname=RemapBingoEvent(eventname); + + //Remapping can also block an event from being marked + if (eventname==""){ + return; + } + + data = class'PlayerDataItem'.static.GiveItem(player()); + previousbingos = data.NumberOfBingos(); + l(self$"._MarkBingo("$eventname$") data: "$data$", previousbingos: "$previousbingos); + + if( ! data.IncrementBingoProgress(eventname)) return; + + nowbingos = data.NumberOfBingos(); + l(self$"._MarkBingo("$eventname$") previousbingos: "$previousbingos$", nowbingos: "$nowbingos); + + if( nowbingos > previousbingos ) { + time = class'DXRStats'.static.GetTotalTime(dxr); + player().ClientMessage("That's a bingo! Game time: " $ class'DXRStats'.static.fmtTimeToString(time),, true); + + j = js.static.Start("Bingo"); + js.static.Add(j, "newevent", eventname); + js.static.Add(j, "location", vectclean(player().Location)); + GeneralEventData(dxr, j); + BingoEventData(dxr, j); + GameTimeEventData(dxr, j); + js.static.End(j); + + class'DXRTelemetry'.static.SendEvent(dxr, player(), j); + + CheckBingoWin(dxr,nowbingos); + } else { + player().ClientMessage("Completed bingo goal: " $ data.GetBingoDescription(eventname)); + } +} + +static function MarkBingo(DXRando dxr, coerce string eventname) +{ + local DXREvents e; + e = DXREvents(dxr.FindModule(class'DXREvents')); + log(e$".MarkBingo "$dxr$", "$eventname); + if(e != None) { + e._MarkBingo(eventname); + } +} + +function AddBingoScreen(CreditsWindow cw) +{ + local CreditsBingoWindow cbw; + cbw = CreditsBingoWindow(cw.winScroll.NewChild(Class'CreditsBingoWindow')); + cbw.FillBingoWindow(player()); +} + +function AddDXRCredits(CreditsWindow cw) +{ + cw.PrintLn(); + cw.PrintHeader("Bingo"); + AddBingoScreen(cw); + cw.PrintLn(); +} + +static function int BingoActiveMission(int currentMission, int missionsMask) +{ + local int missionAnded, minMission; + if(missionsMask == 0) return 1;// 1==maybe + missionAnded = (1 << currentMission) & missionsMask; + if(missionAnded != 0) return 2;// 2==true + minMission = currentMission; + +#ifdef backtracking + // check conjoined backtracking missions + switch(currentMission) { + case 10: + currentMission=11; + break; + case 11: + currentMission=10; + minMission=10; + break; + case 12: + currentMission=14; + break; + case 14: + currentMission=12; + minMission=12; + break; + } + missionAnded = (1 << currentMission) & missionsMask; + if(missionAnded != 0) return 2;// 2==true +#endif + + if(missionsMask < (1<(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; + + i++; + DecorationsOverwrites[i].type = "CigaretteMachine"; + DecorationsOverwrites[i].bInvincible = false; + DecorationsOverwrites[i].HitPoints = 100; + DecorationsOverwrites[i].minDamageThreshold = 0; + c = class(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 type; var float chance; }; +var RandomGrenadeStruct randomgrens[4]; + +var config float min_rate_adjust, max_rate_adjust; + +function class<#var(prefix)ThrownProjectile> PickRandomGrenade() +{ + local class<#var(prefix)ThrownProjectile> newgren; + local float r; + local int i; + + newgren=None; + + r = initchance(); + for (i=0;i gren, float c) +{ + local int i; + for(i=0;i < ArrayCount(randomgrens);i++){ + if (randomgrens[i].type==None){ + randomgrens[i].type=gren; + randomgrens[i].chance=rngrangeseeded(c,min_rate_adjust,max_rate_adjust,gren.name); + return; + } + } +} + +function CheckConfig() +{ + local float total; + local int i; + + Super.CheckConfig(); + + AddRandomGrenade(class'#var(prefix)EMPGrenade',5); //1 through the game + AddRandomGrenade(class'#var(prefix)GasGrenade',10); //9 through the game + AddRandomGrenade(class'#var(prefix)LAM',20); //18 through the game + AddRandomGrenade(class'#var(prefix)NanoVirusGrenade',5); //0 through the game + + //Scale to 100% + total=0; + for(i=0;i type, Vector loc, Rotator rot) +{ + local #var(prefix)ThrownProjectile gren; + + gren = Spawn(type,,,loc,rot); + + gren.PlayAnim('Open'); + gren.SetPhysics(PHYS_None); + gren.bBounce = False; + gren.bProximityTriggered = True; + gren.bStuck = True; + + return gren; +} + +function FirstEntry() +{ + local #var(prefix)ThrownProjectile gren; + local #var(prefix)ThrownProjectile grens[16]; + local Vector loc; + local Rotator rot; + local int i; + + Super.FirstEntry(); + if(dxr.flags.IsZeroRando()) return; + + SetSeed("RandoGrenades"); + + i=0; + foreach AllActors(class'#var(prefix)ThrownProjectile',gren) + { + if (gren.Owner==None && chance_single(dxr.flags.moresettings.grenadeswap)) { + grens[i++]=gren; + } + } + + for (i=0;grens[i]!=None;i++){ + loc = grens[i].Location; + rot = grens[i].Rotation; + grens[i].Destroy(); + + gren = SpawnNewPlantedGrenade(PickRandomGrenade(),loc,rot); + + if (gren!=None){ + l("Spawned a new grenade "$gren.name); + } else { + l("Failed to spawn a replacement grenade"); + } + } +} + +simulated function AddDXRCredits(CreditsWindow cw) +{ + local int i; + local DXREnemies e; + local class w; + if(dxr.flags.IsZeroRando()) return; + cw.PrintHeader( "Grenade Types" ); + for (i=0;i c, Name datacubeTag, string datacubename) a = SpawnNewActor(c, false); if( a == None ) return None; + // spawn with large collision to ensure enough space, then slightly shrink after to make them easier to get around + #var(prefix)Robot(a).SetBasedPawnSize(a.CollisionRadius * 0.8, a.CollisionHeight * 0.7); + d = #var(prefix)Datacube(SpawnNewActor(class'#var(prefix)Datacube', true, a.Location, min_datacube_distance, max_datacube_distance)); if( d == None ) return a; d.TextPackage = "#var(package)"; @@ -507,6 +510,11 @@ function Actor _SpawnNewActor(class c, bool jitter, vector target, float loc = GetRandomPositionFine(target, mindist, maxdist); else loc = GetRandomPosition(target, mindist, maxdist); + + if(!CheckFreeSpace(loc, c.default.CollisionRadius * 2 + player().CollisionRadius, c.default.CollisionHeight * 2)) { + info("_SpawnNewActor no free space for " $ c @ loc); + return None; + } a = Spawn(c,,, loc ); if( ScriptedPawn(a) != None ) class'DXRNames'.static.GiveRandomName(dxr, ScriptedPawn(a) ); @@ -521,7 +529,7 @@ function Actor SpawnNewActor(class c, bool jitter, optional vector target local Actor a; local int i; - for(i=0; i<10; i++) { + for(i=0; i<20; i++) { a = _SpawnNewActor(c, jitter, target, mindist, maxdist); if( a != None ) return a; } diff --git a/DXRModules/DeusEx/Classes/DXRMemes.uc b/DXRModules/DeusEx/Classes/DXRMemes.uc index aa29cebf1..915990048 100644 --- a/DXRModules/DeusEx/Classes/DXRMemes.uc +++ b/DXRModules/DeusEx/Classes/DXRMemes.uc @@ -317,6 +317,7 @@ function PostFirstEntry() { local ScriptedPawn sp; local InterpolationPoint p; + local LuciusDeBeers lucius; local vector v; local rotator r; local bool memes_enabled; @@ -362,6 +363,12 @@ function PostFirstEntry() AddLeo(); break; } + + if(#defined(injections) && !dxr.flags.IsReducedRando()) { + foreach AllActors(class'LuciusDeBeers', lucius) { + lucius.bInvincible = false; + } + } } function AddLeo() diff --git a/DXRModules/DeusEx/Classes/DXRNPCs.uc b/DXRModules/DeusEx/Classes/DXRNPCs.uc index 5ba2cab09..61d4d19cd 100644 --- a/DXRModules/DeusEx/Classes/DXRNPCs.uc +++ b/DXRModules/DeusEx/Classes/DXRNPCs.uc @@ -73,9 +73,12 @@ function RandomizeItems(out ItemPurchase items[8], optional int forced) classes[i++] = class'#var(prefix)Rebreather'; num=i; + if(chance_single(30)) items[forced++].item = class'#var(prefix)Medkit'; + if(chance_single(30)) items[forced++].item = class'#var(prefix)BioelectricCell'; + // randomize cost for forced items for(i=0; i=40; + case "SmugglerDied": + if (end_mission < 9){ + return True; + } + return start_map>=90; default: return False; } @@ -398,6 +418,23 @@ static function bool BingoGoalImpossible(string bingo_event, int start_map, int return False; } + +static function bool BingoGoalPossible(string bingo_event, int start_map, int end_mission) +{ + switch(start_map) { + case 119: + switch(bingo_event) { + case "TobyAtanwe_Dead": + case "MeetAI4_Played": + case "DeBeersDead": + return true; + } + break; + } + + return false; +} + static function int ChooseRandomStartMap(DXRando dxr, int avoidStart) { local int i; @@ -494,6 +531,7 @@ static function AddStartingSkillPoints(DXRando dxr, #var(PlayerPawn) p) { local int startBonus; startBonus = GetStartMapSkillBonus(dxr.flags.settings.starting_map); + log("AddStartingSkillPoints before "$ p.SkillPointsAvail $ ", bonus: "$ startBonus $", after: " $ (p.SkillPointsAvail + startBonus)); p.SkillPointsAvail += startBonus; //Don't add to the total. It isn't used in the base game, but we use it for scoring. //These starting points are free, so don't count them towards your score diff --git a/DXRModules/DeusEx/Classes/DXRStats.uc b/DXRModules/DeusEx/Classes/DXRStats.uc index ce1ec2d29..a691d1f7a 100644 --- a/DXRModules/DeusEx/Classes/DXRStats.uc +++ b/DXRModules/DeusEx/Classes/DXRStats.uc @@ -770,6 +770,7 @@ function TestScoring() function ExtendedTests() { local int time, completeTime, menutime, completemenutime; + local DeusExRootWindow root; Super.ExtendedTests(); @@ -786,7 +787,9 @@ function ExtendedTests() teststring( IntCommas(104000), "104,000", "IntCommas 104,000"); teststring( IntCommas(1000000), "1,000,000", "IntCommas 1,000,000"); + root = DeusExRootWindow(player().rootWindow); // mission 1 tests + root.bUIPaused = false; testint( GetMissionTime(1), 0, "GetMissionTime(1) == 0"); testint( GetCompleteMissionTime(1), 0, "GetCompleteMissionTime(1) == 0"); testint( GetMissionMenuTime(1), 0, "GetMissionMenuTime(1) == 0"); @@ -799,9 +802,9 @@ function ExtendedTests() testint( GetMissionMenuTime(1), 0, "GetMissionMenuTime(1) == 0"); testint( GetCompleteMissionMenuTime(1), 0, "GetCompleteMissionMenuTime(1) == 0"); - DeusExRootWindow(player().rootWindow).hud.Hide(); + root.bUIPaused = true; IncMissionTimer(1); - DeusExRootWindow(player().rootWindow).hud.Show(); + root.bUIPaused = false; testint( GetMissionTime(1), 1, "GetMissionTime(1) == 1"); testint( GetCompleteMissionTime(1), 1, "GetCompleteMissionTime(1) == 1"); @@ -828,9 +831,9 @@ function ExtendedTests() testint( GetMissionMenuTime(1), 0, "GetMissionMenuTime(1) == 0"); testint( GetCompleteMissionMenuTime(1), 1, "GetCompleteMissionMenuTime(1) == 1"); - DeusExRootWindow(player().rootWindow).hud.Hide(); + root.bUIPaused = true; IncMissionTimer(1); - DeusExRootWindow(player().rootWindow).hud.Show(); + root.bUIPaused = false; testint( GetMissionTime(1), 1, "GetMissionTime(1) == 1"); testint( GetCompleteMissionTime(1), 2, "GetCompleteMissionTime(1) == 2"); @@ -860,9 +863,9 @@ function ExtendedTests() testint( GetMissionMenuTime(12), 0, "GetMissionMenuTime(12) == 0"); testint( GetCompleteMissionMenuTime(12), 0, "GetCompleteMissionMenuTime(12) == 0"); - DeusExRootWindow(player().rootWindow).hud.Hide(); + root.bUIPaused = true; Timer(); - DeusExRootWindow(player().rootWindow).hud.Show(); + root.bUIPaused = false; testint( GetMissionTime(12), time + 1, "GetMissionTime(12) == time + 1"); testint( GetCompleteMissionTime(12), completeTime + 1, "GetCompleteMissionTime(12) == completeTime + 1"); diff --git a/DXRModules/DeusEx/Classes/DXRWeaponMods.uc b/DXRModules/DeusEx/Classes/DXRWeaponMods.uc index ed849f749..cd3429409 100644 --- a/DXRModules/DeusEx/Classes/DXRWeaponMods.uc +++ b/DXRModules/DeusEx/Classes/DXRWeaponMods.uc @@ -92,6 +92,7 @@ function FirstEntry() local int i; Super.FirstEntry(); + if(dxr.flags.IsZeroRando()) return; SetSeed("RandoWeaponMods"); diff --git a/DXRModules/DeusEx/Classes/DXRWeapons.uc b/DXRModules/DeusEx/Classes/DXRWeapons.uc index 2a90f9145..1e22e24b0 100644 --- a/DXRModules/DeusEx/Classes/DXRWeapons.uc +++ b/DXRModules/DeusEx/Classes/DXRWeapons.uc @@ -38,7 +38,14 @@ simulated function RandoWeapon(DeusExWeapon w) min_weapon_dmg = float(dxr.flags.settings.min_weapon_dmg) / 100; max_weapon_dmg = float(dxr.flags.settings.max_weapon_dmg) / 100; - new_damage = rngrange(float(w.default.HitDamage), min_weapon_dmg, max_weapon_dmg); + + new_damage = w.default.HitDamage; +#ifdef injections + if(!dxr.flags.IsZeroRando() && WeaponHideAGun(w) != None) { + new_damage = WeaponHideAGun(w).UpgradeToPS40(); + } +#endif + new_damage = rngrange(new_damage, min_weapon_dmg, max_weapon_dmg); w.HitDamage = int(new_damage + 0.5); if(w.HitDamage < 2 && w.HitDamage < w.default.HitDamage) { info(w $ " w.HitDamage ("$ w.HitDamage $") < 2"); diff --git a/DXRVanilla/DeusEx/Classes/DataLinkPlay.uc b/DXRVanilla/DeusEx/Classes/DataLinkPlay.uc index d6840ba50..47ff024d0 100644 --- a/DXRVanilla/DeusEx/Classes/DataLinkPlay.uc +++ b/DXRVanilla/DeusEx/Classes/DataLinkPlay.uc @@ -6,12 +6,14 @@ var DataLinkTrigger dataLinkTriggerQueue[16]; function bool PushDataLink( Conversation queueCon ) { + local bool bZeroRando; // queue up the incoming message if(Super.PushDataLink(queueCon) == false) return false; + bZeroRando = Human(player).bZeroRando; // make our timer shorter - if(speechFastEndTime > Level.Timeseconds + 0.1 && currentEvent != None) + if(!bZeroRando && speechFastEndTime > Level.Timeseconds + 0.1 && currentEvent != None) SetTimer(speechFastEndTime - Level.Timeseconds, false); return true; @@ -131,7 +133,7 @@ Begin: { PlaySpeech( ConEventSpeech(currentEvent).conSpeech.soundID ); - if(HaveQueued() == false) { + if(HaveQueued() == false || Human(player).bZeroRando) { // Add two seconds to the sound since there seems to be a slight lag SetTimer( con.GetSpeechLength(ConEventSpeech(currentEvent).conSpeech.soundID), False ); } else { diff --git a/DXRVanilla/DeusEx/Classes/HealingItem.uc b/DXRVanilla/DeusEx/Classes/HealingItem.uc index fc0226fad..e3ae975d8 100644 --- a/DXRVanilla/DeusEx/Classes/HealingItem.uc +++ b/DXRVanilla/DeusEx/Classes/HealingItem.uc @@ -2,6 +2,7 @@ // HealingItem. //============================================================================= class HealingItem extends DeusExPickup; +// base class for alcohol var int health; var float energy; @@ -12,25 +13,27 @@ function DoHeal(DeusExPlayer player) local int i; local float f; local string message; + local bool balance; if (player == None) return; player.drugEffectTimer += drugEffect; + balance = ! Human(player).bZeroRando; if( health > 0 ) { - i = Human(player)._HealPlayer(health, false, true); + i = Human(player)._HealPlayer(health, false, balance);// balance bool used for heal legs message = "Healed "$ i $" point"; if(i > 1) message = message $ "s"; } f = FMin( energy, player.EnergyMax - player.Energy); - if( f > 0 ) { + if( f > 0 && balance ) { if( Len(message) > 0 ) message = message $ ", recharged "; else message = "Recharged "; - + player.Energy += f; message = message $ int(f) $ " point"; if( int(f) > 1 ) @@ -51,7 +54,7 @@ state Activated function BeginState() { local DeusExPlayer player; - + Super.BeginState(); player = DeusExPlayer(Owner); diff --git a/DXRVanilla/DeusEx/Classes/LuciusDeBeers.uc b/DXRVanilla/DeusEx/Classes/LuciusDeBeers.uc index 5bab8f97a..ee22569c8 100644 --- a/DXRVanilla/DeusEx/Classes/LuciusDeBeers.uc +++ b/DXRVanilla/DeusEx/Classes/LuciusDeBeers.uc @@ -86,5 +86,5 @@ function Destroyed() defaultproperties { - bInvincible=False; + bInvincible=True; } diff --git a/DXRVanilla/DeusEx/Classes/Player.uc b/DXRVanilla/DeusEx/Classes/Player.uc index a7d256093..2e21d7051 100644 --- a/DXRVanilla/DeusEx/Classes/Player.uc +++ b/DXRVanilla/DeusEx/Classes/Player.uc @@ -421,18 +421,30 @@ function bool CanInstantLeftClick(DeusExPickup item) exec function ParseLeftClick() { local DeusExPickup item; + local Actor A; + local int i; + Super.ParseLeftClick(); item = DeusExPickup(FrobTarget); if (item != None && CanInstantLeftClick(item)) { + foreach item.BasedActors(class'Actor', A) + A.SetBase(None); // So that any effects get applied to you item.SetOwner(self); + item.SetBase(self); // add to the player's inventory, so ChargedPickups travel across maps item.BecomeItem(); item.bDisplayableInv = false; item.Inventory = Inventory; Inventory = item; - item.Activate(); + if(FireExtinguisher(item) != None) { + // this was buggy with multiple, but it doesn't make sense and wouldn't be useful to use multiple at once anyways + item.NumCopies = 1; + } + for(i=item.NumCopies; i > 0; i--) { + item.Activate(); + } FrobTarget = None; } } @@ -562,109 +574,84 @@ exec function bool DropItem(optional Inventory inv, optional bool bDrop) RemoveItemFromSlot(item); } - // if we are highlighting something, try to place the object on the target - if ((FrobTarget != None) && !item.IsA('POVCorpse')) - { - item.Velocity = vect(0,0,0); - - // play the correct anim - PlayPickupAnim(FrobTarget.Location); + // throw velocity is based on augmentation + if (AugmentationSystem != None) + { + mult = AugmentationSystem.GetAugLevelValue(class'AugMuscle'); + if (mult == -1.0) + mult = 1.0; + } - // try to drop the object about one foot above the target - size = FrobTarget.CollisionRadius - item.CollisionRadius * 2; - dropVect.X = size/2 - FRand() * size; - dropVect.Y = size/2 - FRand() * size; - dropVect.Z = FrobTarget.CollisionHeight + item.CollisionHeight + 16; - if (FastTrace(dropVect)) - { - item.DropFrom(FrobTarget.Location + dropVect); - } - else - { - ClientMessage(CannotDropHere); - bDropped = False; - } - } - else - { - // throw velocity is based on augmentation - if (AugmentationSystem != None) - { - mult = AugmentationSystem.GetAugLevelValue(class'AugMuscle'); - if (mult == -1.0) - mult = 1.0; - } + if (bDrop) + { + item.Velocity = VRand() * 30; - if (bDrop) - { - item.Velocity = VRand() * 30; + // play the correct anim + PlayPickupAnim(item.Location); + } + else + { + item.Velocity = Vector(ViewRotation) * mult * 300 + vect(0,0,220) + 40 * VRand(); - // play the correct anim - PlayPickupAnim(item.Location); - } - else - { - item.Velocity = Vector(ViewRotation) * mult * 300 + vect(0,0,220) + 40 * VRand(); + // play a throw anim + PlayAnim('Attack',,0.1); + } - // play a throw anim - PlayAnim('Attack',,0.1); - } + GetAxes(ViewRotation, X, Y, Z); + dropVect = Location + 0.8 * CollisionRadius * X; + dropVect.Z += BaseEyeHeight; - GetAxes(ViewRotation, X, Y, Z); - dropVect = Location + 0.8 * CollisionRadius * X; - dropVect.Z += BaseEyeHeight; + // if we are a corpse, spawn the actual carcass + if (item.IsA('POVCorpse')) + { + if (POVCorpse(item).carcClassString != "") + { + carcClass = class(DynamicLoadObject(POVCorpse(item).carcClassString, class'Class')); + if (carcClass != None) + { + carc = Spawn(carcClass); + if (carc != None) + { + carc.Mesh = carc.Mesh2; + carc.KillerAlliance = POVCorpse(item).KillerAlliance; + carc.KillerBindName = POVCorpse(item).KillerBindName; + carc.Alliance = POVCorpse(item).Alliance; + carc.bNotDead = POVCorpse(item).bNotDead; + carc.bEmitCarcass = POVCorpse(item).bEmitCarcass; + carc.CumulativeDamage = POVCorpse(item).CumulativeDamage; + carc.MaxDamage = POVCorpse(item).MaxDamage; + carc.itemName = POVCorpse(item).CorpseItemName; + carc.CarcassName = POVCorpse(item).CarcassName; + carc.Velocity = item.Velocity * 0.5; + item.Velocity = vect(0,0,0); + carc.bHidden = False; + carc.SetPhysics(PHYS_Falling); + carc.SetScaleGlow(); + if (carc.SetLocation(dropVect)) + { + // must circumvent PutInHand() since it won't allow + // things in hand when you're carrying a corpse + SetInHandPending(None); + item.Destroy(); + item = None; + } + else + carc.bHidden = True; + } + } + } + } + else + { + if (FastTrace(dropVect)) + { + item.DropFrom(dropVect); + item.bFixedRotationDir = True; + item.RotationRate.Pitch = (32768 - Rand(65536)) * 4.0; + item.RotationRate.Yaw = (32768 - Rand(65536)) * 4.0; + } + } - // if we are a corpse, spawn the actual carcass - if (item.IsA('POVCorpse')) - { - if (POVCorpse(item).carcClassString != "") - { - carcClass = class(DynamicLoadObject(POVCorpse(item).carcClassString, class'Class')); - if (carcClass != None) - { - carc = Spawn(carcClass); - if (carc != None) - { - carc.Mesh = carc.Mesh2; - carc.KillerAlliance = POVCorpse(item).KillerAlliance; - carc.KillerBindName = POVCorpse(item).KillerBindName; - carc.Alliance = POVCorpse(item).Alliance; - carc.bNotDead = POVCorpse(item).bNotDead; - carc.bEmitCarcass = POVCorpse(item).bEmitCarcass; - carc.CumulativeDamage = POVCorpse(item).CumulativeDamage; - carc.MaxDamage = POVCorpse(item).MaxDamage; - carc.itemName = POVCorpse(item).CorpseItemName; - carc.CarcassName = POVCorpse(item).CarcassName; - carc.Velocity = item.Velocity * 0.5; - item.Velocity = vect(0,0,0); - carc.bHidden = False; - carc.SetPhysics(PHYS_Falling); - carc.SetScaleGlow(); - if (carc.SetLocation(dropVect)) - { - // must circumvent PutInHand() since it won't allow - // things in hand when you're carrying a corpse - SetInHandPending(None); - item.Destroy(); - item = None; - } - else - carc.bHidden = True; - } - } - } - } - else - { - if (FastTrace(dropVect)) - { - item.DropFrom(dropVect); - item.bFixedRotationDir = True; - item.RotationRate.Pitch = (32768 - Rand(65536)) * 4.0; - item.RotationRate.Yaw = (32768 - Rand(65536)) * 4.0; - } - } - } // if we failed to drop it, put it back inHand if (item != None) diff --git a/DXRando/DeusEx/Classes/BarDancer.uc b/DXRando/DeusEx/Classes/BarDancer.uc new file mode 100644 index 000000000..be2e97ecc --- /dev/null +++ b/DXRando/DeusEx/Classes/BarDancer.uc @@ -0,0 +1,21 @@ +class BarDancer extends MichaelHamner; + +defaultproperties +{ + bImportant=False + Mesh=LodMesh'DeusExCharacters.GM_Suit' + MultiSkins(0)=Texture'DeusExCharacters.Skins.MichaelHamnerTex0' + MultiSkins(1)=Texture'DeusExCharacters.Skins.MichaelHamnerTex2' + MultiSkins(2)=Texture'DeusExCharacters.Skins.MichaelHamnerTex0' + MultiSkins(3)=Texture'DeusExCharacters.Skins.MichaelHamnerTex1' + MultiSkins(4)=Texture'DeusExCharacters.Skins.MichaelHamnerTex1' + MultiSkins(5)=Texture'DeusExCharacters.Skins.FramesTex1' + MultiSkins(6)=Texture'DeusExCharacters.Skins.LensesTex2' + MultiSkins(7)=Texture'DeusExItems.Skins.PinkMaskTex' + CollisionRadius=20.000000 + CollisionHeight=47.500000 + BindName="BarDancer" + FamiliarName="Tony" + UnfamiliarName="Dancing Guy" + Orders="Dancing" +} diff --git a/DXRando/DeusEx/Classes/BingoTrigger.uc b/DXRando/DeusEx/Classes/BingoTrigger.uc index 86e65ead5..da5bb33f5 100644 --- a/DXRando/DeusEx/Classes/BingoTrigger.uc +++ b/DXRando/DeusEx/Classes/BingoTrigger.uc @@ -5,12 +5,20 @@ class BingoTrigger expands Trigger; var() String bingoEvent; var() bool bDestroyOthers; +var() bool bUntrigger; function Trigger(Actor Other, Pawn Instigator) { Super.Trigger(Other, Instigator); DoBingoThing(); +} +function Untrigger(Actor Other, Pawn Instigator) +{ + Super.Untrigger(Other, Instigator); + if (bUntrigger){ + DoBingoThing(); + } } function Touch(Actor Other) @@ -99,4 +107,5 @@ defaultproperties bingoEvent="" bTriggerOnceOnly=True bDestroyOthers=True + bUntrigger=False } diff --git a/DXRando/DeusEx/Classes/DXRBinoculars.uc b/DXRando/DeusEx/Classes/DXRBinoculars.uc index fab72a172..44dfa4e3c 100644 --- a/DXRando/DeusEx/Classes/DXRBinoculars.uc +++ b/DXRando/DeusEx/Classes/DXRBinoculars.uc @@ -2,6 +2,7 @@ class DXRBinoculars injects #var(prefix)Binoculars; var int watchTime; var Actor lastWatched; +var name lastWatchedTex; state Activated { @@ -36,8 +37,12 @@ simulated function Timer() local #var(PlayerPawn) peeper; local Vector HitNormal, HitLocation, StartTrace, EndTrace; local Actor peepee;// pronounced peep-ee, not pee-pee + local Actor target; local DXRando dxr; - local bool newPeepee; + local bool newPeepee, newPeepTex; + local name texName,texGroup; + local int flags, i; + peeper = #var(PlayerPawn)(Owner); @@ -49,7 +54,26 @@ simulated function Timer() //which is basically the worst case scenario EndTrace = StartTrace + 20000 * Vector(peeper.ViewRotation); - peepee = Trace(HitLocation, HitNormal, EndTrace, StartTrace, True); + target=None; + + //peepee = Trace(HitLocation, HitNormal, EndTrace, StartTrace, True); + foreach TraceTexture(class'Actor',target,texName,texGroup,flags,HitLocation,HitNormal,EndTrace,StartTrace){ + if (((target.DrawType == DT_None) || target.bHidden) && target!=Level) + { + // Keep tracing past invisible things + } + else if (target==Level && (((flags&0x00000004)!=0) || ((flags&0x00000001)!=0))) + { + //Skip invisible or translucent masked textures + //It won't actually trace beyond the level, it seems, so this doesn't actually help + } + else + { + peepee = target; + break; + } + } + //peeper.ClientMessage("Peeping "$peepee.Name$" in state "$peepee.GetStateName()); @@ -59,26 +83,45 @@ simulated function Timer() return; } - if (peepee!=lastWatched) + if(peepee.IsA('LevelInfo')){ + peepee=None; + } + + if (peepee!=None && peepee!=lastWatched) { lastWatched = peepee; + lastWatchedTex = ''; watchTime=0; if (lastWatched!=None){ newPeepee = True; + newPeepTex = False; } + } else if (peepee==None && texName!=lastWatchedTex) { + lastWatchedTex=texName; + lastWatched=peepee; + watchTime=0; + if (lastWatchedTex!=''){ + newPeepee = False; + newPeepTex = True; + } } else { newPeepee = False; + newPeepTex = False; } if (newPeepee){ + //peeper.ClientMessage("New peeped actor is "$peepee.class.Name); //This should probably only trigger once per thing - TODO, will probably be tracked in DXREvents and PlayerDataItem, like function ReadText(name textTag) class'DXREvents'.static.MarkBingo(dxr,peepee.Class.Name$"_peeped"); if (ScriptedPawn(peepee)!=None){ class'DXREvents'.static.MarkBingo(dxr,"PawnState_"$peepee.GetStateName()); } + } else if (newPeepTex) { + //peeper.ClientMessage("New peeped texture is "$lastWatchedTex); + class'DXREvents'.static.MarkBingo(dxr,lastWatchedTex$"_peepedtex"); } else { watchTime++; if(watchTime>=4){ diff --git a/DXRando/DeusEx/Classes/DXRandoText.uc b/DXRando/DeusEx/Classes/DXRandoText.uc index fc060b7b5..75ecfe776 100644 --- a/DXRando/DeusEx/Classes/DXRandoText.uc +++ b/DXRando/DeusEx/Classes/DXRandoText.uc @@ -10,3 +10,10 @@ class DXRandoText extends object abstract; #exec DEUSEXTEXT IMPORT FILE=Text\MedbotNearby.txt #exec DEUSEXTEXT IMPORT FILE=Text\RepairbotNearby.txt + +#exec DEUSEXTEXT IMPORT FILE=Text\03_EmailMenu_ajacobson.txt +#exec DEUSEXTEXT IMPORT FILE=Text\04_EmailMenu_ajacobson.txt +#exec DEUSEXTEXT IMPORT FILE=Text\05_EmailMenu_ajacobson.txt +#exec DEUSEXTEXT IMPORT FILE=Text\01_Email14.txt +#exec DEUSEXTEXT IMPORT FILE=Text\03_Email09.txt +#exec DEUSEXTEXT IMPORT FILE=Text\05_Email12.txt diff --git a/DXRando/DeusEx/Classes/DynamicBlockMonsters.uc b/DXRando/DeusEx/Classes/DynamicBlockMonsters.uc new file mode 100644 index 000000000..68aaea838 --- /dev/null +++ b/DXRando/DeusEx/Classes/DynamicBlockMonsters.uc @@ -0,0 +1,6 @@ +class DynamicBlockMonsters extends BlockMonsters; + +defaultproperties +{ + bStatic=False +} diff --git a/DXRando/DeusEx/Classes/PayPhone.uc b/DXRando/DeusEx/Classes/PayPhone.uc new file mode 100644 index 000000000..7ecdbeb40 --- /dev/null +++ b/DXRando/DeusEx/Classes/PayPhone.uc @@ -0,0 +1,11 @@ +class PayPhone extends #var(prefix)Phone; + +defaultproperties +{ + Mesh=None + CollisionRadius=6 + CollisionHeight=12 + Physics=PHYS_None + bCollideWorld=False + ItemName="Pay Phone" +} diff --git a/DXRando/DeusEx/Classes/WHPiano.uc b/DXRando/DeusEx/Classes/WHPiano.uc index 24c8c45db..d161fa389 100644 --- a/DXRando/DeusEx/Classes/WHPiano.uc +++ b/DXRando/DeusEx/Classes/WHPiano.uc @@ -61,7 +61,7 @@ simulated function Tick(float deltaTime) soundHandle = 0; } currentSong=JUST_BROKEN_PIANO; - PlayDoneTime = 8.0 + Level.TimeSeconds; + PlayDoneTime = 1.0 + Level.TimeSeconds; soundHandle = PlaySound(sound'MaxPaynePianoJustBroke', SLOT_Misc,5.0,, 500); message = GetSongMessage(sound'MaxPaynePianoJustBroke'); } diff --git a/DXRando/DeusEx/Text/01_Email14.txt b/DXRando/DeusEx/Text/01_Email14.txt new file mode 100644 index 000000000..27b08302c --- /dev/null +++ b/DXRando/DeusEx/Text/01_Email14.txt @@ -0,0 +1,5 @@ +Alex Jacobson inbox + +

From: AJacobson//UNATCO.00013.76490 +

To: AJacobson//UNATCO.00013.76490 +

Subject: locker code: 2001 diff --git a/DXRando/DeusEx/Text/03_Email09.txt b/DXRando/DeusEx/Text/03_Email09.txt new file mode 100644 index 000000000..934dacb0c --- /dev/null +++ b/DXRando/DeusEx/Text/03_Email09.txt @@ -0,0 +1,12 @@ + +

From: RosyCross//UnderNet.44567.22356 +

To: AJacobson//UNATCO.00013.76490 +

Subject: Inquiry +

+

The Oracle says: +

+

We find your question interesting. We have several references to entity token "MJ12" but all are ambiguous. Will be necessary to expend cycles to clarify relationships for codification of representative patterns before further recognition can be accomplished. This will be time consuming, but past working relationships have shown a 83.23% favorable return. Work with file "SH-187" was very instructive. +

+

We will contact you again when information has been collated. +

+

[[The Oracle would like to know your favorite color.]] \ No newline at end of file diff --git a/DXRando/DeusEx/Text/03_EmailMenu_ajacobson.txt b/DXRando/DeusEx/Text/03_EmailMenu_ajacobson.txt new file mode 100644 index 000000000..e494e62e2 --- /dev/null +++ b/DXRando/DeusEx/Text/03_EmailMenu_ajacobson.txt @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/DXRando/DeusEx/Text/04_EmailMenu_ajacobson.txt b/DXRando/DeusEx/Text/04_EmailMenu_ajacobson.txt new file mode 100644 index 000000000..288f15998 --- /dev/null +++ b/DXRando/DeusEx/Text/04_EmailMenu_ajacobson.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/DXRando/DeusEx/Text/05_Email12.txt b/DXRando/DeusEx/Text/05_Email12.txt new file mode 100644 index 000000000..0d3794f04 --- /dev/null +++ b/DXRando/DeusEx/Text/05_Email12.txt @@ -0,0 +1,10 @@ + +

From: RosyCross//UnderNet.44567.22356 +

To: AJacobson//UNATCO.00013.76490 +

Subject: RE: Inquiry +

+

The Oracle says: +

+

Initial analysis of entity token "MJ12" (also reference entity tokens "Majestic", "Majestic 12", "Majestic Council of Twelve", "They Who Rule the World in Majesty", "MJID") yields confusing results that must be further refined through additional pattern analysis. Would appear to be related to entity token "Illuminati" and other self-organizing institutions that seek to affect global change through localized alteration of sociopolitical systems. Extant records dating back 837 years are extensive, but subject to a number of valid curve-fitting functions that render speculation as to possible motive or overall goals only 27.3% accurate. We dislike this ambiguity and will work to clarify prior to final report. +

+

[[The Oracle would like to know what you think of the color blue.]] \ No newline at end of file diff --git a/DXRando/DeusEx/Text/05_EmailMenu_ajacobson.txt b/DXRando/DeusEx/Text/05_EmailMenu_ajacobson.txt new file mode 100644 index 000000000..4ec7a81a1 --- /dev/null +++ b/DXRando/DeusEx/Text/05_EmailMenu_ajacobson.txt @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/DeusEx.u b/DeusEx.u index 48573234e..d22476dfc 100644 Binary files a/DeusEx.u and b/DeusEx.u differ diff --git a/GMDXRandomizer.u b/GMDXRandomizer.u index c3cd25ece..ec8cc3cb3 100644 Binary files a/GMDXRandomizer.u and b/GMDXRandomizer.u differ diff --git a/GUI/DeusEx/Classes/BingoHintMsgBox.uc b/GUI/DeusEx/Classes/BingoHintMsgBox.uc index a1e1d3f2b..d3bcc57f1 100644 --- a/GUI/DeusEx/Classes/BingoHintMsgBox.uc +++ b/GUI/DeusEx/Classes/BingoHintMsgBox.uc @@ -3,19 +3,27 @@ class BingoHintMsgBox extends MenuUIMessageBoxWindow; #exec TEXTURE IMPORT FILE="Textures\BingoHintBoxBackground_1.pcx" NAME="BingoHintBoxBackground_1" GROUP="DXRandoUI" MIPS=Off #exec TEXTURE IMPORT FILE="Textures\BingoHintBoxBackground_2.pcx" NAME="BingoHintBoxBackground_2" GROUP="DXRandoUI" MIPS=Off +var PersonaScrollAreaWindow winScroll; + event InitWindow() { Super.InitWindow(); btnOK = winButtonBar.AddButton(btnLabelOK, HALIGN_Right); + numButtons = 1; SetFocusWindow(btnOK); } function CreateTextWindow() { - winText = CreateMenuHeader(21, 13, "", winClient); - winText.SetTextAlignments(HALIGN_Left, VALIGN_Center); + winScroll = PersonaScrollAreaWindow(winClient.NewChild(Class'PersonaScrollAreaWindow')); + winText = MenuUIHeaderWindow(winScroll.ClipWindow.NewChild(Class'MenuUIHeaderWindow')); + + winScroll.SetPos(12, 13); + winScroll.SetSize(490,95); + + winText.SetTextAlignments(HALIGN_Left, VALIGN_Center); winText.SetFont(Font'FontMenuHeaders_DS'); winText.SetWindowAlignments(HALIGN_Full, VALIGN_Full, textBorderX, textBorderY); } diff --git a/GUI/DeusEx/Classes/DXRMenuScreenNewGame.uc b/GUI/DeusEx/Classes/DXRMenuScreenNewGame.uc index 549f6fa0d..5349d4ce3 100644 --- a/GUI/DeusEx/Classes/DXRMenuScreenNewGame.uc +++ b/GUI/DeusEx/Classes/DXRMenuScreenNewGame.uc @@ -254,6 +254,16 @@ function CreateActionButtons() actionButtons[3].btn.Hide(); } +function GiveTip() +{ + // DXRando: disable this because we think it's more confusing than helpful? + /*if ((Human(Player) == None) || (!Human(Player).bGaveNewGameTips)) + { + root.MessageBox(CheckboxTipHeader, CheckboxTipText, 1, False, Self); + }*/ +} + + defaultproperties { actionButtons(0)=(Align=HALIGN_Left,Action=AB_Cancel) diff --git a/GUI/DeusEx/Classes/HUDMedBotHealthScreen.uc b/GUI/DeusEx/Classes/HUDMedBotHealthScreen.uc index d58c2a17e..6924888b0 100644 --- a/GUI/DeusEx/Classes/HUDMedBotHealthScreen.uc +++ b/GUI/DeusEx/Classes/HUDMedBotHealthScreen.uc @@ -82,6 +82,13 @@ function Tick(float deltaTime) return; } + if(medBot == None || medBot.bDeleteMe || player == None || player.bDeleteMe) { + player = None; + medBot = None; + DestroyWindow(); + return; + } + UpdateMedBotDisplay(); UpdateRegionWindows(); } diff --git a/GUI/DeusEx/Classes/HUDRechargeWindow.uc b/GUI/DeusEx/Classes/HUDRechargeWindow.uc index 0a3efdf83..cb2ac127c 100644 --- a/GUI/DeusEx/Classes/HUDRechargeWindow.uc +++ b/GUI/DeusEx/Classes/HUDRechargeWindow.uc @@ -92,8 +92,8 @@ function UpdateBioWindows() { local float energyPercent; - if (player==None){ - log("Player doesn't exist while updating RepairBot energy bar - probably dead?"); + if (repairBot==None || player==None){ + log("Player or RepairBot doesn't exist while updating RepairBot energy bar - probably dead?"); return; } @@ -112,6 +112,14 @@ event Tick(float deltaSeconds) return; } + if(repairBot == None || repairBot.bDeleteMe || player == None || player.bDeleteMe) { + player = None; + repairBot = None; + DestroyWindow(); + root.PopWindow(); + return; + } + if (lastRefresh >= refreshInterval) { lastRefresh = 0.0; diff --git a/GUI/DeusEx/Classes/HUDSpeedrunSplits.uc b/GUI/DeusEx/Classes/HUDSpeedrunSplits.uc index b47d5c17c..140928472 100644 --- a/GUI/DeusEx/Classes/HUDSpeedrunSplits.uc +++ b/GUI/DeusEx/Classes/HUDSpeedrunSplits.uc @@ -11,10 +11,11 @@ var config Font textfont; var config Color colorBackground, colorText, colorBehind, colorBehindLosingTime, colorBehindGainingTime, colorAhead, colorAheadLosingTime, colorAheadGainingTime, colorBest, colorBestBehind, colorBestAhead; -var config bool enabled, showPrevprev, showPrev, showCurrentMission, showNext, showSeg, showCur, showPB, showSpeed; +var config bool enabled, showPrevprev, showPrev, showCurrentMission, showNext, showSeg, showCur, showPB, showSpeed, showAllSplits; var config int PB[16]; var config int Golds[16]; +var config byte alwaysShowSplit[16]; var config string title, subtitle, footer; var string ttitle, tsubtitle, tfooter; @@ -218,9 +219,8 @@ function InitSizes(GC gc) function DrawWindow(GC gc) { - local int i, t, prev, prevTime, prevprev, prevprevTime, cur, curTime, next, nextTime, time, total; - local float x, y, h, f, delta; - local int cur_totals[16]; + local int i, t, prev, prevprev, cur, curTime, next, time, total, prevTotal; + local float x, y, f, delta; local string msg, s; if(stats == None) return; @@ -234,20 +234,12 @@ function DrawWindow(GC gc) curTime = stats.missions_times[cur]; curTime += stats.missions_menu_times[cur]; - for(i=1; i=1; i--) { + for(i=1; i 0) { + prevTotal += time; prev = i; - prevTime = time; - break; } } @@ -256,7 +248,6 @@ function DrawWindow(GC gc) time += stats.missions_menu_times[i]; if(time > 0) { prevprev = i; - prevprevTime = time; break; } } @@ -264,7 +255,6 @@ function DrawWindow(GC gc) for(i=cur+1; i<=15; i++) { if(balanced_splits[i] > 0) { next = i; - nextTime = balanced_splits[next]; break; } } @@ -276,54 +266,30 @@ function DrawWindow(GC gc) if(left_col == 0) { InitSizes(gc); } - h = text_height; gc.SetTextColor(colorText); gc.SetAlignments(HALIGN_Center, VALIGN_Center); if(ttitle!="") { gc.DrawText(x+x_pos, y+ty_pos, windowWidth - x, text_height, ttitle); - y += h; + y += text_height; } if(tsubtitle!="") { gc.DrawText(x+x_pos, y+ty_pos, windowWidth - x, text_height, tsubtitle); - y += h; + y += text_height; } gc.SetAlignments(HALIGN_Left, VALIGN_Center); - // prevprev split - if(prevprev > 0 && showPrevprev) { - time = cur_totals[prevprev] - balanced_splits_totals[prevprev]; - t = prevprevTime - balanced_splits[prevprev]; - msg = fmtTimeDiff(time); - - s = fmtTime(cur_totals[prevprev]); - DrawTextLine(gc, MissionName(prevprev), msg, GetCmpColor(time, t, prevprevTime, Golds[prevprev]), x, y, s); - y += h; - } - - // previous split - if(prev > 0 && showPrev) { - 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(showCurrentMission) { - msg = fmtTime(balanced_splits_totals[cur]); - DrawTextLine(gc, MissionName(cur), "", colorText, x, y, msg); - y += h; - } - - // next split - if(next > 0 && showNext) { - msg = fmtTime(balanced_splits_totals[next]); - DrawTextLine(gc, MissionName(next), "", colorText, x, y, msg); - y += h; + for(i=1; i 0) { + // past split + for(i=1; i<=mission; i++) { + total += stats.missions_times[i]; + total += stats.missions_menu_times[i]; + } + totalDiff = total - balanced_splits_totals[mission]; + diff = time - balanced_splits[mission]; + sDiff = fmtTimeDiff(totalDiff); + + sTime = fmtTime(total); + DrawTextLine(gc, MissionName(mission), sDiff, GetCmpColor(totalDiff, diff, time, Golds[mission]), x, y, sTime); + } + else if(balanced_splits[mission] > 0) { + // future split + sTime = fmtTime(balanced_splits_totals[mission]); + DrawTextLine(gc, MissionName(mission), "", colorText, x, y, sTime); + } + else { + // not a real split + return y; + } + + return y + text_height; +} + function string MissionName(int mission) { if(split_names[mission] != "") return split_names[mission]; @@ -487,6 +486,7 @@ defaultproperties showCur=true showPB=true showSpeed=true + showAllSplits=false textfont=Font'DeusExUI.FontMenuHeaders_DS'; colorBackground=(R=0,G=0,B=0,A=100) diff --git a/GUI/DeusEx/Classes/NewGamePlusCreditsWindow.uc b/GUI/DeusEx/Classes/NewGamePlusCreditsWindow.uc index 865da0743..2c51671f7 100644 --- a/GUI/DeusEx/Classes/NewGamePlusCreditsWindow.uc +++ b/GUI/DeusEx/Classes/NewGamePlusCreditsWindow.uc @@ -54,7 +54,7 @@ function AddDXRCreditsGeneral() PrintLn(); PrintHeader("Discord Community"); - PrintText("https://discord.gg/daQVyAp2ds"); + PrintText("Mods4Ever.com/discord"); PrintLn(); PrintLn(); diff --git a/GUI/DeusEx/Classes/PersonaScreenImages.uc b/GUI/DeusEx/Classes/PersonaScreenImages.uc index b0fb94f60..95c6ce113 100644 --- a/GUI/DeusEx/Classes/PersonaScreenImages.uc +++ b/GUI/DeusEx/Classes/PersonaScreenImages.uc @@ -1,8 +1,27 @@ class DXRPersonaScreenImages injects PersonaScreenImages; -function SetImage(DataVaultImage newImage) +function ClearViewedImageFlags() { - local string bingoName; + local DataVaultImage image; + local int listIndex; + local int rowId; + + for(listIndex=0; listIndex 0) + { + image = DataVaultImage(lstImages.GetRowClientObject(rowId)); + MarkViewed(image); + } + } +} + + +function MarkViewed(DataVaultImage newImage) +{ + local string bingoName; local DXRando dxr; if (newImage!=None && newImage.bPlayerViewedImage==False){ @@ -19,6 +38,4 @@ function SetImage(DataVaultImage newImage) newImage.bPlayerViewedImage = True; } - - Super.SetImage(newImage); } diff --git a/HXRandomizer.u b/HXRandomizer.u index 03d774ed6..da791b586 100644 Binary files a/HXRandomizer.u and b/HXRandomizer.u differ diff --git a/Pawns/DeusEx/Classes/PlaceholderEnemy.uc b/Pawns/DeusEx/Classes/PlaceholderEnemy.uc index 1d5ba89f2..30203fec1 100644 --- a/Pawns/DeusEx/Classes/PlaceholderEnemy.uc +++ b/Pawns/DeusEx/Classes/PlaceholderEnemy.uc @@ -1,17 +1,18 @@ class PlaceholderEnemy extends #var(prefix)ThugMale; -static function PlaceholderEnemy Create(DXRBase a, vector loc, optional int yaw, optional Name orders, optional Name ordertag) +static function PlaceholderEnemy Create(DXRBase a, vector loc, optional int yaw, optional Name orders, optional Name ordertag, optional Name Alliance) { local PlaceholderEnemy e; local rotator r; r = a.rotm(0, yaw, 0, 16384);// Pawns need an offset of 16384 e = a.Spawn(class'PlaceholderEnemy',,, loc, r); + e.Alliance = Alliance; e.InitializeAlliances(); //Needed to populate Alliance table in time for enemy shuffling if(orders!='') { e.SetOrders(orders, ordertag, false); e.FollowOrders(); } - log("Created "$ class'DXRInfo'.static.ActorToString(e), 'PlaceholderEnemy'); + log("Created "$ class'DXRInfo'.static.ActorToString(e) @ Alliance, 'PlaceholderEnemy'); return e; } diff --git a/RevRandomizer.u b/RevRandomizer.u index daf732fec..6d323a048 100644 Binary files a/RevRandomizer.u and b/RevRandomizer.u differ diff --git a/VMDRandomizer.u b/VMDRandomizer.u index b7bae7a57..a446f2c4b 100644 Binary files a/VMDRandomizer.u and b/VMDRandomizer.u differ diff --git a/notes/missiondistribution.py b/notes/missiondistribution.py new file mode 100644 index 000000000..8880fc342 --- /dev/null +++ b/notes/missiondistribution.py @@ -0,0 +1,41 @@ +eventfile = "..\DXRModules\DeusEx\Classes\DXREvents.uc" + +mission=[] +for i in range(0,16): + mission.append(0) + +def parseBingoLine(line): + if "missions" in line: + missionVal = int(line.split("missions=")[1].replace(")","")) + #print(missionVal) + + for i in range(0,16): + if(missionVal & 1): + mission[i]+=1 + if (i==7 or i==13): + print(line) + missionVal = missionVal >> 1 + + else: + mission[0]+=1 + + +f = open(eventfile) +defprops=False + +for line in f: + if "defaultproperties" in line: + defprops = True + + if defprops: + if "bingo_options" in line: + parseBingoLine(line) + +print("Any mission: "+str(mission[0])) +for i in range(1,16): + print("Mission "+str(i)+": "+str(mission[i])) + + +f.close() + +