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

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

	
}