5
\$\begingroup\$

Today I decided to continue working on a UnrealScript (Unreal Engine 1) class that lets bots jump onto ledges by firing weapons beneath themselves.

(The only problem is that they don't aim and jump correctly, but as soon as this is fixed I'll edit.)

Methodology:

  1. The Bot that wants to use the navigation node will call the function SpecialHandling in the node's class after calling SpecialCost (and if the cost is doable).
  2. SpecialCost determines if the pawn is capable of doing the jump, and checks if the pawn is a bot.
  3. The bot calls the SpecialHandling function which can alter its navigation path depending on the conditions, and if the bot doesn't have the weapon determined by the class variable WeaponToJump (which he'll use to perform the jump), he'll grab one; if there's no weapon nearby the bot will ignore that route for some time.
  4. If the bot did run through all the tests, it's time to do the jump! The node's state becomes BotJumping where all the action will be performed.
  5. In it, the bot will jump to the destination, will aim to a location determined by an abstract actor and will SHOOT! Beneath him, that is, so he gains momentum to pass to the ledge defined by the target LiftExit actor with matching LiftTag.

WeaponJumpSpot:

///////////////////////////////////////////////////////////////////////////
// WeaponJumpSpot.                                                       //   
//                                                                         //
// Makes the bot fire the weapon beneath it to gain boost to the target. //
///////////////////////////////////////////////////////////////////////////
class WeaponJumpSpot expands LiftCenter;

//The weapon the pawn will relize the jump with.
var()   class<Weapon>       WeaponToJump;

//Two variables that enables transmitting the pawn and aim rotator from SpecialHandling to Timer.
var     pawn                rstt;
var     rotator             IAim;

//This variable defines the delay between the jump and the shoot.
var()   float               ShootDelay;

//Used in state code
var     float               OldCost;
var     Pawn                SeekerBot;
var     Weapon              We;
var     LiftExit            Le;
var     Pawn                TickPawn;

//The target to shoot at
var()   name                ShootTarget;
var     WeaponJumpTarget    TargetSpot;

//How much time the bot will ignore the route if he doesn't has the weapon to do the jump
var()   float               IgnoreTime;

//Minimum health needed to perform the leap.
var()   int                 MinHealth;

function PreBeginPlay()
{
    local WeaponJumpTarget closest;
    local WeaponJumpTarget current;
    local float            dist;
    local float            mdist;

    Super.PreBeginPlay();

    dist = 4096;

    foreach AllActors(class'WeaponJumpTarget', current, ShootTarget)
    {
        mdist = VSize(current.location - self.location);
        if( mdist < dist )
        {
            dist = mdist;
            closest = current;
        }
    }
    if( closest != None )
        TargetSpot = closest;
    else
        self.Destroy();
}

//SpecialCost changes the cost of the path depending on certain conditions.
//
//Returns the new cost value.
//
event int SpecialCost(Pawn Seeker)
{
    if ( !Seeker.IsA('Bot') || WeaponToJump == None || Seeker.Health < MinHealth )
        return 10000000;
    return 300;
}

//Calculates the aim for the jump.
function rotator CalculateJumpAim(Actor Other, Actor Target)
{
    local   rotator Aiming; //the result
    local   vector  Distance; //the vector of offset between Other and the target
    local   float   PitchFactor; //the factor that the pitch of Aiming will be modified by

    if ( Other == None || Target == None )
        return rot(0, 0, 0);

    // Points to the nearest WeaponJumpTarget.
    if ( TargetSpot != None )
        return rotator(TargetSpot.location - Other.location);

    return rot(0, 0, 0);
}

//Calculates the closest actor of type Type from vector Origin, in a maximum distance of MaxDistance.
//
//Returns the closest actor, of course.
function Actor ClosestActor(class<Actor> Type, vector Origin, float MaxDistance)
{
    local float MaxDist;
    local float Distance;
    local Actor A;
    local Actor C;

    MaxDist = MaxDistance;

    //Sanity check
    if ( Type == None || MaxDistance == 0 )
        return None;

    foreach AllActors(Type, A)
    {
        Distance = VSize(A.Location - Origin);

        if ( Distance < MaxDist )
        {
            MaxDist = Distance;
            C = A;
        }
    }

    return C;
}

//SpecialHandling changes the path of the bot depending in conditions.
//
//Returns where the bot must go after the SpecialHandling call.
//
function Actor SpecialHandling(Pawn Other)
{
    local   LiftExit    Target; //the target LiftExit the bot wants to go to

    local   Weapon      W, XW, YW;
    local   float       WX, np;

    WX = 4096;
    Target = LiftExit(Other.MoveTarget);

    //finds out whether the pawn has the weapon in their inventory
    if ( WeaponToJump != None )
        W = Weapon(Other.FindInventoryType(WeaponToJump));

    //makes the jump and calls SetTimer so the bot fires
    if ( (Target != None) && (Target.LiftTag == LiftTag) && (W != None) )
    {
        We = W;
        SeekerBot = Other;
        Le = Target;
        GoToState('Botjump');
    }

    //if it doesn't has the weapon to do the jump acquires a weapon before realizing the jump.
    //it does a ForEach for all weapons and determine the closer one from the bot that is the weapon for the Weapon Jump.
    else if ( ( W == None ) && ( WeaponToJump != None ) )
    {
        YW = Weapon(ClosestActor(WeaponToJump, Other.Location, 4096));

        if ( YW != None )
            if ( YW.bActive && YW.IsInState('Pickup') )
                return YW; //acquires the weapon before trying again.

        GoToState('IgnoreRoute');
    }

    return ClosestActor(class'PathNode', Other.Location, 2048); //goes to the closest path node and temporarily ignores the route
                                                                  //if there is no path node, goes to None
}

function Timer()
{
    //makes the pawn fire it's weapon
    rstt.ViewRotation = IAim;
    GoToState('Fire');
}

auto state() Idle
{
}

//Performs the jump and calls SetTimer()
state() Botjump
{
    Begin:
        if ( Bot(SeekerBot) == None )
            GoToState('Idle');

        IAim = CalculateJumpAim(SeekerBot, Target);
        SeekerBot.TurnToward(Target);
        Bot(SeekerBot).BigJump(Le);
        SeekerBot.Weapon = We;
        rstt = SeekerBot;
        SetTimer(ShootDelay, false);
        SeekerBot = None;
        GoToState('Idle');
}

//Makes bots temporarily ignore the route
state() IgnoreRoute
{
    Begin:
        OldCost = ExtraCost;
        ExtraCost = 10000000;
        Sleep(IgnoreTime);
        ExtraCost = OldCost;    
        GoToState('Idle');
}

//Makes bots perform the fire beneath themselves
state() Fire
{
    Begin:
        Sleep(0.1);
        rstt.FireWeapon();
        GoToState('Idle');
}

WeaponJumpTarget:

//=============================================================================
// WeaponJumpTarget.
//=============================================================================
class WeaponJumpTarget expands Keypoint abstract;
\$\endgroup\$
  • \$\begingroup\$ Make sure that fix is edited in before an answer gets posted, otherwise you'll have to post a new version. \$\endgroup\$ – Jamal Feb 26 '16 at 21:44
  • \$\begingroup\$ Once the fix is discovered, I'll put it here. But now I'll have to post it in Mathematica. \$\endgroup\$ – Gustavo6046 Feb 26 '16 at 21:45
  • \$\begingroup\$ I posted it in Mathematica, but it was downvoted. But I found the fix, which I initially didn't want because it involved another actor, but it's easier than making an entire algorithm myself. \$\endgroup\$ – Gustavo6046 Apr 3 '16 at 15:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.