1
///////////////////////////////////////////////
3
// Snipe2d ludum dare 48h compo entry
8
///////////////////////////////////////////////
10
///////////////////////////////////////////////
12
// This software is provided 'as-is', without any express or implied
13
// warranty. In no event will the authors be held liable for any damages
14
// arising from the use of this software.
16
// Permission is granted to anyone to use this software for any purpose,
17
// including commercial applications, and to alter it and redistribute it
18
// freely, subject to the following restrictions:
20
// 1. The origin of this software must not be misrepresented; you must not
21
// claim that you wrote the original software. If you use this software
22
// in a product, an acknowledgment in the product documentation would be
23
// appreciated but is not required.
24
// 2. Altered source versions must be plainly marked as such, and must not be
25
// misrepresented as being the original software.
26
// 3. This notice may not be removed or altered from any source distribution.
28
// (eg. same as ZLIB license)
30
///////////////////////////////////////////////
32
// Houses are taken from a satellite picture of glasgow.
34
// The sources are a mess, as I didn't even try to do anything
35
// really organized here.. and hey, it's a 48h compo =)
38
//#define DRAW_DEBUGLINES
40
int route(int x1,int y1,int x2, int y2)
42
if ( SDL_LockSurface(Game.AIMap) < 0 )
45
char *t = (char*)Game.AIMap->pixels;
55
if (len == 0) return 0;
57
xinc = ((x2 - x1) << SHIFT_AMOUNT) / len;
58
yinc = ((y2 - y1) << SHIFT_AMOUNT) / len;
60
x = (x1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
61
y = (y1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
63
for (i = 1; i <= len; i++)
65
if ((t[(x >> SHIFT_AMOUNT) +
67
(Game.AIMap->pitch)] & 0xff) == 1)
73
SDL_UnlockSurface(Game.AIMap);
77
void drawLine(SDL_Surface * aTarget, int x1,int y1,int x2, int y2, int clr)
79
if ( SDL_LockSurface(aTarget) < 0 )
82
short *t = (short*)aTarget->pixels;
94
xinc = ((x2 - x1) << SHIFT_AMOUNT) / len;
95
yinc = ((y2 - y1) << SHIFT_AMOUNT) / len;
97
x = (x1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
98
y = (y1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
100
for (i = 1; i <= len; i++)
102
t[(x >> SHIFT_AMOUNT) +
103
(y >> SHIFT_AMOUNT) *
104
(aTarget->pitch / 2)] = clr;
110
SDL_UnlockSurface(aTarget);
117
if ( SDL_LockSurface(Game.AIMap) < 0 )
121
Game.num_waypoints = 0;
122
Game.num_spawnpoints = 0;
124
for (j = 0; j < 600; j++)
126
int ofs = j * Game.AIMap->pitch;
127
for (i = 0; i < 800; i++)
129
switch (*((char*)Game.AIMap->pixels + ofs) & 0xff)
135
case 2: // bad guys spawn points
136
Game.num_spawnpoints++;
139
case 3: // VIP spawn points
140
Game.num_spawnpoints++;
144
Game.num_waypoints++;
146
case 5: // neutral spawn points
147
Game.num_spawnpoints++;
148
Game.pedestrian.spawn++;
154
Game.spawnpoints= (SPAWNPOINT*)calloc(Game.num_spawnpoints, sizeof(SPAWNPOINT));
155
Game.waypoints = (WAYPOINT*)calloc(Game.num_waypoints, sizeof(WAYPOINT));
158
for (j = 0; j < 600; j++)
160
int ofs = j * Game.AIMap->pitch;
161
for (i = 0; i < 800; i++)
163
switch (*((char*)Game.AIMap->pixels + ofs) & 0xff)
169
case 2: // bad guys spawn points
170
Game.spawnpoints[spawnpoint].mX = i;
171
Game.spawnpoints[spawnpoint].mY = j;
172
Game.spawnpoints[spawnpoint].mType = CHAR_BADGUY;
175
case 3: // VIP spawn points
176
Game.spawnpoints[spawnpoint].mX = i;
177
Game.spawnpoints[spawnpoint].mY = j;
178
Game.spawnpoints[spawnpoint].mType = CHAR_VIP;
182
Game.waypoints[waypoint].mX = i;
183
Game.waypoints[waypoint].mY = j;
186
case 5: // neutral spawn points
187
Game.spawnpoints[spawnpoint].mX = i;
188
Game.spawnpoints[spawnpoint].mY = j;
189
Game.spawnpoints[spawnpoint].mType = 2;
197
// Find and store connections
199
for (i = 0; i < Game.num_waypoints; i++)
202
for (j = 0; j < Game.num_waypoints; j++)
203
if (route(Game.waypoints[i].mX, Game.waypoints[i].mY, Game.waypoints[j].mX, Game.waypoints[j].mY))
206
Game.waypoints[i].mConnections = waypoint;
207
Game.waypoints[i].mConnection = (int*)calloc(waypoint, sizeof(int));
210
for (j = 0; j < Game.num_waypoints; j++)
211
if (route(Game.waypoints[i].mX, Game.waypoints[i].mY, Game.waypoints[j].mX, Game.waypoints[j].mY))
213
#ifdef DRAW_DEBUGLINES
214
drawLine(Game.Map, Game.waypoints[i].mX, Game.waypoints[i].mY, Game.waypoints[j].mX, Game.waypoints[j].mY, 0xffff);
216
Game.waypoints[i].mConnection[waypoint] = j;
221
for (i = 0; i < Game.num_spawnpoints; i++)
223
int spawndist = 10000;
224
for (j = 0; j < Game.num_waypoints; j++)
226
int newdist = route(Game.spawnpoints[i].mX, Game.spawnpoints[i].mY, Game.waypoints[j].mX, Game.waypoints[j].mY);
227
if (newdist && newdist < spawndist)
230
Game.spawnpoints[i].mClosestWaypoint = j;
234
#ifdef DRAW_DEBUGLINES
235
for (i = 0; i < Game.num_spawnpoints; i++)
236
drawLine(Game.Map, Game.spawnpoints[i].mX, Game.spawnpoints[i].mY, Game.waypoints[Game.spawnpoints[i].mClosestWaypoint].mX, Game.waypoints[Game.spawnpoints[i].mClosestWaypoint].mY, 0xf << (Game.spawnpoints[i].mType * 6));
239
SDL_UnlockSurface(Game.AIMap);
242
float distance(float aX1, float aY1, float aX2, float aY2)
244
return (float)sqrt((aX2 - aX1) * (aX2 - aX1) + (aY2 - aY1) * (aY2 - aY1));
247
float distance_wp(int aWaypoint, float aX, float aY)
249
return distance((float)Game.waypoints[aWaypoint].mX, (float)Game.waypoints[aWaypoint].mY, aX, aY);
252
float distance_wpwp(int aWaypoint1, int aWaypoint2)
254
return distance((float)Game.waypoints[aWaypoint1].mX, (float)Game.waypoints[aWaypoint1].mY, (float)Game.waypoints[aWaypoint2].mX, (float)Game.waypoints[aWaypoint2].mY);
257
void validateWaypoint(CHARACTER &c, int &next)
260
int candidate = next;
265
for (i = 0; i < 7; i++)
266
if (c.mLastWaypoints[i] == Game.waypoints[c.mNextWaypoint].mConnection[candidate])
271
if (candidate >= Game.waypoints[c.mNextWaypoint].mConnections)
273
if (candidate == next) // no valid waypoints
280
void handle_ai(CHARACTER &c)
282
// is this AI inactive?
286
// Kludge: hit position sign
287
if (c.mType == 3 || c.mType == 4)
297
// Pedestrians just walk around, and try not to walk
301
float dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
302
// Have we arrived at waypoint?
306
#ifdef RECYCLE_PEDESTRIANS
307
// Reduce time to live..
311
// Wipe and recycle..
317
// Store current waypoint in old waypoints list..
318
c.mLastWaypoints[c.mLastWaypoint] = c.mNextWaypoint;
320
if (c.mLastWaypoint >= 7)
322
// Find a new waypoint
324
int next = rand() % Game.waypoints[c.mNextWaypoint].mConnections;
325
validateWaypoint(c, next);
326
c.mNextWaypoint = Game.waypoints[c.mNextWaypoint].mConnection[next];
327
// Calculate vector..
328
dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
329
c.mXi = ((Game.waypoints[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
330
c.mYi = ((Game.waypoints[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
336
// VIPs try to find their way to their exit point.
337
if (c.mType == CHAR_VIP)
339
if (c.mNextWaypoint == -1)
341
// Have we arrived home?
342
float dist = distance((float)Game.spawnpoints[c.mTarget].mX, (float)Game.spawnpoints[c.mTarget].mY, c.mX, c.mY);
347
Game.Score += (int)((float)5000*Game.ScoreMod);
354
float dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
355
// Have we arrived at waypoint?
358
// Store current waypoint in old waypoints list..
359
c.mLastWaypoints[c.mLastWaypoint] = c.mNextWaypoint;
361
if (c.mLastWaypoint >= 7)
363
// Find a new waypoint
365
// Can we get to the final destination from here?
366
if (route((int)c.mX, (int)c.mY, Game.spawnpoints[c.mTarget].mX, Game.spawnpoints[c.mTarget].mY))
368
// Yep, calculate vector to home
369
c.mNextWaypoint = -1;
370
dist = distance((float)Game.spawnpoints[c.mTarget].mX, (float)Game.spawnpoints[c.mTarget].mY, c.mX, c.mY);
371
c.mXi = ((Game.spawnpoints[c.mTarget].mX - c.mX) / dist) * c.mSpeed;
372
c.mYi = ((Game.spawnpoints[c.mTarget].mY - c.mY) / dist) * c.mSpeed;
376
// Nope, try to figure out the closest waypoint to target that's connected from here
378
dist = distance_wp(Game.waypoints[c.mNextWaypoint].mConnection[0], (float)Game.spawnpoints[c.mTarget].mX, (float)Game.spawnpoints[c.mTarget].mY);
380
for (i = 1; i < Game.waypoints[c.mNextWaypoint].mConnections; i++)
382
float newdist = distance_wp(Game.waypoints[c.mNextWaypoint].mConnection[i], (float)Game.spawnpoints[c.mTarget].mX, (float)Game.spawnpoints[c.mTarget].mY);
389
// Make sure we're not walking in circles:
390
validateWaypoint(c, next);
391
c.mNextWaypoint = Game.waypoints[c.mNextWaypoint].mConnection[next];
392
// Calculate vector..
393
dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
394
c.mXi = ((Game.waypoints[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
395
c.mYi = ((Game.waypoints[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
403
// Bad guys try to find their way to a VIP.
404
if (c.mType == CHAR_BADGUY)
406
if (c.mTarget != -1 && Game.characters[c.mTarget].mType != 1)
412
if (c.mTarget == -1) // Bad guy without a target
414
if (Game.vip.count == 0)
416
// No VIPs to pester, walk around randomly
418
if (c.mNextWaypoint == -1)
420
// We were walking towards a VIP last time, so
421
// we'll need to find the closest waypoint and walk to that.
424
float dist = distance_wp(0, c.mX, c.mY);
425
for (i = 1; i < Game.num_waypoints; i++)
427
float newdist = distance_wp(i, c.mX, c.mY);
428
if (newdist < dist && route(Game.waypoints[i].mX, Game.waypoints[i].mY, (int)c.mX, (int)c.mY))
434
// Calculate vector towards the closest waypoint
435
c.mXi = ((Game.waypoints[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
436
c.mYi = ((Game.waypoints[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
438
else // just walk towards the next waypoint normally
440
float dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
441
// Have we arrived at waypoint?
444
int next = rand() % Game.waypoints[c.mNextWaypoint].mConnections;
445
// Bad guys have nowhere to go, so they might
446
// as well walk in circles.. (hence, no validatewaypoint)
447
c.mNextWaypoint = Game.waypoints[c.mNextWaypoint].mConnection[next];
448
// Calculate vector..
449
dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
450
c.mXi = ((Game.waypoints[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
451
c.mYi = ((Game.waypoints[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
457
int t = rand() % Game.vip.count;
459
while (t > 0 || Game.characters[i].mType != CHAR_VIP)
461
if (Game.characters[i].mType == CHAR_VIP)
466
// Avoid sudden death:
467
if (distance(c.mX, c.mY, Game.characters[c.mTarget].mX, Game.characters[c.mTarget].mY) < 20)
470
if (c.mNextWaypoint == -1)
479
int nolineofsight = 1;
481
// Do we have line of sight to the VIP?
482
if (c.mTarget != -1 && route((int)c.mX, (int)c.mY, (int)Game.characters[c.mTarget].mX, (int)Game.characters[c.mTarget].mY))
485
// Calculate new vector to it
486
float dist = distance(Game.characters[c.mTarget].mX, Game.characters[c.mTarget].mY, c.mX, c.mY);
487
c.mXi = ((Game.characters[c.mTarget].mX - c.mX) / dist) * c.mSpeed;
488
c.mYi = ((Game.characters[c.mTarget].mY - c.mY) / dist) * c.mSpeed;
489
c.mNextWaypoint = -1;
493
if (c.mNextWaypoint == -1)
495
// Caught up with the VIP?
496
float dist = distance((float)Game.characters[c.mTarget].mX, (float)Game.characters[c.mTarget].mY, c.mX, c.mY);
501
Game.Score -= (int)((float)10000*Game.ScoreMod); // +game over
504
Game.characters[c.mTarget].mType = -1;
505
#ifdef DISPLAY_GAMEOVER_SCREEN
506
// gameoverscreen(2);
507
Game.GameOverReason = OESREASON_NEGLIGENT;
508
// draw_gameoverscreen(Game.Screen);
516
// Lost the VIP. Find closest accessible waypoint.
519
float dist = distance_wp(0, Game.characters[c.mTarget].mX, Game.characters[c.mTarget].mY);
520
for (i = 1; i < Game.num_waypoints; i++)
522
float newdist = distance_wp(i, Game.characters[c.mTarget].mX, Game.characters[c.mTarget].mY);
523
if (newdist < dist && route(Game.waypoints[i].mX, Game.waypoints[i].mY, (int)c.mX, (int)c.mY))
529
// Calculate vector towards the closest waypoint
530
dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
531
c.mXi = ((Game.waypoints[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
532
c.mYi = ((Game.waypoints[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
538
float dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
539
// Have we arrived at waypoint?
542
// Find a new waypoint
546
int oldWaypoint = c.mNextWaypoint;
548
// just choose the next-closest waypoint in the area (I know: we'll cycle).
551
float dist = distance_wp(0, c.mX, c.mY);
552
for (i = 1; i < Game.num_waypoints; i++)
554
if (i != oldWaypoint) {
555
float newdist = distance_wp(i, c.mX, c.mY);
556
if (newdist < dist && route(Game.waypoints[i].mX, Game.waypoints[i].mY, (int)c.mX, (int)c.mY))
563
// Calculate vector towards the closest waypoint
564
c.mXi = ((Game.waypoints[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
565
c.mYi = ((Game.waypoints[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
567
else if (nolineofsight)
569
// Can't see the VIP, try to figure out the closest waypoint to target that's connected from here
571
dist = distance_wp(Game.waypoints[c.mNextWaypoint].mConnection[0], Game.characters[c.mTarget].mX, Game.characters[c.mTarget].mY);
573
for (i = 1; i < Game.waypoints[c.mNextWaypoint].mConnections; i++)
575
float newdist = distance_wp(Game.waypoints[c.mNextWaypoint].mConnection[i], Game.characters[c.mTarget].mX, Game.characters[c.mTarget].mY);
582
// Note: bad guys MAY run in circles.
583
c.mNextWaypoint = Game.waypoints[c.mNextWaypoint].mConnection[next];
584
// Calculate vector..
585
dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
586
c.mXi = ((Game.waypoints[c.mNextWaypoint].mX - c.mX) / dist) * c.mSpeed;
587
c.mYi = ((Game.waypoints[c.mNextWaypoint].mY - c.mY) / dist) * c.mSpeed;
600
int findspawnpoint(int aIndex, int aType)
605
while (i < Game.num_spawnpoints)
607
if (Game.spawnpoints[i].mType == aType)
616
int spawn_ai(int aType)
621
while (slot < Game.num_characters && Game.characters[slot].mType != -1) slot++;
622
Game.characters[slot].mType = -1; // Overwrite the last slot if all slots were in use
623
Game.characters[slot].mLastWaypoint = 0;
625
for (i = 0; i < 7; i++)
626
Game.characters[slot].mLastWaypoints[i] = -1;
628
if (aType == CHAR_BADGUY)
633
int i = rand() % Game.baddy.spawn;
634
spawnpoint = findspawnpoint(i, CHAR_BADGUY);
636
Game.characters[slot].mType = CHAR_BADGUY;
637
Game.characters[slot].mX = (float)Game.spawnpoints[spawnpoint].mX;
638
Game.characters[slot].mY = (float)Game.spawnpoints[spawnpoint].mY;
639
Game.characters[slot].mTarget = -1; // find target at next handle_ai pass
640
Game.characters[slot].mNextWaypoint = Game.spawnpoints[spawnpoint].mClosestWaypoint;
643
if (aType == CHAR_VIP)
645
if (Game.vip.count >= 3)
646
return 0; // 3 vips at a time, thanks
650
int i = rand() % Game.vip.spawn;
651
spawnpoint = findspawnpoint(i, CHAR_VIP);
653
Game.characters[slot].mType = CHAR_VIP;
654
Game.characters[slot].mX = (float)Game.spawnpoints[spawnpoint].mX;
655
Game.characters[slot].mY = (float)Game.spawnpoints[spawnpoint].mY;
656
Game.characters[slot].mNextWaypoint = Game.spawnpoints[spawnpoint].mClosestWaypoint;
658
int targetspawnpoint = 0;
660
// find target waypont, avoiding free score
663
i = rand() % Game.vip.spawn;
664
targetspawnpoint = findspawnpoint(i, 1);
665
dist = distance(Game.characters[slot].mX, Game.characters[slot].mY, (float)Game.spawnpoints[targetspawnpoint].mX, (float)Game.spawnpoints[targetspawnpoint].mY);
667
Game.characters[slot].mTarget = targetspawnpoint;
670
if (aType == CHAR_PEDESTRIAN)
672
// spawn a pedestrian
674
int i = rand() % Game.pedestrian.spawn;
675
spawnpoint = findspawnpoint(i, CHAR_PEDESTRIAN);
676
Game.characters[slot].mType = CHAR_PEDESTRIAN;
677
Game.characters[slot].mX = (float)Game.spawnpoints[spawnpoint].mX;
678
Game.characters[slot].mY = (float)Game.spawnpoints[spawnpoint].mY;
679
Game.characters[slot].mTTL = rand() % 10 + 5;
680
Game.characters[slot].mNextWaypoint = Game.spawnpoints[spawnpoint].mClosestWaypoint;
682
float dist = distance_wp(Game.characters[slot].mNextWaypoint, Game.characters[slot].mX, Game.characters[slot].mY);
683
Game.characters[slot].mSpeed = (((((rand()%32768)/32768.0)) + 0.5f) * 0.5f) /
685
// slow down pedestrians, they're not in a hurry..
686
if (aType == CHAR_PEDESTRIAN) Game.characters[slot].mSpeed *= 0.5f;
687
Game.characters[slot].mXi = ((Game.waypoints[Game.characters[slot].mNextWaypoint].mX - Game.characters[slot].mX) / dist) * Game.characters[slot].mSpeed;
688
Game.characters[slot].mYi = ((Game.waypoints[Game.characters[slot].mNextWaypoint].mY - Game.characters[slot].mY) / dist) * Game.characters[slot].mSpeed;
696
Game.WobbleIndex += 2048;
697
Game.Reloading = Game.ReloadTime;
699
if (Game.MouseZ < 0.25f) Game.MouseZ = 0.25f;
701
Game.CoordScale = Game.MouseZ;
703
Game.CoordScale = ((int)(Game.MouseZ * 4)) / 4.0f;
704
if (Game.CoordScale < 0.05f) Game.CoordScale = 0.05f;
708
while (Game.characters[slot].mType != -1) slot++;
709
Game.characters[slot].mLastWaypoint = 0;
711
float worldx = Game.MouseX + Game.WobbleX + Game.CenterX + 320 * Game.CoordScale;
712
float worldy = Game.MouseY + Game.WobbleY + Game.CenterY + 240 * Game.CoordScale;
717
for (i = 0; i < Game.num_characters; i++)
719
if (Game.characters[i].mType != -1)
721
if (Game.characters[i].mX > worldx - 1 &&
722
Game.characters[i].mX < worldx + 1 &&
723
Game.characters[i].mY > worldy - 1 &&
724
Game.characters[i].mY < worldy + 1)
726
if (Game.characters[i].mType == CHAR_BADGUY)
728
Game.Score += (int)((float)1000*Game.ScoreMod);
732
if (Game.characters[i].mType == CHAR_VIP)
734
Game.Score -= (int)((float)100000*Game.ScoreMod); // +game over
738
if (Game.characters[i].mType == CHAR_PEDESTRIAN)
740
Game.pedestrian.dead++;
741
Game.Score -= (int)((float)100*Game.ScoreMod);
742
#ifdef RECYCLE_PEDESTRIANS
743
spawn_ai(2); // spawn a new pedestrian
746
Game.characters[i].mType = -1;
751
#ifdef DISPLAY_GAMEOVER_SCREEN
754
// gameoverscreen(1);
755
Game.GameOverReason = OESREASON_FRAG;
756
// draw_gameoverscreen(Game.Screen);
760
Game.characters[slot].mType = hit?3:4; // hit marker
761
Game.characters[slot].mX = worldx;
762
Game.characters[slot].mY = worldy;
763
Game.characters[slot].mTTL = 100;