1 ///////////////////////////////////////////////
2 //
3 //  Snipe2d ludum dare 48h compo entry
4 //
5 //  Jari Komppa aka Sol
6 //  http://iki.fi/sol
7 //
8 ///////////////////////////////////////////////
9 // License
10 ///////////////////////////////////////////////
11 //
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.
15 //
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:
19 //
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.
27 //
28 // (eg. same as ZLIB license)
29 //
30 ///////////////////////////////////////////////
31 //
32 // Houses are taken from a satellite picture of glasgow.
33 //
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 =)
36 //
37 #include "snipe2d.h"
38 //#define DRAW_DEBUGLINES
39 
route(int x1,int y1,int x2,int y2)40 int route(int x1,int y1,int x2, int y2)
41 {
42     if ( SDL_LockSurface(Game.AIMap) < 0 )
43         return 0;
44 
45     char *t = (char*)Game.AIMap->pixels;
46 
47     int x, y;
48     int xinc;
49     int yinc;
50     int len,i;
51 
52     len = abs(x2 - x1);
53     i = abs(y2 - y1);
54     if (i > len) len = i;
55     if (len == 0) return 0;
56 
57     xinc = ((x2 - x1) << SHIFT_AMOUNT) / len;
58     yinc = ((y2 - y1) << SHIFT_AMOUNT) / len;
59 
60     x = (x1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
61     y = (y1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
62 
63     for (i = 1; i <= len; i++)
64     {
65         if ((t[(x >> SHIFT_AMOUNT) +
66                (y >> SHIFT_AMOUNT) *
67                (Game.AIMap->pitch)] & 0xff) == 1)
68             return 0;
69         x = x + xinc;
70         y = y + yinc;
71     }
72 
73     SDL_UnlockSurface(Game.AIMap);
74     return len;
75 }
76 
drawLine(SDL_Surface * aTarget,int x1,int y1,int x2,int y2,int clr)77 void drawLine(SDL_Surface * aTarget, int x1,int y1,int x2, int y2, int clr)
78 {
79     if ( SDL_LockSurface(aTarget) < 0 )
80         return;
81 
82     short *t = (short*)aTarget->pixels;
83 
84     int x, y;
85     int xinc;
86     int yinc;
87     int len,i;
88 
89     len = abs(x2 - x1);
90     i = abs(y2 - y1);
91     if (i > len) len = i;
92     if (len == 0) return;
93 
94     xinc = ((x2 - x1) << SHIFT_AMOUNT) / len;
95     yinc = ((y2 - y1) << SHIFT_AMOUNT) / len;
96 
97     x = (x1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
98     y = (y1 << SHIFT_AMOUNT) + ((1 << SHIFT_AMOUNT) / 2);
99 
100     for (i = 1; i <= len; i++)
101     {
102         t[(x >> SHIFT_AMOUNT) +
103           (y >> SHIFT_AMOUNT) *
104           (aTarget->pitch / 2)] = clr;
105 
106         x = x + xinc;
107         y = y + yinc;
108     }
109 
110     SDL_UnlockSurface(aTarget);
111 }
112 
113 
114 
precalc_ai()115 void precalc_ai()
116 {
117     if ( SDL_LockSurface(Game.AIMap) < 0 )
118         return;
119     // Count waypoints
120 
121     Game.num_waypoints = 0;
122     Game.num_spawnpoints = 0;
123     int i,j;
124     for (j = 0; j < 600; j++)
125     {
126         int ofs = j * Game.AIMap->pitch;
127         for (i = 0; i < 800; i++)
128         {
129             switch (*((char*)Game.AIMap->pixels + ofs) & 0xff)
130             {
131             case 0: // street
132                 break;
133             case 1: // house
134                 break;
135             case 2: // bad guys spawn points
136                 Game.num_spawnpoints++;
137                 Game.baddy.spawn++;
138                 break;
139             case 3: // VIP spawn points
140                 Game.num_spawnpoints++;
141                 Game.vip.spawn++;
142                 break;
143             case 4: // waypoints
144                 Game.num_waypoints++;
145                 break;
146             case 5: // neutral spawn points
147                 Game.num_spawnpoints++;
148                 Game.pedestrian.spawn++;
149                 break;
150             }
151             ofs++;
152         }
153     }
154     Game.spawnpoints= (SPAWNPOINT*)calloc(Game.num_spawnpoints, sizeof(SPAWNPOINT));
155     Game.waypoints = (WAYPOINT*)calloc(Game.num_waypoints, sizeof(WAYPOINT));
156     int waypoint = 0;
157     int spawnpoint = 0;
158     for (j = 0; j < 600; j++)
159     {
160         int ofs = j * Game.AIMap->pitch;
161         for (i = 0; i < 800; i++)
162         {
163             switch (*((char*)Game.AIMap->pixels + ofs) & 0xff)
164             {
165             case 0: // street
166                 break;
167             case 1: // house
168                 break;
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;
173                 spawnpoint++;
174                 break;
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;
179                 spawnpoint++;
180                 break;
181             case 4: // waypoints
182                 Game.waypoints[waypoint].mX = i;
183                 Game.waypoints[waypoint].mY = j;
184                 waypoint++;
185                 break;
186             case 5: // neutral spawn points
187                 Game.spawnpoints[spawnpoint].mX = i;
188                 Game.spawnpoints[spawnpoint].mY = j;
189                 Game.spawnpoints[spawnpoint].mType = 2;
190                 spawnpoint++;
191                 break;
192             }
193             ofs++;
194         }
195     }
196 
197     // Find and store connections
198 
199     for (i = 0; i < Game.num_waypoints; i++)
200     {
201         waypoint = 0;
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))
204                 waypoint++;
205 
206         Game.waypoints[i].mConnections = waypoint;
207         Game.waypoints[i].mConnection = (int*)calloc(waypoint, sizeof(int));
208 
209         waypoint = 0;
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))
212             {
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);
215 #endif
216                 Game.waypoints[i].mConnection[waypoint] = j;
217                 waypoint++;
218             }
219     }
220 
221     for (i = 0; i < Game.num_spawnpoints; i++)
222     {
223         int spawndist = 10000;
224         for (j = 0; j < Game.num_waypoints; j++)
225         {
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)
228             {
229                 spawndist = newdist;
230                 Game.spawnpoints[i].mClosestWaypoint = j;
231             }
232         }
233     }
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));
237 #endif
238 
239     SDL_UnlockSurface(Game.AIMap);
240 }
241 
distance(float aX1,float aY1,float aX2,float aY2)242 float distance(float aX1, float aY1, float aX2, float aY2)
243 {
244     return (float)sqrt((aX2 - aX1) * (aX2 - aX1) + (aY2 - aY1) * (aY2 - aY1));
245 }
246 
distance_wp(int aWaypoint,float aX,float aY)247 float distance_wp(int aWaypoint, float aX, float aY)
248 {
249     return distance((float)Game.waypoints[aWaypoint].mX, (float)Game.waypoints[aWaypoint].mY, aX, aY);
250 }
251 
distance_wpwp(int aWaypoint1,int aWaypoint2)252 float distance_wpwp(int aWaypoint1, int aWaypoint2)
253 {
254     return distance((float)Game.waypoints[aWaypoint1].mX, (float)Game.waypoints[aWaypoint1].mY, (float)Game.waypoints[aWaypoint2].mX, (float)Game.waypoints[aWaypoint2].mY);
255 }
256 
validateWaypoint(CHARACTER & c,int & next)257 void validateWaypoint(CHARACTER &c, int &next)
258 {
259     int valid = 0;
260     int candidate = next;
261     while (!valid)
262     {
263         valid = 1;
264         int i;
265         for (i = 0; i < 7; i++)
266             if (c.mLastWaypoints[i] == Game.waypoints[c.mNextWaypoint].mConnection[candidate])
267                 valid = 0;
268         if (!valid)
269         {
270             candidate++;
271             if (candidate >= Game.waypoints[c.mNextWaypoint].mConnections)
272                 candidate = 0;
273             if (candidate == next) // no valid waypoints
274                 return;
275         }
276     }
277     next = candidate;
278 }
279 
handle_ai(CHARACTER & c)280 void handle_ai(CHARACTER &c)
281 {
282     // is this AI inactive?
283     if (c.mType == -1)
284         return;
285 
286     // Kludge: hit position sign
287     if (c.mType == 3 || c.mType == 4)
288     {
289 
290         c.mTTL--;
291         if (c.mTTL < 0)
292             c.mType = -1;
293         return;
294     }
295 
296     // Pedestrian AI
297     // Pedestrians just walk around, and try not to walk
298     // in circles.
299     if (c.mType == 2)
300     {
301         float dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
302         // Have we arrived at waypoint?
303         if (dist < 4)
304         {
305 
306 #ifdef RECYCLE_PEDESTRIANS
307             // Reduce time to live..
308             c.mTTL--;
309             if (c.mTTL <= 0)
310             {
311                 // Wipe and recycle..
312                 spa(2);
313                 c.mType = -1;
314                 return;
315             }
316 #endif
317             // Store current waypoint in old waypoints list..
318             c.mLastWaypoints[c.mLastWaypoint] = c.mNextWaypoint;
319             c.mLastWaypoint++;
320             if (c.mLastWaypoint >= 7)
321                 c.mLastWaypoint = 0;
322             // Find a new waypoint
323 
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;
331 
332         }
333     }
334 
335     // VIP AI
336     // VIPs try to find their way to their exit point.
337     if (c.mType == CHAR_VIP)
338     {
339         if (c.mNextWaypoint == -1)
340         {
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);
343             if (dist < 4)
344             {
345                 // arrived safely.
346                 c.mType = -1;
347                 Game.Score += (int)((float)5000*Game.ScoreMod);
348                 Game.vip.count--;
349                 Game.vip.goal++;
350             }
351         }
352         else
353         {
354             float dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
355             // Have we arrived at waypoint?
356             if (dist < 4)
357             {
358                 // Store current waypoint in old waypoints list..
359                 c.mLastWaypoints[c.mLastWaypoint] = c.mNextWaypoint;
360                 c.mLastWaypoint++;
361                 if (c.mLastWaypoint >= 7)
362                     c.mLastWaypoint = 0;
363                 // Find a new waypoint
364 
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))
367                 {
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;
373                 }
374                 else
375                 {
376                     // Nope, try to figure out the closest waypoint to target that's connected from here
377                     int next = 0;
378                     dist = distance_wp(Game.waypoints[c.mNextWaypoint].mConnection[0], (float)Game.spawnpoints[c.mTarget].mX, (float)Game.spawnpoints[c.mTarget].mY);
379                     int i;
380                     for (i = 1; i < Game.waypoints[c.mNextWaypoint].mConnections; i++)
381                     {
382                         float newdist = distance_wp(Game.waypoints[c.mNextWaypoint].mConnection[i], (float)Game.spawnpoints[c.mTarget].mX, (float)Game.spawnpoints[c.mTarget].mY);
383                         if (newdist < dist)
384                         {
385                             dist = newdist;
386                             next = i;
387                         }
388                     }
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;
396                 }
397 
398             }
399         }
400     }
401 
402     // Bad guy AI
403     // Bad guys try to find their way to a VIP.
404     if (c.mType == CHAR_BADGUY)
405     {
406         if (c.mTarget != -1 && Game.characters[c.mTarget].mType != 1)
407         {
408             // Lost target
409             c.mTarget = -1;
410         }
411 
412         if (c.mTarget == -1) // Bad guy without a target
413         {
414             if (Game.vip.count == 0)
415             {
416                 // No VIPs to pester, walk around randomly
417 
418                 if (c.mNextWaypoint == -1)
419                 {
420                     // We were walking towards a VIP last time, so
421                     // we'll need to find the closest waypoint and walk to that.
422                     c.mNextWaypoint = 0;
423                     int i;
424                     float dist = distance_wp(0, c.mX, c.mY);
425                     for (i = 1; i < Game.num_waypoints; i++)
426                     {
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))
429                         {
430                             dist = newdist;
431                             c.mNextWaypoint = i;
432                         }
433                     }
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;
437                 }
438                 else // just walk towards the next waypoint normally
439                 {
440                     float dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
441                     // Have we arrived at waypoint?
442                     if (dist < 4)
443                     {
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;
452                     }
453                 }
454             }
455             else // target a VIP
456             {
457                 int t = rand() % Game.vip.count;
458                 int i = 0;
459                 while (t > 0 || Game.characters[i].mType != CHAR_VIP)
460                 {
461                     if (Game.characters[i].mType == CHAR_VIP)
462                         t--;
463                     i++;
464                 }
465                 c.mTarget = i;
466                 // Avoid sudden death:
467                 if (distance(c.mX, c.mY, Game.characters[c.mTarget].mX, Game.characters[c.mTarget].mY) < 20)
468                 {
469                     c.mTarget = -1;
470                     if (c.mNextWaypoint == -1)
471                     {
472                         c.mXi = 0;
473                         c.mYi = 0;
474                     }
475                 }
476             }
477         }
478 
479         int nolineofsight = 1;
480 
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))
483         {
484             nolineofsight = 0;
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;
490         }
491 
492 
493         if (c.mNextWaypoint == -1)
494         {
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);
497             if (dist < 3)
498             {
499                 // arrived safely.
500                 c.mType = -1;
501                 Game.Score -= (int)((float)10000*Game.ScoreMod); // +game over
502                 Game.vip.count--;
503                 Game.baddy.count--;
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);
509                 return;
510 #endif
511             }
512             else
513             {
514                 if (nolineofsight)
515                 {
516                     // Lost the VIP. Find closest accessible waypoint.
517                     c.mNextWaypoint = 0;
518                     int i;
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++)
521                     {
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))
524                         {
525                             dist = newdist;
526                             c.mNextWaypoint = i;
527                         }
528                     }
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;
533                 }
534             }
535         }
536         else
537         {
538             float dist = distance_wp(c.mNextWaypoint, c.mX, c.mY);
539             // Have we arrived at waypoint?
540             if (dist < 4)
541             {
542                 // Find a new waypoint
543 
544                 if (c.mTarget == -1)
545                 {
546 		    int oldWaypoint = c.mNextWaypoint;
547 
548 		    // just choose the next-closest waypoint in the area (I know: we'll cycle).
549                     c.mNextWaypoint = 0;
550                     int i;
551                     float dist = distance_wp(0, c.mX, c.mY);
552                     for (i = 1; i < Game.num_waypoints; i++)
553                     {
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))
557 			    {
558 				dist = newdist;
559 				c.mNextWaypoint = i;
560 			    }
561 			}
562                     }
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;
566                 }
567                 else if (nolineofsight)
568                 {
569                     // Can't see the VIP, try to figure out the closest waypoint to target that's connected from here
570                     int next = 0;
571                     dist = distance_wp(Game.waypoints[c.mNextWaypoint].mConnection[0], Game.characters[c.mTarget].mX, Game.characters[c.mTarget].mY);
572                     int i;
573                     for (i = 1; i < Game.waypoints[c.mNextWaypoint].mConnections; i++)
574                     {
575                         float newdist = distance_wp(Game.waypoints[c.mNextWaypoint].mConnection[i], Game.characters[c.mTarget].mX, Game.characters[c.mTarget].mY);
576                         if (newdist < dist)
577                         {
578                             dist = newdist;
579                             next = i;
580                         }
581                     }
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;
588                 }
589 
590             }
591         }
592     }
593 
594 
595     // Make 'em walk
596     c.mX += c.mXi;
597     c.mY += c.mYi;
598 }
599 
findspawnpoint(int aIndex,int aType)600 int findspawnpoint(int aIndex, int aType)
601 {
602     int i, j;
603     j = 0;
604     i = 0;
605     while (i < Game.num_spawnpoints)
606     {
607         if (Game.spawnpoints[i].mType == aType)
608             j++;
609         if (j > aIndex)
610             return i;
611         i++;
612     }
613     return i;
614 }
615 
spawn_ai(int aType)616 int spawn_ai(int aType)
617 {
618 
619     // find empty slot
620     int slot = 0;
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;
624     int i;
625     for (i = 0; i < 7; i++)
626         Game.characters[slot].mLastWaypoints[i] = -1;
627 
628     if (aType == CHAR_BADGUY)
629     {
630         Game.baddy.count++;
631         // spawn a bad guy
632         int spawnpoint = 0;
633         int i = rand() % Game.baddy.spawn;
634         spawnpoint = findspawnpoint(i, CHAR_BADGUY);
635 
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;
641     }
642 
643     if (aType == CHAR_VIP)
644     {
645         if (Game.vip.count >= 3)
646             return 0; // 3 vips at a time, thanks
647         Game.vip.count++;
648         // spawn a VIP
649         int spawnpoint = 0;
650         int i = rand() % Game.vip.spawn;
651         spawnpoint = findspawnpoint(i, CHAR_VIP);
652 
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;
657 
658         int targetspawnpoint = 0;
659         float dist = 0;
660         // find target waypont, avoiding free score
661         while (dist < 20)
662         {
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);
666         }
667         Game.characters[slot].mTarget = targetspawnpoint;
668     }
669 
670     if (aType == CHAR_PEDESTRIAN)
671     {
672         // spawn a pedestrian
673         int spawnpoint = 0;
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;
681     }
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) /
684     5.0f;
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;
689     return slot;
690 }
691 
692 
693 
shoot()694 void shoot()
695 {
696     Game.WobbleIndex += 2048;
697     Game.Reloading = Game.ReloadTime;
698 #ifdef CAMERA_RECOIL
699     if (Game.MouseZ < 0.25f) Game.MouseZ = 0.25f;
700 #ifndef CAMERA_STEPS
701     Game.CoordScale = Game.MouseZ;
702 #else
703     Game.CoordScale = ((int)(Game.MouseZ * 4)) / 4.0f;
704     if (Game.CoordScale < 0.05f) Game.CoordScale = 0.05f;
705 #endif
706 #endif
707     int slot = 0;
708     while (Game.characters[slot].mType != -1) slot++;
709     Game.characters[slot].mLastWaypoint = 0;
710 
711     float worldx = Game.MouseX + Game.WobbleX + Game.CenterX + 320 * Game.CoordScale;
712     float worldy = Game.MouseY + Game.WobbleY + Game.CenterY + 240 * Game.CoordScale;
713 
714     int hit = 0;
715     int gameover = 0;
716     int i;
717     for (i = 0; i < Game.num_characters; i++)
718     {
719         if (Game.characters[i].mType != -1)
720         {
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)
725             {
726                 if (Game.characters[i].mType == CHAR_BADGUY)
727                 {
728                     Game.Score += (int)((float)1000*Game.ScoreMod);
729                     Game.baddy.count--;
730                     Game.baddy.dead++;
731                 }
732                 if (Game.characters[i].mType == CHAR_VIP)
733                 {
734                     Game.Score -= (int)((float)100000*Game.ScoreMod); // +game over
735                     gameover = 1;
736                     Game.vip.count--;
737                 }
738                 if (Game.characters[i].mType == CHAR_PEDESTRIAN)
739                 {
740                     Game.pedestrian.dead++;
741                     Game.Score -= (int)((float)100*Game.ScoreMod);
742 #ifdef RECYCLE_PEDESTRIANS
743                    spawn_ai(2); // spawn a new pedestrian
744 #endif
745                 }
746                 Game.characters[i].mType = -1;
747                 hit = 1;
748             }
749         }
750     }
751 #ifdef DISPLAY_GAMEOVER_SCREEN
752     if (gameover)
753     {
754 //        gameoverscreen(1);
755         Game.GameOverReason = OESREASON_FRAG;
756 //        draw_gameoverscreen(Game.Screen);
757     }
758 #endif
759 
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;
764 }
765