Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //=====================================================================
- // RX6T5_02.
- // ""(§)FrÅgÊd(§)""
- // **Monarch**
- //
- // 555 ---===CrAzYBoNeS===--- 555
- // Boppolis_The_Dog
- //
- //
- // Thank you Al McElrath for SLV2.
- //=====================================================================
- /** Contains bot behavior for riding rockets. Spawned on the server
- when the pilot or gunner is set.
- */
- class SLBotBrain extends SLInfo;
- var StrangeShell sl;
- var Bot bot;
- var bool bIsPilot;
- var bool bGuiding; // The bot is controlling the rocket thru gr.
- var bool bEject; // An eject is recommended.
- /** Current goal index. For CTF, this is the flag base index. For
- Assault, it's the fort standard index. Both in goals[] in SkyNet.
- */
- var byte goal;
- var SkyNode targetNode; // The node we're heading for.
- var SkyNode lastNode; // The previous node.
- var SkyNode launchNode; // Our first node.
- var bool bNoExpire; // Our target node doesn't expire.
- var bool bReachEject; // Eject when we reach our target node.
- var float targetAcq; // Time we acquired this node.
- var() float targetExpireTime; // Time to give up on this node and find a reachable one.
- var() int ejectDist; // If we get this close to our objective, eject.
- var() int ejectDistFlag; // Same, but just for flag carriers and flag bases.
- var() float randomLinkAdjust; // Random factor for nextNode().
- var() float wallDeter; // The % of wall deaths which will deter us.
- var() float shotDeter; // The % of shot deaths which will deter us.
- var() float skill; // 0.0 to 1.0. Affects reflexes and turning speed.
- /** This only happens when there are no enemy flags at base.
- NOT IMPLEMENTED. FIX.
- */
- var bool bHunting;
- /** E.g. somebody carrying our flag. :)
- */
- var Actor nukeTarget;
- var SkyNet skynet;
- var float suggestedSpeed;
- var Util u;
- function postBeginPlay() {
- u = spawn(Class'Util', self);
- // Invalid goal.
- goal = -1;
- }
- /** The launcher usually handles this, but just in case.
- */
- function spawnSkyNet() {
- foreach allActors(Class'SkyNet', skynet)
- break;
- if (skynet == none)
- skynet = spawn(Class'SkyNet');
- }
- /** Called from the Strangelove, right after spawn.
- */
- function setBot(Bot b, StrangeShell s, optional SkyNet net, optional SkyNode ln) {
- bot = b;
- sl = s;
- launchNode = ln;
- targetNode = ln;
- // They need all the help they can get...
- //skill = b.skill / 3.0;
- skill = (b.skill / 3.0) * 0.2 + 0.8;
- bIsPilot = (b == sl.getPilot());
- if (bIsPilot) {
- bGuiding = true;
- skynet = net;
- spawnSkyNet();
- } else {
- gotoState('GunnerBoarding');
- }
- // Skill == reaction time. Minimum timer is 0.01.
- setTimer(fmax(1.0 * (1.0 - skill), 0.01), true);
- u.debug("setBot(): brain set for: " $ u.sname(bot) $ " skill: " $ u.chopf(skill) $ " launch node: " $ u.sname(ln));
- }
- function timer() {
- // Eject if fuel low! Max throttle first.
- if (sl.fuel < 3 && (sl.fc == none || !(sl.fc.getAmmo() > 0))) {
- u.debug("timer(): No fuel! Ejecting!");
- if (bIsPilot)
- sl.throttle = 1.0;
- bEject = true;
- } else {
- if (bIsPilot) {
- pilotBehavior();
- } else {
- gunnerBehavior();
- }
- }
- if (bEject)
- eject();
- }
- /** First, set the goal. If it's valid, decide where and what to do.
- */
- function pilotBehavior() {
- local SkyNode temp;
- if (setGoal()) {
- // First check for nuke targets.
- if (bot.moveTarget == myFlag()) {
- nukeTarget = bot.moveTarget;
- } else if (bot.enemy == myFlag().holder) {
- // Ramming speed!!!
- sl.arm();
- sl.throttle = 0.1;
- nukeTarget = bot.enemy;
- } else {
- nukeTarget = none;
- }
- if (nukeTarget != none) {
- targetActor(nukeTarget);
- } else {
- if (targetNode == none) {
- // This might happen at a dead end node.
- u.debug("pilotBehavior(): no target node");
- temp = closestNode(skynet.linkRadius);
- if (temp != lastNode) {
- setTargetNode(temp);
- } else {
- bEject = true;
- }
- }
- if (bReachEject && targetNode != none) {
- if (inrange(targetNode, 0.5)) {
- u.debug("pilotBehavior(): within range of eject node");
- // So we don't kill outselves. FIX!
- sl.disarm();
- bEject = true;
- }
- } else {
- /*
- if (inrange(skynet.goalNodes[goal], 1.5)) {
- u.debug("pilotBehavior(): within range of goal node");
- bEject = true;
- }
- */
- }
- // Throttle! Shoot mode!
- if (targetNode != none && !inrange(targetNode, 2.0) && linedup(targetNode, 0.01)) {
- if (suggestedSpeed == -1)
- sl.throttle = 0.5 + 0.5 * skill;
- if (bot.enemy != none) {
- // Switch to shoot mode!
- if (!sl.bShootMode) {
- u.debug("pilotBehavior(): going to shoot mode");
- sl.goShootMode();
- }
- }
- } else {
- if (suggestedSpeed == -1)
- sl.throttle = 0.1;
- // Man the wheel!
- if (sl.bShootMode) {
- u.debug("pilotBehavior(): returning from shoot mode");
- sl.goShootMode();
- }
- }
- // Handle speed.
- if (suggestedSpeed >= 0) {
- if (suggestedSpeed < 1.0) {
- sl.throttle = suggestedSpeed;
- } else {
- if (abs(vsize(sl.velocity) - suggestedSpeed) < 50.0) {
- // Hold.
- } else {
- if (vsize(sl.velocity) < suggestedSpeed) {
- sl.throttle += 0.1;
- } else {
- sl.throttle -= 0.1;
- }
- sl.throttle = fclamp(sl.throttle, 0.0, 1.0);
- }
- }
- }
- // Can we see the goal node? Go for it! Did we already
- // spot it earlier? Keep going.
- if (targetNode != none && !bReachEject) {
- temp = skynet.goalNodes[goal].randomLink();
- if (temp != none && los(temp)) {
- u.debug("pilotBehavior(): new beeline: " $ u.sname(temp));
- setTargetNode(temp);
- bReachEject = true;
- bNoExpire = true;
- }
- }
- // Expire the node if it's been too long. Usually means
- // we're doing loop de loops.
- if (!bNoExpire && targetNodeExpired()) {
- u.debug("timer(): target expired: " $ u.sname(targetNode));
- setTargetNode(nextNode(targetNode, true));
- }
- // Target it.
- if (targetNode != none)
- targetActor(targetNode);
- }
- }
- }
- /** My flag?
- */
- function CTFFlag myFlag() {
- if (skynet.bCTF) {
- return CTFReplicationInfo(level.game.gameReplicationInfo).flagList[bot.playerReplicationInfo.team];
- }
- return none;
- }
- /** Returns true if this flag is an enemy flag.
- */
- function bool enemyFlag(CTFFlag flag) {
- return (flag != myFlag());
- }
- /** Returns the distance to the specified actor.
- */
- function float distto(Actor a) {
- return vsize(a.location - bot.location);
- }
- /** Returns true if the specified actor is within the specified
- range. If the range is < 10.0, it's a factor of the link radius.
- */
- function bool inrange(Actor a, float range) {
- if (range < 10.0) {
- return (distTo(a) < range * skynet.linkRadius);
- } else {
- return (distTo(a) < range);
- }
- }
- /** Returns true if the bot has LOS.
- */
- function bool los(Actor a) {
- u.debug("los(" $ u.sname(a) $ ")", DL_Verbose);
- return fastTrace(a.location, bot.location);
- }
- /** Lined up.
- */
- function bool linedup(Actor a, optional float error) {
- return ((1.0 - (normal(sl.velocity) dot normal(a.location - bot.location))) <= error);
- }
- /** Turns us towards the specified actor.
- */
- function targetActor(Actor a) {
- desiredRotation = adjustRot(rotator(a.location - bot.location));
- }
- /** Same as above, but for a raw location vector.
- */
- function targetLoc(vector l) {
- desiredRotation = adjustRot(rotator(l - bot.location));
- }
- /** This sets the goal index. We may set bEject, so this needs to
- happen just before we test for that in pilotBehavior(). Returns
- true if we have a valid goal.
- */
- function bool setGoal() {
- local byte oldg;
- // We need to compare with the new one for debugging.
- oldg = goal;
- if (skynet.getGoal(bot, goal)) {
- if (goal != oldg)
- u.debug("setGoal(): new goal is: " $ u.sname(skynet.goals[goal]));
- return true;
- }
- bEject = true;
- return false;
- }
- /** When the pilot is cleared (and a damage type is specified), it
- notifies us so we can update the node counts.
- */
- function deathBy(name type) {
- local SkyNode n;
- local byte team;
- // Find the closer node. We'll always have a targetNode. Right?
- if (lastNode != none && distto(lastNode) > distto(targetNode))
- n = targetNode;
- else
- n = lastNode;
- team = bot.playerReplicationInfo.team;
- switch (name) {
- case 'Shot':
- n.shotDeaths[team]++;
- break;
- case 'HitWall':
- n.wallDeaths++;
- break;
- }
- }
- function bool targetNodeExpired() {
- return ((level.timeseconds - targetAcq) > targetExpireTime);
- }
- function setTargetNode(SkyNode sn) {
- lastNode = targetNode;
- u.debug("setTargetNode(" $ u.sname(sn) $ ")");
- targetNode = sn;
- if (targetNode != none) {
- targetAcq = level.timeseconds;
- suggestedSpeed = sn.suggestedSpeed;
- if (skynet.bShowLinks) {
- if (lastNode != none)
- lastNode.vis(false);
- targetNode.vis(true, goal);
- }
- targetNode.passes++;
- targetNode.teamPasses[bot.playerReplicationInfo.team]++;
- }
- }
- function SkyNode closestNode(float r) {
- return skynet.closestNode(bot.location, r);
- }
- /** The Strangelove reports a touch on the sky node.
- */
- function nodeReached(SkyNode sn) {
- if (sn == targetNode) {
- u.debug("nodeReached(): " $ u.sname(sn));
- if (sn != launchNode)
- // Success! Or so we think...
- launchNode.addLaunch(true);
- setTargetNode(nextNode(sn, true));
- }
- }
- /** Decide which node to go to next from the current one. If random is
- true, we add a slight random factor in, which essentially makes close
- calls go either way, rather than always to the higher weight.
- */
- function SkyNode nextNode(SkyNode cur, optional bool random, optional bool bNextNext) {
- local float RLA;
- local int i;
- local SkyNode next, nn;
- local float w, curw;
- // Use ours if non-zero.
- if (randomLinkAdjust > 0)
- RLA = randomLinkAdjust;
- else
- RLA = skynet.randomLinkAdjust;
- for (i = 0; i < arrayCount(cur.links); i++) {
- if (bNextNext && !los(cur.links[i]))
- continue;
- // Skip it if we just came from there?
- if (cur.links[i] == lastNode)
- continue;
- curw = cur.links[i].weight[goal];
- if (random)
- curw += frand() * RLA;
- if (curw > w || next == none) {
- next = cur.links[i];
- w = curw;
- }
- }
- // If the next node is weighted lower than the current, eject.
- if (next == none || w < cur.weight[goal] || avoidNode(next, bot.playerReplicationInfo.team)) {
- u.debug("nextNode(" $ u.sname(cur) $ "): ejecting: next: " $ u.sname(next) $ " current/next weight: " $ cur.weight[goal] $ "/" $ curw);
- if (!bNextNext)
- bEject = true;
- } else {
- /** FIX. Infinite recursion.
- // Skip to the next next one if we can see it.
- if (next != none) {
- nn = nextNode(next, random, true);
- if (nn != none) {
- u.debug("nextNode(): skipping " $ u.sname(next) $ " to " $ u.sname(nn));
- // This is so it doesn't expire too soon.
- targetAcq += targetExpireTime;
- next = nn;
- }
- }
- */
- }
- return next;
- }
- /** Return true if we don't want to go to this node, because everytime
- someone's tried, they've hit a wall or gotten shot down.
- */
- function bool avoidNode(SkyNode n, byte team) {
- local bool avoid;
- avoid = ((n.wallDeaths / float(n.passes)) > wallDeter || (n.shotDeaths[team] / float(n.teamPasses[team])) > shotDeter);
- if (avoid) {
- u.debug("avoidNode(" $ u.sname(n) $ ") death ratios: wall: " $ (n.wallDeaths / float(n.passes)) $ " shot: " $ (n.shotDeaths[team] / float(n.teamPasses[team])));
- // Turn off the warhead, if we can.
- sl.disarm();
- }
- return avoid;
- }
- function FlagBase getFlag() {
- return CTFReplicationInfo(level.game.gameReplicationInfo).flagList[bot.playerReplicationInfo.team].homeBase;
- }
- /** This takes the desired rotation and slowly moves to meet
- it. Basically our own home brewed bRotateToDesired (which doesn't
- seem to work in this instance). Note we could use the full
- rotation rate, but this uses the yaw rate for both yaw and pitch.
- */
- function tick(float delta) {
- if (sl != none) {
- setRotation(rotation + sturn(desiredRotation, rotation, int(delta * skill * rotationRate.yaw)));
- }
- }
- /** This compensates for the sliding of the rocket.
- */
- function rotator adjustRot(rotator r) {
- return (r - sturn(rotator(sl.velocity), r, 0, 16384));
- }
- /** Given a new and old rotation, it figures the smallest deltas to
- get to the new rotation. If max is given, it limits the turn by that
- much.
- The limit optional arg is used when trying to compensate for
- momentum. I can't explain it in less than 500 words, but if you don't
- drop the compensation when the delta is over 90 degrees, it goes in
- the direct opposite direction.
- */
- function rotator sturn(rotator newr, rotator oldr, optional int max, optional int limit) {
- local int dyaw, dpitch;
- local rotator turn;
- dyaw = (newr.yaw & 65535) - (oldr.yaw & 65535);
- dpitch = (newr.pitch & 65535) - (oldr.pitch & 65535);
- // If it's more than the limit, zero that component.
- if (limit > 0) {
- if (abs(dyaw) > limit)
- dyaw = 0;
- if (abs(dpitch) > limit)
- dpitch = 0;
- }
- if (dyaw < -32768)
- dyaw += 65536;
- else if (dyaw > 32768)
- dyaw -= 65536;
- if (dpitch < -32768)
- dpitch += 65536;
- else if (dpitch > 32768)
- dpitch -= 65536;
- if (max > 0) {
- dyaw = fmin(dyaw, max);
- dpitch = fmin(dpitch, max);
- }
- turn.yaw = dyaw;
- turn.pitch = dpitch;
- turn.roll = 0;
- return turn;
- }
- function rotator getGuidedRotation() {
- return rotation;
- }
- /** Decide what the bot does, as far as ejecting, etc., as a gunner.
- */
- function gunnerBehavior() {
- bot.bCanFly = true;
- bot.bCanWalk = false;
- bot.bAdvancedTactics = false;
- //bot.bCanDuck = true;
- //bot.moveTarget = -1;
- // If the rider ejects or gets killed, eject!
- if (sl.getPilot() == none && !isInState('GunnerEject'))
- gotoState('GunnerEject');
- // Don't jump off unless we're less than 30' (360 uu) off the ground.
- if (sl.fastTrace(sl.location + vect(0, 0, -1) * 360)) {
- u.debug("gunnerBehavior(): over 360 off ground", DL_Verbose);
- } else {
- // We have the flag! Even if we're set not to obey orders, eject if we get within flag base dist.
- if (skynet.bCTF && bot.playerReplicationInfo.hasFlag != none && inrange(getFlag(), ejectDistFlag)) {
- u.debug("gunnerBehavior(): eject! close to flag base");
- eject();
- // We're in eject distance of something we want.
- } else if (bot.moveTarget != none && bot.moveTarget != sl.getPilot() && inrange(bot.moveTarget, ejectDist)) {
- // Eject if we're obeying orders (and they're not 'follow'
- // orders) or we're out of ammo and our movetarget is a weapon or ammo.
- if ((sl.bBotsObeyOrders && !(bot.orders == 'Follow' && bot.orderObject == sl.getPilot())) || ((bot.moveTarget.isA('Weapon') || bot.moveTarget.isA('Ammo')) && bot.weapon != none && (bot.weapon.bMeleeWeapon || bot.weapon.ammoType.ammoAmount == 0))) {
- u.debug("gunnerBehavior(): eject! within range of movetarget: " $ u.sname(bot.moveTarget));
- eject();
- }
- }
- }
- }
- /** This state makes it so they don't jump off immediately.
- */
- state GunnerBoarding {
- ignores gunnerBehavior;
- begin:
- sleep(3.0);
- gotoState('');
- }
- /** Give them some response time for ejecting. This only happens if
- the rider ejects or is killed.
- */
- state GunnerEject {
- ignores gunnerBehavior;
- begin:
- sleep(1.5);
- if (sl.gunner != none)
- eject();
- }
- function eject() {
- sl.eject(bot);
- //bot.gotoState('Roaming', 'PreBegin');
- bot.whatToDoNext('', '');
- }
- function destroyed() {
- u.debug("destroyed()");
- if (skynet != none) {
- if (skynet.bShowLinks) {
- if (targetNode != none) {
- targetNode.vis(false);
- }
- }
- } else {
- u.debug("destroyed(): skynet == none");
- }
- super.destroyed();
- }
- // end
- defaultproperties
- {
- targetExpireTime=3.000000
- ejectDist=1000
- ejectDistFlag=2000
- wallDeter=0.500000
- shotDeter=0.500000
- Skill=0.500000
- RotationRate=(Pitch=10000,Yaw=10000)
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement