Food Feud

Food Feud is a networked multiplayer game with a quirky flair built using UDK. Players compete to collect and protect customers in this custom capture-the-flag game. Food Feud features four unique weapons, four custom classes, network support and one custom game mode.

I worked on Food Feud as lead programmer. My responsibilities included leading the programming team, as well the artificial intelligence systems and custom capture-the-flag game mode.

The game was developed by a team of seven over three months.

Click here to download the game

View Code

[code lang=”cpp”] class FFSquadAI extends UTSquadAI;

var FFBaseObjective homeBase, enemyBase;

var FFFlag ownedFlags[ 3 ];
var FFFlag enemyFlags[ 3 ];
var FFFlag neutralFlags[ 3 ];
var int nOwnedFlags;
var int nEnemyFlags;
var int nNeutralFlags;

var float AIDropFlagDistTolerance;
var float AIIgnoreFlagDistance;

/* FindPathToObjective()
Returns path a bot should use moving toward a base
*/
function bool FindPathToObjective( UTBot B, Actor O )
{
// don’t ever use gather points when have flag
if ( UTPlayerReplicationInfo(B.PlayerReplicationInfo).bHasFlag )
{
B.bFinalStretch = true;
}

return Super.FindPathToObjective(B, O);
}

// Find out who owns which flags
function RecheckFlagStatus()
{
local FFFlag curFlag;

nOwnedFlags = 0;
nEnemyFlags = 0;
nNeutralFlags = 0;

foreach AllActors(class’FoodFeud.FFFlag’, curFlag )
{
if ( curFlag.Team == Team )
{
ownedFlags[ nOwnedFlags ] = curFlag;
++nOwnedFlags;
}
else if ( curFlag.Team == none )
{
neutralFlags[ nNeutralFlags ] = curFlag;
++nNeutralFlags;
}
else
{
enemyFlags[ nEnemyFlags ] = curFlag;
++nEnemyFlags;
}
}
}

/* OrdersForFlagCarrier()
Tell bot what to do if he’s carrying the flag
*/
function bool OrdersForFlagCarrier( UTBot B )
{
//local UTCTFBase FlagBase;

B.GoalString = "Return to Base with enemy flag!";
if ( !FindPathToObjective( B, homeBase ) )
{
B.GoalString = "No path to home base for flag carrier";
// FIXME – suicide after a while
`log( "Bot could not find path to home base" );
return false;
}

if ( B.MoveTarget == homeBase )
{
B.GoalString = "Near my Base with enemy flag!";

if ( VSize( B.Pawn.Location – homeBase.Location ) < AIDropFlagDistTolerance )
FFPawn( B.Pawn ).DropFlag();
}
return true;
}

function bool CheckSquadObjectives( UTBot B )
{
local FFFlag closestFlag;
local float closestFlagDist;
local FFCTF game;
local UTGameObjective testObjective;

if ( UTPlayerReplicationInfo(B.PlayerReplicationInfo).bHasFlag )
return OrdersForFlagCarrier(B);

AddTransientCosts(B,1);

// Always grab a free flag when it’s visible and close enough
game = FFCTF( WorldInfo.Game );
closestFlag = game.ClosestVisibleFreeFlag( B );

if ( closestFlag != none )
{
// I see a free flag
closestFlagDist = VSize( closestFlag.Location – B.Pawn.Location );

if ( closestFlagDist < AIIgnoreFlagDistance )
{
// Flag is close enough to matter
if ( closestFlagDist < game.FlagGrabDistance )
{
// Flag is close enough to grab
FFPawn( B.Pawn ).GrabFlag( closestFlag );
return OrdersForFlagCarrier( B );
}

// Move to the flag
if ( FindPathToObjective( B, closestFlag ) )
return true;
}
}

// Don’t see a free flag, or no path to it.
// If I’m not grabbing a free flag, then…

// If we have a delivery guy who is boosting, go grab a granny.
if ( FFBot( B ).bBoosting )
{
if ( FFFlagSpawnPoint( SquadObjective ) == none )
{
testObjective = FFCTF( WorldInfo.Game ).FindReadyFlagBaseObjective();
if ( testObjective != none )
{
if ( FFFlagSpawnPoint( testObjective ).bFlagIsHere )
SquadObjective = testObjective;
}
}

if ( FFFlagSpawnPoint( SquadObjective ) == none && B.Enemy != none )
return false;
}
// I should go to the squad’s objective only if I’m not actively fighting
else if ( B.Enemy != none )
{
return false;
}

// If I’ve reached my squad’s objective, send my squad somewhere else
if ( SquadObjective == none || VSize( B.Pawn.Location – SquadObjective.Location ) < AIDropFlagDistTolerance )
{
UTTeamInfo( Team ).AI.FindNewObjectiveFor( self, true );
// Or TellBotHowToDisable() could set a new squad objective.
return true;
}

return Super.CheckSquadObjectives(B);
}

DefaultProperties
{
AIDropFlagDistTolerance = 100;
AIIgnoreFlagDistance = 1000;
}
[/code]

[code lang=”cpp”] class GreaseTrapProjectile extends Actor;

var float BounceRatio;

var float maxLifespan;
var float curAge;
var float greaseDuration;
var float greaseGrowTime;
var float greaseShrinkTime;
var float greaseMaxRadius;
var float greaseRadius;

var byte nBounces;
var byte maxBounces;

var GreaseBlob myBlob;

var Controller damageInstigator;

//=========================================================================
//=========================================================================
auto simulated state Flying
{
simulated function Tick( float DeltaTime )
{
curAge += DeltaTime;
if ( curAge > maxLifespan ) Landed( Velocity, none );
}

//=========================================================================
//=========================================================================
simulated function Landed( Vector HitNormal, Actor FloorActor )
{
SetPhysics( PHYS_NONE );

GotoState(‘Resting’);
}

//=========================================================================
//=========================================================================
simulated function HitWall (vector norm, actor wall, primitivecomponent something )
{
if ( wall.bWorldGeometry )
{
if ( norm.Z > 0.7 )
{
++nBounces;

if ( nBounces > maxBounces )
{
Landed( norm, wall );

return;
}
}

Velocity = BounceRatio * (( Velocity dot norm ) * norm * (-2.0) + Velocity);
}
}

//=========================================================================
//=========================================================================
simulated function BeginState( Name PreviousStateName )
{
curAge = 0;
}
}

//=========================================================================
//=========================================================================
simulated state Resting
{
simulated function Tick( float DeltaTime )
{
curAge += DeltaTime;

if ( curAge > greaseDuration )
{
myBlob.Destroy();
Destroy();
return;
}
else if ( curAge > greaseDuration – greaseShrinkTime )
{
greaseRadius = ( greaseDuration – curAge ) / greaseShrinkTime * greaseMaxRadius;
myBlob.setSize( greaseRadius );
}
else if ( curAge > greaseGrowTime )
{
if ( greaseRadius < greaseMaxRadius )
{
greaseRadius = greaseMaxRadius;
myBlob.setSize( greaseRadius );
}
}
else
{
greaseRadius = curAge / greaseGrowTime * greaseMaxRadius;
myBlob.setSize( greaseRadius );
}
}

simulated function BeginState( Name PreviousStateName )
{
if ( Role == ROLE_Authority )
{
myBlob = Spawn( class’FoodFeud.GreaseBlob’ );

myBlob.DamageInstigator = damageInstigator;
}

curAge = 0;
}
}

//=========================================================================
//=========================================================================
replication
{
if ( Role==ROLE_Authority )
BounceRatio, maxLifespan, curAge,
greaseDuration, greaseGrowTime,
greaseShrinkTime, greaseMaxRadius,
nBounces, maxBounces, myBlob;
}

//=========================================================================
//=========================================================================
DefaultProperties
{
Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
End Object
Components(0)=MyLightEnvironment

Begin Object Class=StaticMeshComponent Name=MyStaticMesh
LightEnvironment=MyLightEnvironment
StaticMesh=StaticMesh’FoodFued.Models.fry_basket_mesh_01′
End Object
Components(1)=MyStaticMesh
CollisionComponent=MyStaticMesh;

RemoteRole=ROLE_SimulatedProxy;

bCollideWorld=true
Physics=PHYS_Falling
bStatic=False
bMovable=True
bAlwaysRelevant=true;

bBounce = true
curAge = 0
nBounces = 0;
greaseRadius = 0;

// How bouncy is it ( 0 – no bounce, 1 – crazy bouncy )
BounceRatio = 0.3

// How many times does the grease trap bounce on the ground before landing
maxBounces = 1;

// How long will it bounce around before exploding due to timeout
maxLifespan = 5

// How long is the grease ball active
greaseDuration = 13;

// How much of that time is spent growing to full size?
greaseGrowTime = 2;

greaseShrinkTime = 1;

greaseMaxRadius = 500;

}
[/code]