| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- 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;
- }
- }
- }
- }
- }
|