From 8687513a422a85f0508fa2badede9933081b0590 Mon Sep 17 00:00:00 2001 From: Robert MacGregor Date: Wed, 24 Jun 2015 21:03:57 -0400 Subject: [PATCH] Better timed AI code; dodging code; better audible dodge code --- scripts/DXAI_Helpers.cs | 7 ++ scripts/DXAI_Main.cs | 22 ++++-- scripts/DXAI_Objectives.cs | 135 ++++++++++++++++++++++++++++++------- 3 files changed, 134 insertions(+), 30 deletions(-) diff --git a/scripts/DXAI_Helpers.cs b/scripts/DXAI_Helpers.cs index d3acde4..76e8711 100644 --- a/scripts/DXAI_Helpers.cs +++ b/scripts/DXAI_Helpers.cs @@ -155,6 +155,13 @@ function GameConnection::getObjectsInViewcone(%this, %typeMask, %distance, %perf return %result; } +function vectorMultiply(%vec1, %vec2) +{ + return (getWord(%vec1, 0) * getWord(%vec2, 0)) SPC + (getWord(%vec1, 1) * getWord(%vec2, 1)) SPC + (getWord(%vec1, 2) * getWord(%vec2, 2)); +} + // If the map editor was instantiated, this will prevent a little bit // of console warnings function Terraformer::getType(%this) { return 0; } diff --git a/scripts/DXAI_Main.cs b/scripts/DXAI_Main.cs index b85d890..de2dbf8 100644 --- a/scripts/DXAI_Main.cs +++ b/scripts/DXAI_Main.cs @@ -118,7 +118,7 @@ function DXAI::validateEnvironment() { error("DXAI: Function 'DefaultGame::AIChooseGameObjective' detected to be overwritten by the current gamemode. Correcting ..."); - eval(%strReplace(%payloadTemplate, "", "AIChooseGameObjective")); + eval(strReplace(%payloadTemplate, "", "AIChooseGameObjective")); // Make sure the patch took if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595) @@ -129,7 +129,7 @@ function DXAI::validateEnvironment() { error("DXAI: Function 'DefaultGame::onAIRespawn' detected to be overwritten by the current gamemode. Correcting ... "); - eval(%strReplace(%payloadTemplate, "", "onAIRespawn")); + eval(strReplace(%payloadTemplate, "", "onAIRespawn")); if (game.onAIRespawn($DXAI::System::RuntimeDummy) != 11595) error("DXAI: Failed to patch 'DefaultGame::onAIRespawn'! DXAI may not function correctly."); @@ -212,6 +212,14 @@ package DXAI_Hooks DXAI::update(); } + // Listen server fix + function disconnect() + { + parent::disconnect(); + + DXAI::Cleanup(); + } + function DefaultGame::AIChangeTeam(%game, %client, %newTeam) { // Remove us from the old commander's control first @@ -250,13 +258,13 @@ package DXAI_Hooks parent::onExplode(%data, %proj, %pos, %mod); // Look for any bots nearby - InitContainerRadiusSearch(%pos, 10, $TypeMasks::PlayerObjectType); + InitContainerRadiusSearch(%pos, 100, $TypeMasks::PlayerObjectType); while ((%targetObject = containerSearchNext()) != 0) { %currentDistance = containerSearchCurrRadDamageDist(); - if (%currentDistance > 10 || !%targetObject.client.isAIControlled()) + if (%currentDistance > 100 || !%targetObject.client.isAIControlled()) continue; // Get the projectile team @@ -276,12 +284,12 @@ package DXAI_Hooks %heardHit = false; %hitDistance = vectorDist(%targetObject.getWorldBoxCenter(), %pos); - if (%hitDistance < 55 && %hitDistance <= %data.explosion.soundProfile.description.maxDistance) + if (%hitDistance <= 20 && %hitDistance <= %data.explosion.soundProfile.description.maxDistance) %heardHit = true; // If the thing has any radius damage (and we heard it), run around a little bit if we need to - if (%data.indirectDamage != 0 && %shouldRun) - %targetObject.client.setDangerLocation(%pos, 20); + if (%data.indirectDamage != 0 && %heardHit) + %targetObject.client.schedule(getRandom(250, 400), "setDangerLocation", %pos, 20); // If we should care and it wasn't a teammate projectile, notify if (%shouldRun && %projectileTeam != %targetObject.client.team) diff --git a/scripts/DXAI_Objectives.cs b/scripts/DXAI_Objectives.cs index 2591f68..8978283 100644 --- a/scripts/DXAI_Objectives.cs +++ b/scripts/DXAI_Objectives.cs @@ -28,22 +28,21 @@ function AIVisualAcuity::weight(%task, %client) %task.setWeight(999); } -function AIVisualAcuity::monitor(%task, %client) +function AIConnection::visualAcuityUpdate(%this) { // Called when the bot is performing the task - - if (%client.enableVisualDebug) + if (%this.enableVisualDebug) { - if (!isObject(%client.originMarker)) + if (!isObject(%this.originMarker)) { - %client.originMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %client.team; name = %client.namebase SPC " Origin"; }; - %client.clockwiseMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %client.team; name = %client.namebase SPC " Clockwise"; }; - %client.counterClockwiseMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %client.team; name = %client.namebase SPC " Counter Clockwise"; }; - %client.upperMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %client.team; name = %client.namebase SPC " Upper"; }; - %client.lowerMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %client.team; name = %client.namebase SPC " Lower"; }; + %this.originMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Origin"; }; + %this.clockwiseMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Clockwise"; }; + %this.counterClockwiseMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Counter Clockwise"; }; + %this.upperMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Upper"; }; + %this.lowerMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Lower"; }; } - %viewCone = %client.calculateViewCone(); + %viewCone = %this.calculateViewCone(); %coneOrigin = getWords(%viewCone, 0, 2); %viewConeClockwiseVector = getWords(%viewCone, 3, 5); %viewConeCounterClockwiseVector = getWords(%viewCone, 6, 8); @@ -52,24 +51,114 @@ function AIVisualAcuity::monitor(%task, %client) %viewConeLowerVector = getWords(%viewCone, 12, 14); // Update all the markers - %client.clockwiseMarker.setPosition(%viewConeClockwiseVector); - %client.counterClockwiseMarker.setPosition(%viewConeCounterClockwiseVector); - %client.upperMarker.setPosition(%viewConeUpperVector); - %client.lowerMarker.setPosition(%viewConeLowerVector); - %client.originMarker.setPosition(%coneOrigin); + %this.clockwiseMarker.setPosition(%viewConeClockwiseVector); + %this.counterClockwiseMarker.setPosition(%viewConeCounterClockwiseVector); + %this.upperMarker.setPosition(%viewConeUpperVector); + %this.lowerMarker.setPosition(%viewConeLowerVector); + %this.originMarker.setPosition(%coneOrigin); } - else if (isObject(%client.originMarker)) + else if (isObject(%this.originMarker)) { - %client.originMarker.delete(); - %client.clockwiseMarker.delete(); - %client.counterClockwiseMarker.delete(); - %client.upperMarker.delete(); - %client.lowerMarker.delete(); + %this.originMarker.delete(); + %this.clockwiseMarker.delete(); + %this.counterClockwiseMarker.delete(); + %this.upperMarker.delete(); + %this.lowerMarker.delete(); } - %result = %client.getObjectsInViewcone($TypeMasks::ProjectileObjectType | $TypeMasks::PlayerObjectType, %client.viewDistance, true); + %result = %this.getObjectsInViewcone($TypeMasks::ProjectileObjectType | $TypeMasks::PlayerObjectType, %this.viewDistance, true); - echo(%result.getCount()); + // What can we see? + for (%i = 0; %i < %result.getCount(); %i++) + { + %current = %result.getObject(%i); + %this.awarenessTicks[%current]++; + + if (%current.getType() & $TypeMasks::ProjectileObjectType) + { + // Did we "notice" the object yet? + // We pick a random notice time between 700ms and 1200 ms + // Obviously this timer runs on a 32ms tick, but it should help provide a little unpredictability + %noticeTime = getRandom(700, 1200); + if (%this.awarenessTicks[%current] < (%noticeTime / 32)) + continue; + + %className = %current.getClassName(); + + // LinearFlareProjectile and LinearProjectile have linear properties, so we can easily determine if a dodge is necessary + if (%className $= "LinearFlareProjectile" || %className $= "LinearProjectile") + { + //%this.setDangerLocation(%current.getPosition(), 20); + + // Perform a raycast to determine a hitpoint + %currentPosition = %current.getPosition(); + %rayCast = containerRayCast(%currentPosition, vectorAdd(%currentPosition, vectorScale(%current.initialDirection, 200)), -1, 0); + %hitObject = getWord(%raycast, 0); + + // We're set for a direct hit on us! + if (%hitObject == %this.player) + { + %this.setDangerLocation(%current.getPosition(), 30); + continue; + } + + // If there is no radius damage, don't worry about it now + if (!%current.getDatablock().hasDamageRadius) + continue; + + // How close is the hit loc? + %hitLocation = getWords(%rayCast, 1, 3); + %hitDistance = vectorDist(%this.player.getPosition(), %hitLocation); + + // Is it within the radius damage of this thing? + if (%hitDistance <= %current.getDatablock().damageRadius) + %this.setDangerLocation(%current.getPosition(), 30); + } + // A little bit harder to detect. + else if (%className $= "GrenadeProjectile") + { + + } + } + // See a player? + else if (%current.getType() & $TypeMasks::PlayerObjectType) + { + // ... if the moron is right there in our LOS then we probably should see them + %start = %this.player.getPosition(); + %end = vectorAdd(%start, vectorScale(%this.player.getEyeVector(), %this.viewDistance)); + + %rayCast = containerRayCast(%start, %end, -1, %this.player); + %hitObject = getWord(%raycast, 0); + + echo(%hitObject); + echo(%current); + if (%hitObject == %current) + %this.stepEngage(%current); + } + } %result.delete(); } + +function AIConnection::enhancedLogicUpdate(%this) +{ + cancel(%this.enhancedLogicHandle); + + //if (!isObject(%this.)) + + if (!isObject(%this.player)) + { + %this.enhancedLogicHandle = %this.schedule(32, "enhancedLogicUpdate"); + return; + } + + %this.visualAcuityUpdate(); + %this.enhancedLogicHandle = %this.schedule(32, "enhancedLogicUpdate"); +} + +function AIVisualAcuity::monitor(%task, %client) +{ + return; + + +}