~ubuntu-branches/ubuntu/hardy/orbital-eunuchs-sniper/hardy

« back to all changes in this revision

Viewing changes to src/ai.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook
  • Date: 2007-05-29 09:32:48 UTC
  • mfrom: (1.1.1 upstream) (2.1.2 gutsy)
  • Revision ID: james.westby@ubuntu.com-20070529093248-laj1bsm2dffohdf9
Tags: 1.30+svn20070601-1
Fix broken "upstream" rule to generate correctly versioned orig.tar.gz
to avoid native package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
}