Urho3D/bin/Data/Scripts/NinjaSnowWar/AIController.as
2015-01-18 21:31:13 +08:00

196 lines
7.0 KiB
ActionScript

const float initialAggression = 0.0020;
const float initialPrediction = 30;
const float initialAimSpeed = 10;
const float deltaAggression = 0.000025;
const float deltaPrediction = -0.15;
const float deltaAimSpeed = 0.30;
const float maxAggression = 0.01;
const float maxPrediction = 20;
const float maxAimSpeed = 40;
float aiAggression = initialAggression;
float aiPrediction = initialPrediction;
float aiAimSpeed = initialAimSpeed;
void ResetAI()
{
aiAggression = initialAggression;
aiPrediction = initialPrediction;
aiAimSpeed = initialAimSpeed;
}
void MakeAIHarder()
{
aiAggression += deltaAggression;
if (aiAggression > maxAggression)
aiAggression = maxAggression;
aiPrediction += deltaPrediction;
if (aiPrediction < maxPrediction)
aiPrediction = maxPrediction;
aiAimSpeed += deltaAimSpeed;
if (aiAimSpeed > maxAimSpeed)
aiAimSpeed = maxAimSpeed;
}
class AIController
{
// Use a weak handle instead of a normal handle to point to the current target
// so that we don't mistakenly keep it alive.
WeakHandle currentTarget;
float newTargetTimer = 0;
void Control(Ninja@ ownNinja, Node@ ownNode, float timeStep)
{
// Get new target if none. Do not constantly scan for new targets to conserve CPU time
if (currentTarget.Get() is null)
{
newTargetTimer += timeStep;
if (newTargetTimer > 0.5)
GetNewTarget(ownNode);
}
Node@ targetNode = currentTarget.Get();
if (targetNode !is null)
{
// Check that current target is still alive. Otherwise choose new
Ninja@ targetNinja = cast<Ninja>(targetNode.scriptObject);
if (targetNinja is null || targetNinja.health <= 0)
{
currentTarget = null;
return;
}
RigidBody@ targetBody = targetNode.GetComponent("RigidBody");
ownNinja.controls.Set(CTRL_FIRE, false);
ownNinja.controls.Set(CTRL_JUMP, false);
float deltaX = 0.0f;
float deltaY = 0.0f;
// Aim from own head to target's feet
Vector3 ownPos(ownNode.position + Vector3(0, 0.9, 0));
Vector3 targetPos(targetNode.position + Vector3(0, -0.9, 0));
float distance = (targetPos - ownPos).length;
// Use prediction according to target distance & estimated snowball speed
Vector3 currentAim(ownNinja.GetAim() * Vector3(0, 0, 1));
float predictDistance = distance;
if (predictDistance > 50) predictDistance = 50;
Vector3 predictedPos = targetPos + targetBody.linearVelocity * predictDistance / aiPrediction;
Vector3 targetAim = (predictedPos - ownPos);
// Add distance/height compensation
float compensation = Max(targetAim.length - 15, 0.0);
targetAim += Vector3(0, 0.6, 0) * compensation;
// X-aiming
targetAim.Normalize();
Vector3 currentYaw(currentAim.x, 0, currentAim.z);
Vector3 targetYaw(targetAim.x, 0, targetAim.z);
currentYaw.Normalize();
targetYaw.Normalize();
deltaX = Clamp(Quaternion(currentYaw, targetYaw).yaw, -aiAimSpeed, aiAimSpeed);
// Y-aiming
Vector3 currentPitch(0, currentAim.y, 1);
Vector3 targetPitch(0, targetAim.y, 1);
currentPitch.Normalize();
targetPitch.Normalize();
deltaY = Clamp(Quaternion(currentPitch, targetPitch).pitch, -aiAimSpeed, aiAimSpeed);
ownNinja.controls.yaw += 0.1 * deltaX;
ownNinja.controls.pitch += 0.1 * deltaY;
// Firing? if close enough and relatively correct aim
if ((distance < 25) && (currentAim.DotProduct(targetAim) > 0.75))
{
if (Random(1.0) < aiAggression)
ownNinja.controls.Set(CTRL_FIRE, true);
}
// Movement
ownNinja.dirChangeTime -= timeStep;
if (ownNinja.dirChangeTime <= 0)
{
ownNinja.dirChangeTime = 0.5 + Random(1.0);
ownNinja.controls.Set(CTRL_UP|CTRL_DOWN|CTRL_LEFT|CTRL_RIGHT, false);
// Far distance: go forward
if (distance > 30)
ownNinja.controls.Set(CTRL_UP, true);
else if (distance > 6)
{
// Medium distance: random strafing, predominantly forward
float v = Random(1.0);
if (v < 0.8)
ownNinja.controls.Set(CTRL_UP, true);
float h = Random(1.0);
if (h < 0.3)
ownNinja.controls.Set(CTRL_LEFT, true);
if (h > 0.7)
ownNinja.controls.Set(CTRL_RIGHT, true);
}
else
{
// Close distance: random strafing backwards
float v = Random(1.0);
if (v < 0.8)
ownNinja.controls.Set(CTRL_DOWN, true);
float h = Random(1.0);
if (h < 0.4)
ownNinja.controls.Set(CTRL_LEFT, true);
if (h > 0.6)
ownNinja.controls.Set(CTRL_RIGHT, true);
}
}
// Random jump, if going forward
if ((ownNinja.controls.IsDown(CTRL_UP)) && (distance < 1000))
{
if (Random(1.0) < (aiAggression / 5.0))
ownNinja.controls.Set(CTRL_JUMP, true);
}
}
else
{
// If no target, walk idly
ownNinja.controls.Set(CTRL_ALL, false);
ownNinja.controls.Set(CTRL_UP, true);
ownNinja.dirChangeTime -= timeStep;
if (ownNinja.dirChangeTime <= 0)
{
ownNinja.dirChangeTime = 1 + Random(2);
ownNinja.controls.yaw += 0.1 * (Random(600) - 300);
}
if (ownNinja.isSliding)
ownNinja.controls.yaw += 0.2;
}
}
void GetNewTarget(Node@ ownNode)
{
newTargetTimer = 0;
Array<Node@> nodes = ownNode.scene.GetChildrenWithScript("Ninja", true);
float closestDistance = M_INFINITY;
for (uint i = 0; i < nodes.length; ++i)
{
Node@ otherNode = nodes[i];
Ninja@ otherNinja = cast<Ninja>(otherNode.scriptObject);
if (otherNinja.side == SIDE_PLAYER && otherNinja.health > 0)
{
float distance = (ownNode.position - otherNode.position).lengthSquared;
if (distance < closestDistance)
{
currentTarget = otherNode;
closestDistance = distance;
}
}
}
}
}