home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2002 November
/
Chip_2002-11_cd1.bin
/
oddech
/
orbital
/
orbital.exe
/
src
/
ai.cpp
next >
Wrap
C/C++ Source or Header
|
2002-07-15
|
26KB
|
737 lines
///////////////////////////////////////////////
//
// Snipe2d ludum dare 48h compo entry
//
// Jari Komppa aka Sol
// http://iki.fi/sol
//
///////////////////////////////////////////////
// License
///////////////////////////////////////////////
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// (eg. same as ZLIB license)
//
///////////////////////////////////////////////
//
// Houses are taken from a satellite picture of glasgow.
//
// The sources are a mess, as I didn't even try to do anything
// really organized here.. and hey, it's a 48h compo =)
//
#include "snipe2d.h"
//#define DRAW_DEBUGLINES
int route(int x1,int y1,int x2, int y2)
{
if ( SDL_LockSurface(gAIMap) < 0 )
return 0;
char *t = (char*)gAIMap->pixels;
int x, y;
int xinc;
int yinc;
int len,i;
len = abs(x2 - x1);
i = abs(y2 - y1);
if (i > len) len = i;
if (len == 0) return 0;
xinc = ((x2 - x1) << SHIFT_AMOUNT) / len;
yinc = ((y2 - y1) << SHIFT_AMOUNT) / len;
x = (x1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
y = (y1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
for (i = 1; i <= len; i++)
{
if ((t[(x >> SHIFT_AMOUNT) +
(y >> SHIFT_AMOUNT) *
(gAIMap->pitch)] & 0xff) == 1)
return 0;
x = x + xinc;
y = y + yinc;
}
SDL_UnlockSurface(gAIMap);
return len;
}
void drawLine(SDL_Surface * aTarget, int x1,int y1,int x2, int y2, int clr)
{
if ( SDL_LockSurface(aTarget) < 0 )
return;
short *t = (short*)aTarget->pixels;
int x, y;
int xinc;
int yinc;
int len,i;
len = abs(x2 - x1);
i = abs(y2 - y1);
if (i > len) len = i;
if (len == 0) return;
xinc = ((x2 - x1) << SHIFT_AMOUNT) / len;
yinc = ((y2 - y1) << SHIFT_AMOUNT) / len;
x = (x1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
y = (y1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
for (i = 1; i <= len; i++)
{
t[(x >> SHIFT_AMOUNT) +
(y >> SHIFT_AMOUNT) *
(aTarget->pitch / 2)] = clr;
x = x + xinc;
y = y + yinc;
}
SDL_UnlockSurface(aTarget);
}
void precalc_ai()
{
if ( SDL_LockSurface(gAIMap) < 0 )
return;
// Count waypoints
gWaypointCount = 0;
gSpawnpointCount = 0;
int i,j;
for (j = 0; j < 600; j++)
{
int ofs = j * gAIMap->pitch;
for (i = 0; i < 800; i++)
{
switch (*((char*)gAIMap->pixels + ofs) & 0xff)
{
case 0: // street
break;
case 1: // house
break;
case 2: // bad guys spawn points
gSpawnpointCount++;
gBadGuySpawnCount++;
break;
case 3: // VIP spawn points
gSpawnpointCount++;
gVIPSpawnCount++;
break;
case 4: // waypoints
gWaypointCount++;
break;
case 5: // neutral spawn points
gSpawnpointCount++;
gPedestrianSpawnCount++;
break;
}
ofs++;
}
}
gSpawnpoint = new SPAWNPOINT[gSpawnpointCount];
gWaypoint = new WAYPOINT[gWaypointCount];
int waypoint = 0;
int spawnpoint = 0;
for (j = 0; j < 600; j++)
{
int ofs = j * gAIMap->pitch;
for (i = 0; i < 800; i++)
{
switch (*((char*)gAIMap->pixels + ofs) & 0xff)
{
case 0: // street
break;
case 1: // house
break;
case 2: // bad guys spawn points
gSpawnpoint[spawnpoint].mX = i;
gSpawnpoint[spawnpoint].mY = j;
gSpawnpoint[spawnpoint].mType = CHAR_BADGUY;
spawnpoint++;
break;
case 3: // VIP spawn points
gSpawnpoint[spawnpoint].mX = i;
gSpawnpoint[spawnpoint].mY = j;
gSpawnpoint[spawnpoint].mType = CHAR_VIP;
spawnpoint++;
break;
case 4: // waypoints
gWaypoint[waypoint].mX = i;
gWaypoint[waypoint].mY = j;
waypoint++;
break;
case 5: // neutral spawn points
gSpawnpoint[spawnpoint].mX = i;
gSpawnpoint[spawnpoint].mY = j;
gSpawnpoint[spawnpoint].mType = 2;
spawnpoint++;
break;
}
ofs++;
}
}
// Find and store connections
for (i = 0; i < gWaypointCount; i++)
{
waypoint = 0;
for (j = 0; j < gWaypointCount; j++)
if (route(gWaypoint[i].mX, gWaypoint[i].mY, gWaypoint[j].mX, gWaypoint[j].mY))
waypoint++;
gWaypoint[i].mConnections = waypoint;
gWaypoint[i].mConnection = new int[waypoint];
waypoint = 0;
for (j = 0; j < gWaypointCount; j++)
if (route(gWaypoint[i].mX, gWaypoint[i].mY, gWaypoint[j].mX, gWaypoint[j].mY))
{
#ifdef DRAW_DEBUGLINES
drawLine(gMap, gWaypoint[i].mX, gWaypoint[i].mY, gWaypoint[j].mX, gWaypoint[j].mY, 0xffff);
#endif
gWaypoint[i].mConnection[waypoint] = j;
waypoint++;
}
}
for (i = 0; i < gSpawnpointCount; i++)
{
int spawndist = 10000;
for (j = 0; j < gWaypointCount; j++)
{
int newdist = route(gSpawnpoint[i].mX, gSpawnpoint[i].mY, gWaypoint[j].mX, gWaypoint[j].mY);
if (newdist && newdist < spawndist)
{
spawndist = newdist;
gSpawnpoint[i].mClosestWaypoint = j;
}
}
}
#ifdef DRAW_DEBUGLINES
for (i = 0; i < gSpawnpointCount; i++)
drawLine(gMap, gSpawnpoint[i].mX, gSpawnpoint[i].mY, gWaypoint[gSpawnpoint[i].mClosestWaypoint].mX, gWaypoint[gSpawnpoint[i].mClosestWaypoint].mY, 0xf << (gSpawnpoint[i].mType * 6));
#endif
SDL_UnlockSurface(gAIMap);
}
float distance(float aX1, float aY1, float aX2, float aY2)
{
return (float)sqrt((aX2 - aX1) * (aX2 - aX1) + (aY2 - aY1) * (aY2 - aY1));
}
float distance(int aWaypoint, float aX, float aY)
{
return distance((float)gWaypoint[aWaypoint].mX, (float)gWaypoint[aWaypoint].mY, aX, aY);
}
float distance(int aWaypoint1, int aWaypoint2)
{
return distance((float)gWaypoint[aWaypoint1].mX, (float)gWaypoint[aWaypoint1].mY, (float)gWaypoint[aWaypoint2].mX, (float)gWaypoint[aWaypoint2].mY);
}
void validateWaypoint(CHARACTER &c, int &next)
{
int valid = 0;
int candidate = next;
while (!valid)
{
valid = 1;
int i;
for (i = 0; i < 7; i++)
if (c.mLastWaypoints[i] == gWaypoint[c.mNextWaypoint].mConnection[candidate])
valid = 0;
if (!valid)
{
candidate++;
if (candidate >= gWaypoint[c.mNextWaypoint].mConnections)
candidate = 0;
if (candidate == next) // no valid waypoints
return;
}
}
next = candidate;
}
void handle_ai(CHARACTER &c)
{
// is this AI inactive?
if (c.mType == -1)
return;
// Kludge: hit position sign
if (c.mType == 3 || c.mType == 4)
{
c.mTTL--;
if (c.mTTL < 0)
c.mType = -1;
return;
}
// Pedestrian AI
// Pedestrians just walk around, and try not to walk
// in circles.
if (c.mType == 2)
{
float dist = distance(c.mNextWaypoint, c.mX, c.mY);
// Have we arrived at waypoint?
if (dist < 4)
{
#ifdef RECYCLE_PEDESTRIANS
// Reduce time to live..
c.mTTL--;
if (c.mTTL <= 0)
{
// Wipe and recycle..
spa(2);
c.mType = -1;
return;
}
#endif
// Store current waypoint in old waypoints list..
c.mLastWaypoints[c.mLastWaypoint] = c.mNextWaypoint;
c.mLastWaypoint++;
if (c.mLastWaypoint >= 7)
c.mLastWaypoint = 0;
// Find a new waypoint
int next = rand() % gWaypoint[c.mNextWaypoint].mConnections;
validateWaypoint(c, next);
c.mNextWaypoint = gWaypoint[c.mNextWaypoint].mConnection[next];
// Calculate vector..
dist = distance(c.mNextWaypoint, c.mX, c.mY);
c.mXi = ((gWaypoint[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
c.mYi = ((gWaypoint[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
}
}
// VIP AI
// VIPs try to find their way to their exit point.
if (c.mType == CHAR_VIP)
{
if (c.mNextWaypoint == -1)
{
// Have we arrived home?
float dist = distance((float)gSpawnpoint[c.mTarget].mX, (float)gSpawnpoint[c.mTarget].mY, c.mX, c.mY);
if (dist < 4)
{
// arrived safely.
c.mType = -1;
gScore += 5000;
gVIPCount--;
gVIPsGottenToSafety++;
}
}
else
{
float dist = distance(c.mNextWaypoint, c.mX, c.mY);
// Have we arrived at waypoint?
if (dist < 4)
{
// Store current waypoint in old waypoints list..
c.mLastWaypoints[c.mLastWaypoint] = c.mNextWaypoint;
c.mLastWaypoint++;
if (c.mLastWaypoint >= 7)
c.mLastWaypoint = 0;
// Find a new waypoint
// Can we get to the final destination from here?
if (route((int)c.mX, (int)c.mY, gSpawnpoint[c.mTarget].mX, gSpawnpoint[c.mTarget].mY))
{
// Yep, calculate vector to home
c.mNextWaypoint = -1;
dist = distance((float)gSpawnpoint[c.mTarget].mX, (float)gSpawnpoint[c.mTarget].mY, c.mX, c.mY);
c.mXi = ((gSpawnpoint[c.mTarget].mX - c.mX) / dist) * c.mSpeed;
c.mYi = ((gSpawnpoint[c.mTarget].mY - c.mY) / dist) * c.mSpeed;
}
else
{
// Nope, try to figure out the closest waypoint to target that's connected from here
int next = 0;
dist = distance(gWaypoint[c.mNextWaypoint].mConnection[0], (float)gSpawnpoint[c.mTarget].mX, (float)gSpawnpoint[c.mTarget].mY);
int i;
for (i = 1; i < gWaypoint[c.mNextWaypoint].mConnections; i++)
{
float newdist = distance(gWaypoint[c.mNextWaypoint].mConnection[i], (float)gSpawnpoint[c.mTarget].mX, (float)gSpawnpoint[c.mTarget].mY);
if (newdist < dist)
{
dist = newdist;
next = i;
}
}
// Make sure we're not walking in circles:
validateWaypoint(c, next);
c.mNextWaypoint = gWaypoint[c.mNextWaypoint].mConnection[next];
// Calculate vector..
dist = distance(c.mNextWaypoint, c.mX, c.mY);
c.mXi = ((gWaypoint[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
c.mYi = ((gWaypoint[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
}
}
}
}
// Bad guy AI
// Bad guys try to find their way to a VIP.
if (c.mType == CHAR_BADGUY)
{
if (c.mTarget != -1 && gCharacter[c.mTarget].mType != 1)
{
// Lost target
c.mTarget = -1;
}
if (c.mTarget == -1) // Bad guy without a target
{
if (gVIPCount == 0)
{
// No VIPs to pester, walk around randomly
if (c.mNextWaypoint == -1)
{
// We were walking towards a VIP last time, so
// we'll need to find the closest waypoint and walk to that.
c.mNextWaypoint = 0;
int i;
float dist = distance(0, c.mX, c.mY);
for (i = 1; i < gWaypointCount; i++)
{
float newdist = distance(i, c.mX, c.mY);
if (newdist < dist && route(gWaypoint[i].mX, gWaypoint[i].mY, (int)c.mX, (int)c.mY))
{
dist = newdist;
c.mNextWaypoint = i;
}
}
// Calculate vector towards the closest waypoint
c.mXi = ((gWaypoint[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
c.mYi = ((gWaypoint[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
}
else // just walk towards the next waypoint normally
{
float dist = distance(c.mNextWaypoint, c.mX, c.mY);
// Have we arrived at waypoint?
if (dist < 4)
{
int next = rand() % gWaypoint[c.mNextWaypoint].mConnections;
// Bad guys have nowhere to go, so they might
// as well walk in circles.. (hence, no validatewaypoint)
c.mNextWaypoint = gWaypoint[c.mNextWaypoint].mConnection[next];
// Calculate vector..
dist = distance(c.mNextWaypoint, c.mX, c.mY);
c.mXi = ((gWaypoint[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
c.mYi = ((gWaypoint[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
}
}
}
else // target a VIP
{
int t = rand() % gVIPCount;
int i = 0;
while (t > 0 || gCharacter[i].mType != CHAR_VIP)
{
if (gCharacter[i].mType == CHAR_VIP)
t--;
i++;
}
c.mTarget = i;
// Avoid sudden death:
if (distance(c.mX, c.mY, gCharacter[c.mTarget].mX, gCharacter[c.mTarget].mY) < 20)
{
c.mTarget = -1;
if (c.mNextWaypoint == -1)
{
c.mXi = 0;
c.mYi = 0;
}
}
}
}
int nolineofsight = 1;
// Do we have line of sight to the VIP?
if (route((int)c.mX, (int)c.mY, (int)gCharacter[c.mTarget].mX, (int)gCharacter[c.mTarget].mY))
{
nolineofsight = 0;
// Calculate new vector to it
float dist = distance(gCharacter[c.mTarget].mX, gCharacter[c.mTarget].mY, c.mX, c.mY);
c.mXi = ((gCharacter[c.mTarget].mX - c.mX) / dist) * c.mSpeed;
c.mYi = ((gCharacter[c.mTarget].mY - c.mY) / dist) * c.mSpeed;
c.mNextWaypoint = -1;
}
if (c.mNextWaypoint == -1)
{
// Caught up with the VIP?
float dist = distance((float)gCharacter[c.mTarget].mX, (float)gCharacter[c.mTarget].mY, c.mX, c.mY);
if (dist < 3)
{
// arrived safely.
c.mType = -1;
gScore -= 10000; // +game over
gVIPCount--;
gBadGuyCount--;
gCharacter[c.mTarget].mType = -1;
#ifdef DISPLAY_GAMEOVER_SCREEN
gameoverscreen(2);
return;
#endif
}
else
{
if (nolineofsight)
{
// Lost the VIP. Find closest accessible waypoint.
c.mNextWaypoint = 0;
int i;
float dist = distance(0, gCharacter[c.mTarget].mX, gCharacter[c.mTarget].mY);
for (i = 1; i < gWaypointCount; i++)
{
float newdist = distance(i, gCharacter[c.mTarget].mX, gCharacter[c.mTarget].mY);
if (newdist < dist && route(gWaypoint[i].mX, gWaypoint[i].mY, (int)c.mX, (int)c.mY))
{
dist = newdist;
c.mNextWaypoint = i;
}
}
// Calculate vector towards the closest waypoint
dist = distance(c.mNextWaypoint, c.mX, c.mY);
c.mXi = ((gWaypoint[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
c.mYi = ((gWaypoint[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
}
}
}
else
{
float dist = distance(c.mNextWaypoint, c.mX, c.mY);
// Have we arrived at waypoint?
if (dist < 4)
{
// Find a new waypoint
if (nolineofsight)
{
// Can't see the VIP, try to figure out the closest waypoint to target that's connected from here
int next = 0;
dist = distance(gWaypoint[c.mNextWaypoint].mConnection[0], gCharacter[c.mTarget].mX, gCharacter[c.mTarget].mY);
int i;
for (i = 1; i < gWaypoint[c.mNextWaypoint].mConnections; i++)
{
float newdist = distance(gWaypoint[c.mNextWaypoint].mConnection[i], gCharacter[c.mTarget].mX, gCharacter[c.mTarget].mY);
if (newdist < dist)
{
dist = newdist;
next = i;
}
}
// Note: bad guys MAY run in circles.
c.mNextWaypoint = gWaypoint[c.mNextWaypoint].mConnection[next];
// Calculate vector..
dist = distance(c.mNextWaypoint, c.mX, c.mY);
c.mXi = ((gWaypoint[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
c.mYi = ((gWaypoint[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
}
}
}
}
// Make 'em walk
c.mX += c.mXi;
c.mY += c.mYi;
}
int findspawnpoint(int aIndex, int aType)
{
int i, j;
j = 0;
i = 0;
while (i < gSpawnpointCount)
{
if (gSpawnpoint[i].mType == aType)
j++;
if (j > aIndex)
return i;
i++;
}
return i;
}
int spawn_ai(int aType)
{
// find empty slot
int slot = 0;
while (slot < gCharacterCount && gCharacter[slot].mType != -1) slot++;
gCharacter[slot].mType = -1; // Overwrite the last slot if all slots were in use
gCharacter[slot].mLastWaypoint = 0;
int i;
for (i = 0; i < 7; i++)
gCharacter[slot].mLastWaypoints[i] = -1;
if (aType == CHAR_BADGUY)
{
gBadGuyCount++;
// spawn a bad guy
int spawnpoint = 0;
int i = rand() % gBadGuySpawnCount;
spawnpoint = findspawnpoint(i, CHAR_BADGUY);
gCharacter[slot].mType = CHAR_BADGUY;
gCharacter[slot].mX = (float)gSpawnpoint[spawnpoint].mX;
gCharacter[slot].mY = (float)gSpawnpoint[spawnpoint].mY;
gCharacter[slot].mTarget = -1; // find target at next handle_ai pass
gCharacter[slot].mNextWaypoint = gSpawnpoint[spawnpoint].mClosestWaypoint;
}
if (aType == CHAR_VIP)
{
if (gVIPCount >= 3)
return 0; // 3 vips at a time, thanks
gVIPCount++;
// spawn a VIP
int spawnpoint = 0;
int i = rand() % gVIPSpawnCount;
spawnpoint = findspawnpoint(i, CHAR_VIP);
gCharacter[slot].mType = CHAR_VIP;
gCharacter[slot].mX = (float)gSpawnpoint[spawnpoint].mX;
gCharacter[slot].mY = (float)gSpawnpoint[spawnpoint].mY;
gCharacter[slot].mNextWaypoint = gSpawnpoint[spawnpoint].mClosestWaypoint;
int targetspawnpoint = 0;
float dist = 0;
// find target waypont, avoiding free score
while (dist < 20)
{
i = rand() % gVIPSpawnCount;
targetspawnpoint = findspawnpoint(i, 1);
dist = distance(gCharacter[slot].mX, gCharacter[slot].mY, (float)gSpawnpoint[targetspawnpoint].mX, (float)gSpawnpoint[targetspawnpoint].mY);
}
gCharacter[slot].mTarget = targetspawnpoint;
}
if (aType == CHAR_PEDESTRIAN)
{
// spawn a pedestrian
int spawnpoint = 0;
int i = rand() % gPedestrianSpawnCount;
spawnpoint = findspawnpoint(i, CHAR_PEDESTRIAN);
gCharacter[slot].mType = CHAR_PEDESTRIAN;
gCharacter[slot].mX = (float)gSpawnpoint[spawnpoint].mX;
gCharacter[slot].mY = (float)gSpawnpoint[spawnpoint].mY;
gCharacter[slot].mTTL = rand() % 10 + 5;
gCharacter[slot].mNextWaypoint = gSpawnpoint[spawnpoint].mClosestWaypoint;
}
float dist = distance(gCharacter[slot].mNextWaypoint, gCharacter[slot].mX, gCharacter[slot].mY);
gCharacter[slot].mSpeed = (((rand()/32768.0f) + 0.5f) * 0.5f) / 5.0f; // HatConstant(tm)
// slow down pedestrians, they're not in a hurry..
if (aType == CHAR_PEDESTRIAN) gCharacter[slot].mSpeed *= 0.5f;
gCharacter[slot].mXi = ((gWaypoint[gCharacter[slot].mNextWaypoint].mX - gCharacter[slot].mX) / dist) * gCharacter[slot].mSpeed;
gCharacter[slot].mYi = ((gWaypoint[gCharacter[slot].mNextWaypoint].mY - gCharacter[slot].mY) / dist) * gCharacter[slot].mSpeed;
return slot;
}
void shoot()
{
gWobbleIndex += 2048;
gReloading = RELOAD_TIME;
#ifdef CAMERA_RECOIL
if (gMouseZ < 0.25f) gMouseZ = 0.25f;
#ifndef CAMERA_STEPS
gCoordScale = gMouseZ;
#else
gCoordScale = ((int)(gMouseZ * 4)) / 4.0f;
if (gCoordScale < 0.05f) gCoordScale = 0.05f;
#endif
#endif
int slot = 0;
while (gCharacter[slot].mType != -1) slot++;
gCharacter[slot].mLastWaypoint = 0;
float worldx = gMouseX + gWobbleX + gCenterX + 320 * gCoordScale;
float worldy = gMouseY + gWobbleY + gCenterY + 240 * gCoordScale;
int hit = 0;
int gameover = 0;
int i;
for (i = 0; i < gCharacterCount; i++)
{
if (gCharacter[i].mType != -1)
{
if (gCharacter[i].mX > worldx - 1 &&
gCharacter[i].mX < worldx + 1 &&
gCharacter[i].mY > worldy - 1 &&
gCharacter[i].mY < worldy + 1)
{
if (gCharacter[i].mType == CHAR_BADGUY)
{
gScore += 1000;
gBadGuyCount--;
gBadGuisKilled++;
}
if (gCharacter[i].mType == CHAR_VIP)
{
gScore -= 100000; // +game over
gameover = 1;
gVIPCount--;
}
if (gCharacter[i].mType == CHAR_PEDESTRIAN)
{
gPedestriansKilled++;
gScore -= 100;
#ifdef RECYCLE_PEDESTRIANS
spawn_ai(2); // spawn a new pedestrian
#endif
}
gCharacter[i].mType = -1;
hit = 1;
}
}
}
#ifdef DISPLAY_GAMEOVER_SCREEN
if (gameover)
{
gameoverscreen(1);
}
#endif
gCharacter[slot].mType = hit?3:4; // hit marker
gCharacter[slot].mX = worldx;
gCharacter[slot].mY = worldy;
gCharacter[slot].mTTL = 100;
}