View difference between Paste ID: vBteuwA7 and rSrWdYJn
SHOW: | | - or go back to the newest paste.
1
// ================================================================
2
//  rTurret
3-
//      Version 1.0
3+
//      Version 1.1
4
//      Created for SA-MP 0.3z
5
//      © 2014 by Corekt/robotmonkey
6
//
7
//  Changelog:
8
//      Version 1.1 (7/19/2014)
9
//          Added ToggleTurretTargetFocus,
10
//          Added GetTurretOwner, SetTurretOwner,
11
//          Added SetTurretMaterial, GetTurretMaterial
12
//          Added definitions for British English spelling
13-
//      easily create and customize interactive turrets to your
13+
//          Minor bug fixes
14-
//      liking.
14+
15
//          First release
16
//
17
//  Description:
18
//      Designed with flexibility in mind, rTurret allows you to
19
//      easily create and customize interactive turrets.
20
//
21
//  Installation:
22
//      Requires -
23
//          Streamer Plugin - http://forum.sa-mp.com/showthread.php?t=102865
24
//          foreach include - http://forum.sa-mp.com/showthread.php?t=92679
25
//      If using this in a filterscript, define FILTERSCRIPT
26
//      before including this file.
27
//
28
//  Thanks to:
29
//      Y_Less - foreach
30
//      Incognito - Streamer Plugin
31
//      Kye/Kalcor and the SA-MP Team - SA-MP
32
//
33
//  Core Functions:
34
//      CreateTurret(behavior = TURRET_BEHAVIOR_STATIONARY, playerid = INVALID_PLAYER_ID, Float:x = 0.0, Float:y = 0.0, Float:z = 0.0, Float:rx = 0.0, Float:ry = 0.0, Float:rz = 1000.0, world = -1, interior = -1, rate = 100, Float:speed = 20.0, Float:dmg = 25.0, Float:range = 50.0, Float:streamdistance = 200.0);
35
//      DestroyTurret(turretid, &iter_next = -1);
36
//      IsValidTurret(turretid);
37
//
38
//  Callbacks:
39
//      OnTurretCreate(turretid);
40
//      OnTurretDestroy(turretid);
41
//      OnTurretHitPlayer(turretid, playerid);
42-
//      http://forum.sa-mp.com/showthread.php?p=3121654
42+
43
//      OnTurretMoved(turretid);
44
//      OnTurretCustomShoot(turretid);
45
//
46
//  For more documentation and updates visit:
47
//      http://forum.sa-mp.com/showthread.php?t=525988
48
// ================================================================
49
50
#if !defined _samp_included
51
    #error You must include a_samp before rTurret.
52
#endif
53
54
#include <streamer>
55
#include <foreach>
56
57
#if !defined MAX_TURRETS
58
    #define MAX_TURRETS                             (256)
59
#endif
60
61
#if !defined MIN_TURRET_RATE
62
    #define MIN_TURRET_RATE                         (100)
63
#endif
64
65
#if !defined MAX_TURRET_VISIBLE_BULLETS
66
    #define MAX_TURRET_VISIBLE_BULLETS              (32)
67
#endif
68
69
#define INVALID_TURRET                              (-1)
70
#define INVALID_TURRET_ID                           INVALID_TURRET
71
72
// Streamer data identifiers
73
// Although highly unlikely, make sure your
74
// streamer extra data don't contain these values
75
#define TURRET_INT                                  (-706637)
76
#define TURRET_BIG_AREA_INT                         (-316)
77
#define TURRET_SHOOT_AREA_INT                       (-56007)
78
#define TURRET_BULLET_AREA_INT                      (-4234)
79
#define TURRET_BULLET_OBJECT_INT                    (-801137)
80
81
// Turret moveids
82
#define TURRET_MOVE_ROTATE_CLOCKWISE_1              (-1)
83
#define TURRET_MOVE_ROTATE_CLOCKWISE_2              (-2)
84
#define TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_1       (-3)
85
#define TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_2       (-4)
86
#define TURRET_MOVE_OTHER                           (-5)
87
88
// Turret Behaviors
89
#define TURRET_BEHAVIOR_STATIONARY                  (0)
90
#define TURRET_BEHAVIOR_ROTATE_CLOCKWISE            (1)
91
#define TURRET_BEHAVIOR_ROTATE_COUNTERCLOCKWISE     (2)
92
#define TURRET_BEHAVIOR_AIM_CLOSEST                 (3)
93
#define TURRET_BEHAVIOR_AIM_FARTHEST                (4)
94
#define TURRET_BEHAVIOR_AIM_WEAKEST                 (5)
95
#define TURRET_BEHAVIOR_AIM_STRONGEST               (6)
96
#define TURRET_BEHAVIOR_AIM_RANDOM                  (7)
97
#define TURRET_BEHAVIOR_AIM_SINGLE                  (8)
98
#define TURRET_BEHAVIOR_AIM_CUSTOM                  (9)
99
#define TURRET_BEHAVIOR_MAX                         (10)
100
101
// ======================================================
102
// Callbacks
103
// ======================================================
104
forward OnTurretCreate(turretid);
105
forward OnTurretDestroy(turretid);
106
forward OnTurretHitPlayer(turretid, playerid);
107
forward OnPlayerTakeTurretDamage(playerid, turretid, Float:damage);
108
forward OnTurretMoved(turretid);
109
forward OnTurretCustomShoot(turretid);
110
111
enum E_TURRET_DATA
112
{
113
    e_turr_objectid,
114
    e_turr_behavior,
115
    e_turr_owner,
116
    e_turr_team,
117
    e_turr_world,
118
    e_turr_interior,
119
    e_turr_area,
120
    e_turr_bigarea,
121
    e_turr_rate,
122
    Text3D:e_turr_3dtext,
123
    Float:e_turr_3dtext_xoffset,
124
    Float:e_turr_3dtext_yoffset,
125
    Float:e_turr_3dtext_zoffset,
126
    Float:e_turr_3dtext_draw,
127
    e_turr_3dtext_testlos,
128
    Float:e_turr_speed,
129
    Float:e_turr_dmg,
130
    Float:e_turr_range,
131
    Float:e_turr_stream,
132
    Float:e_turr_rotspeed,
133
    Float:e_turr_bullet_radius,
134
    e_turr_bullet_model,
135
    e_turr_bullet_color,
136
    e_turr_timer,
137
    e_turr_visible_bullets,
138
    e_turr_max_visible_bullets,
139
    bool:e_turr_active,
140
    bool:e_turr_3dspace,
141
    bool:e_turr_predict,
142
    bool:e_turr_checkarea,
143
    bool:e_turr_playsound,
144
    bool:e_turr_target_focus,
145
    e_turr_soundid,
146
    e_turr_target
147
}
148
149
static
150
    rTurret_Killed[MAX_PLAYERS char],
151
    rTurret_KilledBy[MAX_PLAYERS],
152
    rTurretData[MAX_TURRETS][E_TURRET_DATA];
153
    
154
new
155
    Iterator:rTurrets<MAX_TURRETS>,
156
    Iterator:rTurretTargets[MAX_TURRETS]<MAX_PLAYERS>,
157
    Iterator:rTurretPlayersInBigArea[MAX_TURRETS]<MAX_PLAYERS>;
158
159
// ======================================================
160
// Core Functions
161
// ======================================================
162
163
stock CreateTurret(behavior = TURRET_BEHAVIOR_STATIONARY, playerid = INVALID_PLAYER_ID, Float:x = 0.0, Float:y = 0.0, Float:z = 0.0, Float:rx = 0.0, Float:ry = 0.0, Float:rz = 1000.0, world = -1, interior = -1, rate = 100, Float:speed = 20.0, Float:dmg = 25.0, Float:range = 50.0, Float:streamdistance = 200.0)
164
{
165
    new
166
        turretid = Iter_Free(rTurrets);
167
        
168
    if (turretid == -1) {
169
        printf("rTurret error: Max turrets of %d reached. Please define the #MAX_TURRETS macro to a higher value.", MAX_TURRETS);
170
        return INVALID_TURRET;
171
    }
172
    
173
    if (behavior < 0 || behavior >= TURRET_BEHAVIOR_MAX) {
174
        printf("rTurret warning: Invalid turret behavior! Turret %d's behavior has been changed from %d to TURRET_BEHAVIOR_STATIONARY (%d).", turretid, behavior, TURRET_BEHAVIOR_STATIONARY);
175
        behavior = TURRET_BEHAVIOR_STATIONARY;
176
    }
177
    
178
    if (rate < MIN_TURRET_RATE) {
179
        printf("rTurret warning: Turret %d's rate of %d has been set to MIN_TURRET_RATE %d.", turretid, rate, MIN_TURRET_RATE);
180
        rate = MIN_TURRET_RATE;
181
    }
182
    
183
    Iter_Add(rTurrets, turretid);
184
    rTurretData[turretid][e_turr_behavior] =            behavior;
185
    rTurretData[turretid][e_turr_team] =                255;
186
    rTurretData[turretid][e_turr_owner] =               playerid;
187
    rTurretData[turretid][e_turr_world] =               world;
188
    rTurretData[turretid][e_turr_interior] =            interior;
189
    rTurretData[turretid][e_turr_rate] =                rate;
190
    rTurretData[turretid][e_turr_3dtext_xoffset] =      0;
191
    rTurretData[turretid][e_turr_3dtext_yoffset] =      0;
192
    rTurretData[turretid][e_turr_3dtext_zoffset] =      0;
193
    rTurretData[turretid][e_turr_3dtext_draw] =         25;
194
    rTurretData[turretid][e_turr_3dtext_testlos] =      0;
195
    rTurretData[turretid][e_turr_speed] =               floatabs(speed);
196
    rTurretData[turretid][e_turr_dmg] =                 dmg;
197
    rTurretData[turretid][e_turr_range] =               range;
198
    rTurretData[turretid][e_turr_stream] =              streamdistance;
199
    rTurretData[turretid][e_turr_rotspeed] =            0.0005;
200
    rTurretData[turretid][e_turr_bullet_radius] =       1.2;
201
    rTurretData[turretid][e_turr_bullet_model] =        2352;
202
    rTurretData[turretid][e_turr_bullet_color] =        0;
203
    rTurretData[turretid][e_turr_max_visible_bullets] = MAX_TURRET_VISIBLE_BULLETS;
204
    rTurretData[turretid][e_turr_active] =              true;
205
    rTurretData[turretid][e_turr_3dspace] =             false;
206
    rTurretData[turretid][e_turr_predict] =             false;
207
    rTurretData[turretid][e_turr_checkarea] =           true;
208
    rTurretData[turretid][e_turr_playsound] =           true;
209
    rTurretData[turretid][e_turr_soundid] =             1130;
210
    rTurretData[turretid][e_turr_target] =              INVALID_PLAYER_ID;
211
    rTurretData[turretid][e_turr_target_focus] =        false;
212
    
213
    rTurretData[turretid][e_turr_objectid] =    CreateDynamicObject(2985, x, y, z, rx, ry, (behavior != TURRET_BEHAVIOR_ROTATE_CLOCKWISE && behavior != TURRET_BEHAVIOR_ROTATE_COUNTERCLOCKWISE) ? rz : 0.0, world, interior, .streamdistance = streamdistance);
214
    rTurretData[turretid][e_turr_area] =        CreateDynamicSphere(0, 0, 0, range, world, interior);
215
    rTurretData[turretid][e_turr_bigarea] =     CreateDynamicSphere(0, 0, 0, 60.0, world, interior);
216
    rTurretData[turretid][e_turr_3dtext] =      CreateDynamic3DTextLabel(" ", -1, x, y, z + 2, 25, .testlos = 0, .worldid = world, .interiorid = interior, .streamdistance = 50);
217
    
218
    AttachDynamicAreaToObject(rTurretData[turretid][e_turr_area], rTurretData[turretid][e_turr_objectid]);
219
    AttachDynamicAreaToObject(rTurretData[turretid][e_turr_bigarea], rTurretData[turretid][e_turr_objectid]);
220
    // Turret object array data (identifier, turretid, moveid (below))
221
    Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_INT);
222
    Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, turretid);
223
    // Shoot area array data (turretid, identifier)
224
    Streamer_AppendArrayData(STREAMER_TYPE_AREA, rTurretData[turretid][e_turr_area], E_STREAMER_EXTRA_ID, turretid);
225
    Streamer_AppendArrayData(STREAMER_TYPE_AREA, rTurretData[turretid][e_turr_area], E_STREAMER_EXTRA_ID, TURRET_SHOOT_AREA_INT);
226
    // Big area array data (turretid, identifier)
227
    Streamer_AppendArrayData(STREAMER_TYPE_AREA, rTurretData[turretid][e_turr_bigarea], E_STREAMER_EXTRA_ID, turretid);
228
    Streamer_AppendArrayData(STREAMER_TYPE_AREA, rTurretData[turretid][e_turr_bigarea], E_STREAMER_EXTRA_ID, TURRET_BIG_AREA_INT);
229
    
230
    CallLocalFunction("OnTurretCreate", "d", turretid);
231
    
232
    if (behavior == TURRET_BEHAVIOR_ROTATE_CLOCKWISE) {
233
        Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_CLOCKWISE_1);
234
        MoveDynamicObject(rTurretData[turretid][e_turr_objectid], x, y, z + 0.001, rTurretData[turretid][e_turr_rotspeed], rx, ry, -179);
235
    } else if (behavior == TURRET_BEHAVIOR_ROTATE_COUNTERCLOCKWISE) {
236
        Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_1);
237
        MoveDynamicObject(rTurretData[turretid][e_turr_objectid], x, y, z + 0.001, rTurretData[turretid][e_turr_rotspeed], rx, ry, 179);
238
    } else
239
        Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_OTHER);
240
241
    rTurretData[turretid][e_turr_timer] = SetTimerEx("Timer_TurretShoot", rate, true, "d", turretid);
242
    
243
    return turretid;
244
}
245
246
stock DestroyTurret(turretid, &iter_next = -1)
247
{
248
    if (!Iter_Contains(rTurrets, turretid))
249
        return 0;
250
        
251
    CallLocalFunction("OnTurretDestroy", "d", turretid);
252
        
253
    if (rTurretData[turretid][e_turr_timer] != -1) {
254
        KillTimer(rTurretData[turretid][e_turr_timer]);
255
        rTurretData[turretid][e_turr_timer] = -1;
256
    }
257
    
258
    //rTurretData[turretid][e_turr_visible_bullets] = -1;
259
    DestroyDynamicObject(rTurretData[turretid][e_turr_objectid]);
260
    DestroyDynamicArea(rTurretData[turretid][e_turr_area]);
261
    DestroyDynamicArea(rTurretData[turretid][e_turr_bigarea]);
262
    DestroyDynamic3DTextLabel(rTurretData[turretid][e_turr_3dtext]);
263
    rTurretData[turretid][e_turr_objectid] = INVALID_OBJECT_ID;
264
    Iter_Clear(rTurretTargets[turretid]);
265
    Iter_Clear(rTurretPlayersInBigArea[turretid]);
266
    Iter_SafeRemove(rTurrets, turretid, iter_next);
267
    return 1;
268
}
269
270
stock IsValidTurret(turretid)
271
{
272
    return Iter_Contains(rTurrets, turretid);
273
}
274
275
// ======================================================
276
// Getter Functions
277
// ======================================================
278
279
stock GetTurretObject(turretid)
280
{
281
    if (!Iter_Contains(rTurrets, turretid))
282
        return INVALID_OBJECT_ID;
283
        
284
    return rTurretData[turretid][e_turr_objectid];
285
}
286
287
stock Text3D:GetTurret3DTextLabel(turretid)
288
{
289
    if (!Iter_Contains(rTurrets, turretid))
290
        return Text3D:INVALID_3DTEXT_ID;
291
        
292
    return rTurretData[turretid][e_turr_3dtext];
293
}
294
295
stock GetTurretOwner(turretid)
296
{
297
    if (!Iter_Contains(rTurrets, turretid))
298
        return INVALID_PLAYER_ID;
299
        
300
    return rTurretData[turretid][e_turr_owner];
301
}
302
303
stock GetTurretBehavior(turretid)
304
{
305
    if (!Iter_Contains(rTurrets, turretid))
306
        return -1;
307
        
308
    return rTurretData[turretid][e_turr_behavior];
309
}
310
311
stock GetTurretTeam(turretid)
312
{
313
    if (!Iter_Contains(rTurrets, turretid))
314
        return 255;
315
        
316
    return rTurretData[turretid][e_turr_team];
317
}
318
319
stock GetTurretPos(turretid, &Float:x, &Float:y, &Float:z)
320
{
321
    if (!Iter_Contains(rTurrets, turretid))
322
        return 0;
323
        
324
    GetDynamicObjectPos(rTurretData[turretid][e_turr_objectid], x, y, z);
325
    return 1;
326
}
327
328
stock GetTurretRot(turretid, &Float:rotx, &Float:roty, &Float:rotz)
329
{
330
    if (!Iter_Contains(rTurrets, turretid))
331
        return 0;
332
        
333
    GetDynamicObjectRot(rTurretData[turretid][e_turr_objectid], rotx, roty, rotz);
334
    return 1;
335
}
336
337
stock Float:GetTurretRotSpeed(turretid)
338
{
339
    if (!Iter_Contains(rTurrets, turretid))
340
        return 0.0;
341
        
342
    return rTurretData[turretid][e_turr_rotspeed] / speed;
343
}
344
345
stock Float:GetTurretBulletRadius(turretid)
346
{
347
    if (!Iter_Contains(rTurrets, turretid))
348
        return 0.0;
349
        
350
    return rTurretData[turretid][e_turr_bullet_radius];
351
}
352
353
stock GetTurretWorld(turretid)
354
{
355
    if (!Iter_Contains(rTurrets, turretid))
356
        return -2;
357
        
358
    return rTurretData[turretid][e_turr_world];
359
}
360
361
stock GetTurretInterior(turretid)
362
{
363
    if (!Iter_Contains(rTurrets, turretid))
364
        return -2;
365
        
366
    return rTurretData[turretid][e_turr_interior];
367
}
368
369
stock GetTurretText(turretid, text[], maxlength = sizeof(text))
370
{
371
    if (!Iter_Contains(rTurrets, turretid))
372
        return 0;
373
        
374
    GetDynamic3DTextLabelText(rTurretData[turretid][e_turr_3dtext], text, sizeof(text));
375
    return 1;
376
}
377
378
stock GetTurretTextOffset(turretid, &Float:offsetx, &Float:offsety, &Float:offsetz)
379
{
380
    if (!Iter_Contains(rTurrets, turretid))
381
        return 0;
382
        
383
    offsetx = rTurretData[turretid][e_turr_3dtext_xoffset];
384
    offsety = rTurretData[turretid][e_turr_3dtext_yoffset];
385
    offsetz = rTurretData[turretid][e_turr_3dtext_zoffset];
386
    return 1;
387
}
388
389
stock GetTurretFireRate(turretid)
390
{
391
    if (!Iter_Contains(rTurrets, turretid))
392
        return 0;
393
        
394
    return rTurretData[turretid][e_turr_rate];
395
}
396
397
stock Float:GetTurretBulletSpeed(turretid)
398
{
399
    if (!Iter_Contains(rTurrets, turretid))
400
        return 0.0;
401
        
402
    return rTurretData[turretid][e_turr_speed];
403
}
404
405
stock Float:GetTurretDamage(turretid)
406
{
407
    if (!Iter_Contains(rTurrets, turretid))
408
        return 0.0;
409
        
410
    return rTurretData[turretid][e_turr_dmg];
411
}
412
413
stock Float:GetTurretRange(turretid)
414
{
415
    if (!Iter_Contains(rTurrets, turretid))
416
        return 0.0;
417
        
418
    return rTurretData[turretid][e_turr_range];
419
}
420
421
stock Float:GetTurretStreamDistance(turretid)
422
{
423
    if (!Iter_Contains(rTurrets, turretid))
424
        return 0.0;
425
        
426
    return rTurretData[turretid][e_turr_stream];
427
}
428
429
stock GetTurretMaterial(turretid, materialindex, &modelid, txdname[], texturename[], &materialcolor, maxtxdname = sizeof txdname, maxtexturename = sizeof texturename)
430
{
431
    if (!Iter_Contains(rTurrets, turretid))
432
        return 0;
433
        
434
    GetDynamicObjectMaterial(rTurretData[turretid][e_turr_objectid], materialindex, &modelid, txdname, texturename, &materialcolor, maxtxdname, maxtexturename);
435
    return 1;
436
}
437
438
stock GetTurretBulletModel(turretid)
439
{
440
    if (!Iter_Contains(rTurrets, turretid))
441
        return -1;
442
        
443
    return rTurretData[turretid][e_turr_bullet_model];
444
}
445
446
stock GetTurretBulletColor(turretid)
447
{
448
    if (!Iter_Contains(rTurrets, turretid))
449
        return -1;
450
        
451
    return rTurretData[turretid][e_turr_bullet_color];
452
}
453
454
stock GetTurretVisibleBullets(turretid)
455
{
456
    if (!Iter_Contains(rTurrtts, turretid))
457
        return 0;
458
        
459
    return rTurretData[turretid][e_turr_visible_bullets];
460
}
461
462
stock GetTurretMaxVisibleBullets(turretid)
463
{
464
    if (!Iter_Contains(rTurrets, turretid))
465
        return 0;
466
467
    return rTurretData[turretid][e_turr_max_visible_bullets];
468
}
469
470
stock GetTurretSound(turretid)
471
{
472
    if (!Iter_Contains(rTurrets, turretid))
473
        return 0;
474
        
475
    return rTurretData[turretid][e_turr_soundid];
476
}
477
478
stock GetTurretKiller(playerid)
479
{
480
    return rTurret_KilledBy[playerid];
481
}
482
483
// ======================================================
484
// Setter Functions
485
// ======================================================
486
487
stock SetTurretOwner(turretid, playerid)
488
{
489
    if (!Iter_Contains(rTurrets, turretid))
490
        return 0;
491
        
492
    if (playerid == rTurretData[turretid][e_turr_owner])
493
        return 0;
494
    else
495
        rTurretData[turretid][e_turr_owner] = playerid;
496
    return 1;
497
}
498
499
stock SetTurretBehavior(turretid, behavior)
500
{
501
    if (!Iter_Contains(rTurrets, turretid) || behavior < 0 || behavior >= TURRET_BEHAVIOR_MAX)
502
        return 0;
503
    
504
    if (IsDynamicObjectMoving(rTurretData[turretid][e_turr_objectid]))
505
        StopDynamicObject(rTurretData[turretid][e_turr_objectid]);
506
        
507
    switch(rTurretData[turretid][e_turr_behavior]) {
508
        case TURRET_BEHAVIOR_ROTATE_CLOCKWISE: {
509
            Streamer_RemoveArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_CLOCKWISE_1);
510
            Streamer_RemoveArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_CLOCKWISE_2);
511
        }
512
        case TURRET_BEHAVIOR_ROTATE_COUNTERCLOCKWISE: {
513
            Streamer_RemoveArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_1);
514
            Streamer_RemoveArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_2);
515
        }
516
        default:
517
            Streamer_RemoveArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_OTHER);
518
    }
519
    
520
    rTurretData[turretid][e_turr_behavior] = behavior;
521
    
522
    switch(behavior) {
523
        case TURRET_BEHAVIOR_ROTATE_CLOCKWISE: {
524
            new
525
                Float:turPX,
526
                Float:turPY,
527
                Float:turPZ,
528
                Float:turRX,
529
                Float:turRY,
530
                Float:turRZ;
531
                
532
            GetDynamicObjectPos(rTurretData[turretid][e_turr_objectid], turPX, turPY, turPZ);
533
            GetDynamicObjectRot(rTurretData[turretid][e_turr_objectid], turRX, turRY, turRZ);
534
            SetDynamicObjectRot(rTurretData[turretid][e_turr_objectid], turRX, turRY, 0.0);
535
            Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_CLOCKWISE_1);
536
            MoveDynamicObject(rTurretData[turretid][e_turr_objectid], turPX, turPY, turPZ + 0.001, rTurretData[turretid][e_turr_rotspeed], turRX, turRY, -179);
537
        }
538
        case TURRET_BEHAVIOR_ROTATE_COUNTERCLOCKWISE: {
539
            new
540
                Float:turPX,
541
                Float:turPY,
542
                Float:turPZ,
543
                Float:turRX,
544
                Float:turRY,
545
                Float:turRZ;
546
                
547
            GetDynamicObjectPos(rTurretData[turretid][e_turr_objectid], turPX, turPY, turPZ);
548
            GetDynamicObjectRot(rTurretData[turretid][e_turr_objectid], turRX, turRY, turRZ);
549
            SetDynamicObjectRot(rTurretData[turretid][e_turr_objectid], turRX, turRY, 0.0);
550
            Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_1);
551
            MoveDynamicObject(rTurretData[turretid][e_turr_objectid], turPX, turPY, turPZ + 0.001, rTurretData[turretid][e_turr_rotspeed], turRX, turRY, 179);
552
        }
553
        default:
554
            Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_OTHER);
555
    }
556
    return 1;
557
}
558
559
stock SetTurretTeam(turretid, team)
560
{
561
    if (!Iter_Contains(rTurrets, turretid))
562
        return 0;
563
        
564
    rTurretData[turretid][e_turr_team] = team;
565
    return 1;
566
}
567
568
stock SetTurretPos(turretid, Float:x, Float:y, Float:z)
569
{
570
    if (!Iter_Contains(rTurrets, turretid))
571
        return 0;
572
573
    SetDynamicObjectPos(rTurretData[turretid][e_turr_objectid], x, y, z);
574
    Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_X, turPX + rTurretData[turretid][e_turr_3dtext_xoffset]);
575
    Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_Y, turPY + rTurretData[turretid][e_turr_3dtext_yoffset]);
576
    Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_Z, turPZ + 2 + rTurretData[turretid][e_turr_3dtext_zoffset]);
577
    return 1;
578
}
579
580
stock SetTurretRot(turretid, Float:rotx, Float:roty, Float:rotz)
581
{
582
    if (!Iter_Contains(rTurrets, turretid))
583
        return 0;
584
585
    SetDynamicObjectRot(rTurretData[turretid][e_turr_objectid], rotx, roty, rotz);
586
    return 1;
587
}
588
589
stock SetTurretRotSpeed(turretid, Float:speed)
590
{
591
    if (!Iter_Contains(rTurrets, turretid))
592
        return 0;
593
        
594
    rTurretData[turretid][e_turr_rotspeed] = 0.0005 * floatabs(speed);
595
    return 1;
596
}
597
598
stock SetTurretBulletRadius(turretid, Float:radius)
599
{
600
    if (!Iter_Contains(rTurrets, turretid))
601
        return 0;
602
        
603
    rTurretData[turretid][e_turr_bullet_radius] = radius;
604
    return 1;
605
}
606
607
stock SetTurretWorld(turretid, world)
608
{
609
    if (!Iter_Contains(rTurrets, turretid))
610
        return 0;
611
        
612
    rTurretData[turretid][e_turr_world] = world;
613
    Streamer_SetIntData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_WORLD_ID, world);
614
    Streamer_SetIntData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_WORLD_ID, world);
615
    return 1;
616
}
617
618
stock SetTurretInterior(turretid, interior)
619
{
620
    if (!Iter_Contains(rTurrets, turretid))
621
        return 0;
622
        
623
    rTurretData[turretid][e_turr_interior] = interior;
624
    Streamer_SetIntData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_INTERIOR_ID, interior);
625
    Streamer_SetIntData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_INTERIOR_ID, interior);
626
    return 1;
627
}
628
629
stock SetTurretText(turretid, color, const text[])
630
{
631
    if (!Iter_Contains(rTurrets, turretid))
632
        return 0;
633
        
634
    UpdateDynamic3DTextLabelText(rTurretData[turretid][e_turr_3dtext], color, text);
635
    return 1;
636
}
637
638
stock SetTurretTextOffset(turretid, Float:offsetx, Float:offsety, Float:offsetz, Float:drawdistance = -1.0, testlos = -1)
639
{
640
    if (!Iter_Contains(rTurrets, turretid))
641
        return 0;
642
        
643
    new
644
        Float:turPX,
645
        Float:turPY,
646
        Float:turPZ;
647
        
648
    GetDynamicObjectPos(rTurretData[turretid][e_turr_objectid], turPX, turPY, turPZ);
649
        
650
    Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_X, turPX + offsetx);
651
    Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_Y, turPY + offsety);
652
    Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_Z, turPZ + 2 + offsetz);
653
    
654
    if (floatcmp(drawdistance, -1.0) != 0) {
655
        Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_DRAW_DISTANCE, drawdistance);
656
        Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_STREAM_DISTANCE, drawdistance + 25);
657
        rTurretData[turretid][e_turr_3dtext_draw] = drawdistance;
658
    }
659
    if (testlos != -1) {
660
        Streamer_SetIntData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_TEST_LOS, testlos);
661
        rTurretData[turretid][e_turr_3dtext_testlos] = testlos;
662
    }
663
        
664
    rTurretData[turretid][e_turr_3dtext_xoffset] = offsetx;
665
    rTurretData[turretid][e_turr_3dtext_yoffset] = offsety;
666
    rTurretData[turretid][e_turr_3dtext_zoffset] = offsetz;
667
    return 1;
668
}
669
670
stock SetTurretFireRate(turretid, rate)
671
{
672
    if (!Iter_Contains(rTurrets, turretid))
673
        return 0;
674
        
675
    if (rate < MIN_TURRET_RATE) {
676
        printf("rTurret warning: Turret %d's rate of %d has been set to MIN_TURRET_RATE %d.", turretid, rate, MIN_TURRET_RATE);
677
        rate = MIN_TURRET_RATE;
678
    }
679
    rTurretData[turretid][e_turr_rate] = rate;
680
    if (rTurretData[turretid][e_turr_timer] != -1 && rTurretData[turretid][e_turr_active]) {
681
        KillTimer(rTurretData[turretid][e_turr_timer]);
682
        rTurretData[turretid][e_turr_timer] = SetTimerEx("Timer_TurretShoot", rate, true, "d", turretid);
683
    }
684
    return 1;
685
}
686
687
stock SetTurretBulletSpeed(turretid, Float:speed)
688
{
689
    if (!Iter_Contains(rTurrets, turretid))
690
        return 0;
691
        
692
    rTurretData[turretid][e_turr_speed] = floatabs(speed);
693
    return 1;
694
}
695
696
stock SetTurretDamage(turretid, Float:damage)
697
{
698
    if (!Iter_Contains(rTurrets, turretid))
699
        return 0;
700
        
701
    rTurretData[turretid][e_turr_dmg] = damage;
702
    return 1;
703
}
704
705
stock SetTurretRange(turretid, Float:range)
706
{
707
    if (!Iter_Contains(rTurrets, turretid))
708
        return 0;
709
        
710
    rTurretData[turretid][e_turr_range] = range;
711
    return 1;
712
}
713
714
stock SetTurretStreamDistance(turretid, Float:streamdistance)
715
{
716
    if (!Iter_Contains(rTurrets, turretid))
717
        return 0;
718
        
719
    rTurretData[turretid][e_turr_stream] = streamdistance;
720
    Streamer_SetFloatData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_STREAM_DISTANCE, streamdistance);
721
    return 1;
722
}
723
724
stock SetTurretMaterial(turretid, materialindex, modelid, const txdname[], const texturename[], materialcolor = 0)
725
{
726
    if (!Iter_Contains(rTurrets, turretid))
727
        return 0;
728
        
729
    SetDynamicObjectMaterial(rTurretData[turretid][e_turr_objectid], materialindex, modelid, txdname, texturename, materialcolor);
730
    return 1;
731
}
732
733
stock SetTurretBulletModel(turretid, modelid)
734
{
735
    if (!Iter_Contains(rTurrets, turretid))
736
        return 0;
737
        
738
    rTurretData[turretid][e_turr_bullet_model] = modelid;
739
    return 1;
740
}
741
742
stock SetTurretBulletColor(turretid, color = 0)
743
{
744
    if (!Iter_Contains(rTurrets, turretid))
745
        return 0;
746
        
747
    rTurretData[turretid][e_turr_bullet_color] = color;
748
    return 1;
749
}
750
751
stock SetTurretMaxVisibleBullets(turretid, maxbullets)
752
{
753
    if (!Iter_Contains(rTurrets, turretid))
754
        return 0;
755
    if (maxbullets >= MAX_TURRET_VISIBLE_BULLETS) {
756
        printf("rTurret Warning: Max visible bullets for turret %d has been lowered from %d to MAX_TURRET_VISIBLE_BULLETS %d", turretid, maxbullets, MAX_TURRET_VISIBLE_BULLETS);
757
        maxbullets = MAX_TURRET_VISIBLE_BULLETS;
758
    } else if (maxbullets < 1) {
759
        printf("rTurret Warning: Max visible bullets for turret %d has been raised from %d to %d", turretid, maxbullets, 1);
760
        maxbullets = 1;
761
    }
762
    rTurretData[turretid][e_turr_max_visible_bullets] = maxbullets;
763
    return 1;
764
}
765
766
stock SetTurretSound(turretid, soundid)
767
{
768
    if (!Iter_Contains(rTurrets, turretid))
769
        return 0;
770
        
771
    rTurretData[turretid][e_turr_soundid] = soundid;
772
    return 1;
773
}
774
775
stock SetTurretSingleTarget(turretid, targetid)
776
{
777
    if (!Iter_Contains(rTurrets, turretid))
778
        return 0;
779
        
780
    if (!IsPlayerConnected(targetid))
781
        return 0;
782
        
783
    rTurretData[turretid][e_turr_target] = targetid;
784
    return 1;
785
}
786
787
// ======================================================
788
// Extra Functions
789
// ======================================================
790
791
stock ToggleTurretActive(turretid, toggle)
792
{
793
    if (!Iter_Contains(rTurrets, turretid))
794
        return 1;
795
        
796
    if (toggle) {
797
        if (rTurretData[turretid][e_turr_active])
798
            return 0;
799
        if (rTurretData[turretid][e_turr_timer] == -1)
800
            rTurretData[turretid][e_turr_timer] = SetTimerEx("Timer_TurretShoot", rTurretData[turretid][e_turr_rate], true, "d", turretid);
801
        else {
802
            KillTimer(rTurretData[turretid][e_turr_timer]);
803
            rTurretData[turretid][e_turr_timer] = SetTimerEx("Timer_TurretShoot", rTurretData[turretid][e_turr_rate], true, "d", turretid);
804
        }
805
        rTurretData[turretid][e_turr_active] = true;
806
    } else {
807
        if (!rTurretData[turretid][e_turr_active])
808
            return 0;
809
        if (rTurretData[turretid][e_turr_timer] != -1) {
810
            KillTimer(rTurretData[turretid][e_turr_timer]);
811
            rTurretData[turretid][e_turr_timer] = -1;
812
        }
813
        rTurretData[turretid][e_turr_active] = false;
814
    }
815
    return 1;
816
}
817
818
stock ToggleTurretCheckArea(turretid, toggle)
819
{
820
    if (!Iter_Contains(rTurrets, turretid))
821
        return 0;
822
        
823
    if (toggle) {
824
        if (rTurretData[turretid][e_turr_checkarea])
825
            return 0;
826
        rTurretData[turretid][e_turr_checkarea] = true;
827
        Streamer_RemoveArrayData(STREAMER_TYPE_AREA, rTurretData[turretid][e_turr_area], E_STREAMER_EXTRA_ID, -1);
828
        Streamer_AppendArrayData(STREAMER_TYPE_AREA, rTurretData[turretid][e_turr_area], E_STREAMER_EXTRA_ID, TURRET_SHOOT_AREA_INT);
829
    } else {
830
        if (!rTurretData[turretid][e_turr_checkarea])
831
            return 0;
832
        rTurretData[turretid][e_turr_checkarea] = false;
833
        Streamer_RemoveArrayData(STREAMER_TYPE_AREA, rTurretData[turretid][e_turr_area], E_STREAMER_EXTRA_ID, TURRET_SHOOT_AREA_INT);
834
        Streamer_AppendArrayData(STREAMER_TYPE_AREA, rTurretData[turretid][e_turr_area], E_STREAMER_EXTRA_ID, -1);
835
    }
836
    return 1;
837
}
838
839
stock ToggleTurretPlaySound(turretid, toggle)
840
{
841
    if (!Iter_Contains(rTurrets, turretid))
842
        return 0;
843
        
844
    if (toggle) {
845
        if (rTurretData[turretid][e_turr_playsound])
846
            return 0;
847
        rTurretData[turretid][e_turr_playsound] = true;
848
    } else {
849
        if (!rTurretData[turretid][e_turr_playsound])
850
            return 0;
851
        rTurretData[turretid][e_turr_playsound] = false;
852
    }
853
    return 1;
854
}
855
856
stock ToggleTurretVisible(turretid, toggle)
857
{
858
    if (!Iter_Contains(rTurrets, turretid))
859
        return 0;
860
        
861
    new
862
        Float:sd;
863
        
864
    Streamer_GetFloatData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_STREAM_DISTANCE, sd);
865
    
866
    if (toggle) {
867
        if (floatcmp(sd, rTurretData[turretid][e_turr_stream]) == 0)
868
            return 0;
869
        Streamer_SetFloatData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_STREAM_DISTANCE, rTurretData[turretid][e_turr_stream]);
870
    } else {
871
        if (floatcmp(sd, 0.0) == 0)
872
            return 0;
873
        Streamer_SetFloatData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_STREAM_DISTANCE, 0.0);
874
    }
875
    return 1;
876
}
877
878
stock ToggleTurretAim3dSpace(turretid, toggle)
879
{
880
    if (!Iter_Contains(rTurrets, turretid))
881
        return 0;
882
        
883
    if (toggle) {
884
        if (rTurretData[turretid][e_turr_3dspace])
885
            return 0;
886
        rTurretData[turretid][e_turr_3dspace] = true;
887
    } else {
888
        if (!rTurretData[turretid][e_turr_3dspace])
889
            return 0;
890
        rTurretData[turretid][e_turr_3dspace] = false;
891
    }
892
    return 1;
893
}
894
895
stock ToggleTurretPredictiveAim(turretid, toggle)
896
{
897
    if (!Iter_Contains(rTurrets, turretid))
898
        return 0;
899
        
900
    if (toggle) {
901
        if (rTurretData[turretid][e_turr_predict])
902
            return 0;
903
        rTurretData[turretid][e_turr_predict] = true;
904
    } else {
905
        if (!rTurretData[turretid][e_turr_predict])
906
            return 0;
907
        rTurretData[turretid][e_turr_predict] = false;
908
    }
909
    return 1;
910
}
911
912
stock ToggleTurretTargetFocus(turretid, toggle)
913
{
914
    if (!Iter_Contains(rTurrets, turretid))
915
        return 0;
916
        
917
    if (toggle) {
918
        if (rTurretData[turretid][e_turr_target_focus])
919
            return 0;
920
        rTurretData[turretid][e_turr_target_focus] = true;
921
    } else {
922
        if (!rTurretData[turretid][e_turr_target_focus])
923
            return 0;
924
        rTurretData[turretid][e_turr_target_focus] = false;
925
    }
926
    return 1;
927
}
928
929
stock UpdateTurretTextPos(turretid)
930
{
931
    if (!Iter_Contains(rTurrets, turretid))
932
        return 0;
933
        
934
    new
935
        Float:turPX,
936
        Float:turPY,
937
        Float:turPZ,
938
        
939
    GetDynamicObjectPos(rTurretData[turretid][e_turr_objectid], turPX, turPY, turPZ);
940
    Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_X, turPX + rTurretData[turretid][e_turr_3dtext_xoffset]);
941
    Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_Y, turPY + rTurretData[turretid][e_turr_3dtext_yoffset]);
942
    Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, rTurretData[turretid][e_turr_3dtext], E_STREAMER_Z, turPZ + 2 + rTurretData[turretid][e_turr_3dtext_zoffset]);
943
    return 1;
944
}
945
946
stock MoveTurret(turretid, Float:X, Float:Y, Float:Z, Float:Speed, Float:RotX = -1000.0, Float:RotY = -1000.0, Float:RotZ = -1000.0)
947
{
948
    if (!Iter_Contains(rTurrets, turretid) ||
949
        rTurretData[turretid][e_turr_behavior] == TURRET_BEHAVIOR_ROTATE_CLOCKWISE ||
950
        rTurretData[turretid][e_turr_behavior] == TURRET_BEHAVIOR_ROTATE_COUNTERCLOCKWISE)
951
            return 0;
952
            
953
    MoveDynamicObject(rTurretData[turretid][e_turr_objectid], X, Y, Z, Speed, RotX, RotY, RotZ);
954
    return 1;
955
}
956
957
// ======================================================
958
// Hooks
959
// ======================================================
960
961
#if defined FILTERSCRIPT
962
public OnFilterScriptInit()
963
{
964
    Iter_Init(rTurretTargets);
965
    Iter_Init(rTurretPlayersInBigArea);
966
    foreach(new i : Player) {
967
        rTurret_Killed{i} = false;
968
        rTurret_KilledBy[i] = INVALID_TURRET;
969
    }
970
    for (new i = 0; i < MAX_TURRETS; i++) {
971
        rTurretData[i][e_turr_timer] = -1;
972
        rTurretData[i][e_turr_visible_bullets] = -1;
973
        rTurretData[i][e_turr_objectid] = INVALID_OBJECT_ID;
974
    }
975
    return CallLocalFunction("rTurret_OnFilterScriptInit", "");
976
}
977
    #if defined _ALS_OnFilterScriptInit
978
        #undef OnFilterScriptInit
979
    #else
980
        #define _ALS_OnFilterScriptInit
981
    #endif
982
    #define OnFilterScriptInit rTurret_OnFilterScriptInit
983
    forward rTurret_OnFilterScriptInit();
984
    
985
public OnFilterScriptExit()
986
{
987
    foreach(new i : rTurrets)
988
        DestroyTurret(i);
989
        
990
    Streamer_DestroyAllItems(STREAMER_TYPE_OBJECT, 0);
991
    Streamer_DestroyAllItems(STREAMER_TYPE_AREA, 0);
992
    Streamer_DestroyAllItems(STREAMER_TYPE_3D_TEXT_LABEL, 0);
993
    return CallLocalFunction("rTurret_OnFilterScriptExit", "");
994
}
995
    #if defined _ALS_OnFilterScriptExit
996
        #undef OnFilterScriptExit
997
    #else
998
        #define _ALS_OnFilterScriptExit
999
    #endif
1000
    #define OnFilterScriptExit rTurret_OnFilterScriptExit
1001
    forward rTurret_OnFilterScriptExit();
1002
    
1003
#else
1004
public OnGameModeInit()
1005
{
1006
    Iter_Init(rTurretTargets);
1007
    Iter_Init(rTurretPlayersInBigArea);
1008
    foreach(new i : Player) {
1009
        rTurret_Killed{i} = false;
1010
        rTurret_KilledBy[i] = INVALID_TURRET;
1011
    }
1012
    for (new i = 0; i < MAX_TURRETS; i++) {
1013
        rTurretData[i][e_turr_timer] = -1;
1014
        rTurretData[i][e_turr_visible_bullets] = -1;
1015
        rTurretData[i][e_turr_objectid] = INVALID_OBJECT_ID;
1016
    }
1017
    return CallLocalFunction("rTurret_OnGameModeInit", "");
1018
}
1019
    #if defined _ALS_OnGameModeInit
1020
        #undef OnGameModeInit
1021
    #else
1022
        #define _ALS_OnGameModeInit
1023
    #endif
1024
    #define OnGameModeInit rTurret_OnGameModeInit
1025
    forward rTurret_OnGameModeInit();
1026
#endif
1027
1028
public OnPlayerConnect(playerid)
1029
{
1030
    rTurret_Killed{playerid} = false;
1031
    rTurret_KilledBy[playerid] = INVALID_TURRET;
1032
    return CallLocalFunction("rTurret_OnPlayerConnect", "d", playerid);
1033
}
1034
#if defined _ALS_OnPlayerConnect
1035
    #undef OnPlayerConnect
1036
#else
1037
    #define _ALS_OnPlayerConnect
1038
#endif
1039
#define OnPlayerConnect rTurret_OnPlayerConnect
1040
forward rTurret_OnPlayerConnect(playerid);
1041
1042
public OnPlayerSpawn(playerid)
1043
{
1044
    if (rTurret_Killed{playerid})
1045
        // Prevent taking turret damage upon spawning
1046
        SetTimerEx("Timer_ResetTurretKilled", 1000, false, "d", playerid);
1047
    return CallLocalFunction("rTurret_OnPlayerSpawn", "d", playerid);
1048
}
1049
#if defined _ALS_OnPlayerSpawn
1050
    #undef OnPlayerSpawn
1051
#else
1052
    #define _ALS_OnPlayerSpawn
1053
#endif
1054
#define OnPlayerSpawn rTurret_OnPlayerSpawn
1055
forward rTurret_OnPlayerSpawn(playerid);
1056
1057
public OnPlayerDeath(playerid, killerid, reason)
1058
{
1059
    if (rTurret_Killed{playerid}) {
1060
        if (Iter_Contains(rTurrets, rTurret_KilledBy[playerid])) {
1061
            new
1062
                turretid = rTurret_KilledBy[playerid];
1063
                
1064
            killerid = rTurretData[turretid][e_turr_owner];
1065
            reason = 47;
1066
            if (Iter_Contains(rTurretTargets[turretid], playerid))
1067
                Iter_Remove(rTurretTargets[turretid], playerid);
1068
        }
1069
    }
1070
    return CallLocalFunction("rTurret_OnPlayerDeath", "ddd", playerid, killerid, reason);
1071
}
1072
#if defined _ALS_OnPlayerDeath
1073
    #undef OnPlayerDeath
1074
#else
1075
    #define _ALS_OnPlayerDeath
1076
#endif
1077
#define OnPlayerDeath rTurret_OnPlayerDeath
1078
forward rTurret_OnPlayerDeath(playerid, killerid, reason);
1079
1080
public OnDynamicObjectMoved(objectid)
1081
{
1082
    if (Streamer_IsInArrayData(STREAMER_TYPE_OBJECT, objectid, E_STREAMER_EXTRA_ID, TURRET_BULLET_OBJECT_INT)) {
1083
        // Destroy bullet and its area
1084
        new
1085
            data[3],
1086
            turretid,
1087
            bullet_areaid;
1088
            
1089
        Streamer_GetArrayData(STREAMER_TYPE_OBJECT, objectid, E_STREAMER_EXTRA_ID, data);
1090
        turretid = data[1];
1091-
            bulletid;
1091+
1092
            
1093
        DestroyDynamicArea(bullet_areaid);
1094
        DestroyDynamicObject(objectid);
1095
        rTurretData[turretid][e_turr_visible_bullets]--;
1096
        return CallLocalFunction("rTurret_OnDynamicObjectMoved", "d", objectid);
1097
    } else if (Streamer_IsInArrayData(STREAMER_TYPE_OBJECT, objectid, E_STREAMER_EXTRA_ID, TURRET_INT)) {
1098
        // Turret object
1099
        new
1100
            data[3],
1101
            turretid,
1102
            Float:oX,
1103
            Float:oY,
1104
            Float:oZ,
1105
            Float:oRX,
1106
            Float:oRY,
1107
            Float:oRZ,
1108
            movestate;
1109
            
1110
        Streamer_GetArrayData(STREAMER_TYPE_OBJECT, objectid, E_STREAMER_EXTRA_ID, data);
1111
        turretid = data[1];
1112
        movestate = data[2];
1113
        GetDynamicObjectPos(rTurretData[turretid][e_turr_objectid], oX, oY, oZ);
1114
        GetDynamicObjectRot(rTurretData[turretid][e_turr_objectid], oRX, oRY, oRZ);
1115
        switch(movestate) {
1116
            case TURRET_MOVE_ROTATE_CLOCKWISE_1:{
1117
                Streamer_RemoveArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_CLOCKWISE_1);
1118
                Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_CLOCKWISE_2);
1119
                MoveDynamicObject(rTurretData[turretid][e_turr_objectid], oX, oY, oZ - 0.001, rTurretData[turretid][e_turr_rotspeed], oRX, oRY, -359);
1120
            }
1121
            case TURRET_MOVE_ROTATE_CLOCKWISE_2: {
1122
                Streamer_RemoveArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_CLOCKWISE_2);
1123
                Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_CLOCKWISE_1);
1124
                SetDynamicObjectRot(rTurretData[turretid][e_turr_objectid], oRX, oRY, 0);
1125
                MoveDynamicObject(rTurretData[turretid][e_turr_objectid], oX, oY, oZ + 0.001, rTurretData[turretid][e_turr_rotspeed], oRX, oRY, -179);
1126
            }
1127
            case TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_1: {
1128
                Streamer_RemoveArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_1);
1129
                Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_2);
1130
                MoveDynamicObject(rTurretData[turretid][e_turr_objectid], oX, oY, oZ - 0.001, rTurretData[turretid][e_turr_rotspeed], oRX, oRY, 359);
1131
            }
1132
            case TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_2: {
1133
                Streamer_RemoveArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_2);
1134
                Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, rTurretData[turretid][e_turr_objectid], E_STREAMER_EXTRA_ID, TURRET_MOVE_ROTATE_COUNTERCLOCKWISE_1);
1135-
                    SetPlayerArmour(playerid, 0);
1135+
1136-
                    SetPlayerHealth(playerid, h - floatabs(dif));
1136+
1137
            }
1138
            default: CallLocalFunction("OnTurretMoved", "d", turretid);
1139
        }
1140
        return 1;
1141
    }
1142
    return CallLocalFunction("rTurret_OnDynamicObjectMoved", "d", objectid);
1143
}
1144
#if defined _ALS_OnDynamicObjectMoved
1145
    #undef OnDynamicObjectMoved
1146
#else
1147
    #define _ALS_OnDynamicObjectMoved
1148
#endif
1149
#define OnDynamicObjectMoved rTurret_OnDynamicObjectMoved
1150
forward rTurret_OnDynamicObjectMoved(objectid);
1151
1152
public OnPlayerEnterDynamicArea(playerid, areaid)
1153
{
1154
    // Check if player is dead or not spawned
1155
    if (rTurret_Killed{playerid})
1156
        return CallLocalFunction("rTurret_OnPlayerEnterArea", "dd", playerid, areaid);
1157
        
1158
    if (Streamer_IsInArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, TURRET_BULLET_AREA_INT)) {
1159
        // Destroy bullet and its area
1160
        new
1161
            data[4],
1162
            turretid = INVALID_TURRET,
1163
            bulletid,
1164
            target_focusid;
1165
            
1166
        Streamer_GetArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, data);
1167
        turretid = data[1];
1168
        bulletid = data[2];
1169
        target_focusid = data[3];
1170
        
1171
        if (!Iter_Contains(rTurrets, turretid))
1172
            return CallLocalFunction("rTurret_OnPlayerEnterArea", "dd", playerid, areaid);
1173
            
1174
        if (rTurretData[turretid][e_turr_owner] == playerid || (rTurretData[turretid][e_turr_team] != 255 && rTurretData[turretid][e_turr_team] == GetPlayerTeam(playerid)))
1175
            return 1;
1176
            
1177
        if (rTurretData[turretid][e_turr_target_focus])
1178
            if (target_focusid != INVALID_PLAYER_ID && target_focusid != playerid)
1179
                // playerid is not focus target and is therefore immune to damage
1180
                return 1;
1181
            
1182
        // "*** Streamer_GetArrayData: Invalid ID specified" message shows if you destroy area here.
1183
        // To avoid this, hide object and efficiently "disable" area. Object and area will be destroyed under OnDynamicObjectMoved.
1184
        
1185
        // DestroyDynamicArea(areaid);
1186
        // DestroyDynamicObject(bulletid);
1187
        Streamer_RemoveArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, TURRET_BULLET_AREA_INT);
1188
        Streamer_SetFloatData(STREAMER_TYPE_OBJECT, bulletid, E_STREAMER_STREAM_DISTANCE, 0.0);
1189
            
1190
        rTurretData[turretid][e_turr_visible_bullets]--;
1191
        if (funcidx("OnTurretHitPlayer") != -1 && CallLocalFunction("OnTurretHitPlayer", "dd", turretid, playerid)) {
1192
            // Damage player
1193
            new
1194
                Float:a,
1195
                Float:h,
1196
                Float:dif;
1197
                
1198
            GetPlayerArmour(playerid, a);
1199
            GetPlayerHealth(playerid, h);
1200
            if (floatcmp(a, 0.0) != 1) {
1201
                // No armor
1202
                dif = h - rTurretData[turretid][e_turr_dmg];
1203
                if (floatcmp(dif, 0.0) == -1 || floatcmp(dif, 0.0) == 0) {
1204
                    // Dead
1205
                    rTurret_Killed{playerid} = true;
1206
                    rTurret_KilledBy[playerid] = turretid;
1207
                    SetPlayerHealth(playerid, 0.0);
1208
                } else
1209
                    SetPlayerHealth(playerid, dif);
1210
            } else {
1211
                // Has armor
1212
                dif = a - rTurretData[turretid][e_turr_dmg];
1213
                if (floatcmp(dif, 0.0) == -1) {
1214
                    // Damage health
1215
                    new
1216
                        Float:newdif = h - floatabs(dif);
1217
                        
1218
                    SetPlayerArmour(playerid, 0.0);
1219
                    
1220
                    if (floatcmp(newdif, 0.0) == -1 || floatcmp(newdif, 0.0) == 0) {
1221
                        // Dead
1222
                        rTurret_Killed{playerid} = true;
1223
                        rTurret_KilledBy[playerid] = turretid;
1224
                        SetPlayerHealth(playerid, 0.0);
1225
                    } else
1226
                        SetPlayerHealth(playerid, newdif);
1227
                } else
1228
                    SetPlayerArmour(playerid, dif);
1229
            }
1230
            CallLocalFunction("OnPlayerTakeTurretDamage", "ddf", playerid, turretid, rTurretData[turretid][e_turr_dmg]);
1231
        }
1232-
    if (!Iter_Contains(rTurrets, turretid)) {
1232+
1233
    } else if (Streamer_IsInArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, TURRET_BIG_AREA_INT)) {
1234
        // Turret within hearing range of player
1235
        new
1236
            data[2],
1237
            turretid = INVALID_TURRET;
1238
            
1239
        Streamer_GetArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, data);
1240
        turretid = data[0];
1241
        if (!Iter_Contains(rTurrets, turretid))
1242
            return CallLocalFunction("rTurret_OnPlayerEnterArea", "dd", playerid, areaid);
1243
        Iter_Add(rTurretPlayersInBigArea[data[0]], playerid);
1244
        return 1;
1245
    } else if (Streamer_IsInArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, TURRET_SHOOT_AREA_INT)) {
1246
        // Player within target range of turret
1247
        new
1248
            data[2],
1249
            turretid = INVALID_TURRET;
1250
            
1251
        Streamer_GetArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, data);
1252
        turretid = data[0];
1253
        if (!Iter_Contains(rTurrets, turretid))
1254
            return CallLocalFunction("rTurret_OnPlayerEnterArea", "dd", playerid, areaid);
1255
        if (rTurretData[turretid][e_turr_owner] == playerid || (rTurretData[turretid][e_turr_team] != 255 && rTurretData[turretid][e_turr_team] == GetPlayerTeam(playerid)))
1256
            return 1;
1257
        Iter_Add(rTurretTargets[turretid], playerid);
1258
        if (rTurretData[turretid][e_turr_timer] == -1 && rTurretData[turretid][e_turr_active])
1259
            rTurretData[turretid][e_turr_timer] = SetTimerEx("Timer_TurretShoot", rTurretData[turretid][e_turr_rate], true, "d", turretid);
1260
        return 1;
1261
    }
1262
    return CallLocalFunction("rTurret_OnPlayerEnterArea", "dd", playerid, areaid);
1263
}
1264
#if defined _ALS_OnPlayerEnterDynamicArea
1265-
        Float:newPZ;
1265+
1266
#else
1267
    #define _ALS_OnPlayerEnterDynamicArea
1268
#endif
1269
#define OnPlayerEnterDynamicArea rTurret_OnPlayerEnterArea
1270
forward rTurret_OnPlayerEnterArea(playerid, areaid);
1271
1272
public OnPlayerLeaveDynamicArea(playerid, areaid)
1273
{
1274
    if (Streamer_IsInArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, TURRET_BIG_AREA_INT)) {
1275
        // Turret out of hearing range for player
1276
        new
1277
            data[2],
1278
            turretid = INVALID_TURRET;
1279
            
1280
        Streamer_GetArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, data);
1281
        turretid = data[0];
1282
        if (!Iter_Contains(rTurrets, turretid))
1283
            return CallLocalFunction("rTurret_OnPlayerEnterArea", "dd", playerid, areaid);
1284
        Iter_Remove(rTurretPlayersInBigArea[turretid], playerid);
1285
    } else if (Streamer_IsInArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, TURRET_SHOOT_AREA_INT)) {
1286
        // Player out of turret target range
1287
        new
1288
            data[2],
1289
            turretid = INVALID_TURRET;
1290
            
1291
        Streamer_GetArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, data);
1292
        turretid = data[0];
1293
        if (!Iter_Contains(rTurrets, turretid))
1294
            return CallLocalFunction("rTurret_OnPlayerEnterArea", "dd", playerid, areaid);
1295
        Iter_Remove(rTurretTargets[turretid], playerid);
1296
        if (!Iter_Count(rTurretTargets[turretid]) && rTurretData[turretid][e_turr_checkarea]) {
1297
            if (rTurretData[turretid][e_turr_timer] != -1) {
1298
                KillTimer(rTurretData[turretid][e_turr_timer]);
1299
                rTurretData[turretid][e_turr_timer] = -1;
1300
            }
1301
        }
1302
    }
1303
    return CallLocalFunction("rTurret_OnPlayerLeaveArea", "dd", playerid, areaid);
1304
}
1305
#if defined _ALS_OnPlayerLeaveDynamicArea
1306
    #undef OnPlayerLeaveDynamicArea
1307
#else
1308
    #define _ALS_OnPlayerLeaveDynamicArea
1309
#endif
1310
#define OnPlayerLeaveDynamicArea rTurret_OnPlayerLeaveArea
1311
forward rTurret_OnPlayerLeaveArea(playerid, areaid);
1312
1313
// ======================================================
1314
// Internal Timers
1315
// ======================================================
1316
1317
forward Timer_TurretShoot(turretid);
1318
forward Timer_ResetTurretKilled(playerid);
1319
1320
public Timer_TurretShoot(turretid)
1321
{       
1322
    if (!Iter_Contains(rTurrets, turretid) || (!Iter_Count(rTurretTargets[turretid]) && rTurretData[turretid][e_turr_checkarea])) {
1323
        if (rTurretData[turretid][e_turr_timer] != -1) {
1324
            KillTimer(rTurretData[turretid][e_turr_timer]);
1325
            rTurretData[turretid][e_turr_timer] = -1;
1326
        }
1327
        return 0;
1328
    }
1329
    
1330
    new
1331
        visibleBullets = rTurretData[turretid][e_turr_visible_bullets] + 1;
1332
    
1333
    if (visibleBullets >= rTurretData[turretid][e_turr_max_visible_bullets])
1334
        // Cancel bullet creation
1335
        return 0;
1336
    else
1337
        rTurretData[turretid][e_turr_visible_bullets]++;
1338
        
1339
    new
1340
        areaid,
1341
        bulletid,
1342
        // Player Pos
1343
        Float:pX,
1344
        Float:pY,
1345
        Float:pZ,
1346
        // Turret Pos and Rot
1347
        Float:turPX,
1348
        Float:turPY,
1349
        Float:turPZ,
1350
        Float:turRX,
1351
        Float:turRY,
1352
        Float:turRZ,
1353
        Float:newPX,
1354
        Float:newPY,
1355
        Float:newPZ,
1356
        target_focusid = INVALID_PLAYER_ID;
1357
    
1358
    GetDynamicObjectPos(rTurretData[turretid][e_turr_objectid], turPX, turPY, turPZ);
1359
    GetDynamicObjectRot(rTurretData[turretid][e_turr_objectid], turRX, turRY, turRZ);
1360
    newPZ = turPZ + 1.15;
1361
    
1362
    if (TURRET_BEHAVIOR_AIM_CLOSEST <= rTurretData[turretid][e_turr_behavior] <= TURRET_BEHAVIOR_AIM_CUSTOM) {
1363
        // Set turret facing target
1364
        new
1365
            targetid = INVALID_PLAYER_ID,
1366
            Float:targDist;
1367
            
1368
        switch(rTurretData[turretid][e_turr_behavior]) {
1369
            case TURRET_BEHAVIOR_AIM_CLOSEST: {
1370-
            if (rTurretData[turretid][e_turr_3dspace]) {
1370+
1371
                foreach(new i : rTurretTargets[turretid]) {
1372
                    if (floatcmp(targDist, GetPlayerDistanceFromPoint(i, turPX, turPY, newPZ)) == 1) {
1373
                        // Closest player so far
1374
                        targetid = i;
1375
                        targDist = GetPlayerDistanceFromPoint(i, turPX, turPY, newPZ);
1376
                        GetPlayerPos(i, pX, pY, pZ);
1377
                    }
1378
                }
1379
            }
1380
            case TURRET_BEHAVIOR_AIM_FARTHEST: {
1381
                foreach(new i : rTurretTargets[turretid]) {
1382
                    if (floatcmp(targDist, GetPlayerDistanceFromPoint(i, turPX, turPY, newPZ)) == -1) {
1383
                        // Farthest player so far
1384
                        targetid = i;
1385
                        targDist = GetPlayerDistanceFromPoint(i, turPX, turPY, newPZ);
1386
                        GetPlayerPos(i, pX, pY, pZ);
1387
                    }
1388
                }
1389
            }
1390
            case TURRET_BEHAVIOR_AIM_WEAKEST: {
1391
                new
1392
                    Float:a,
1393
                    Float:h,
1394
                    Float:smallSum = 1000.0;
1395
                    
1396
                foreach(new i : rTurretTargets[turretid]) {
1397
                    GetPlayerArmour(i, a);
1398
                    GetPlayerHealth(i, h);
1399
                    if (floatcmp(smallSum, a + h) == 1) {
1400
                        // Player with lowest health and armour so far
1401
                        smallSum = a + h;
1402
                        targetid = i;
1403
                        targDist = GetPlayerDistanceFromPoint(i, turPX, turPY, newPZ);
1404
                        GetPlayerPos(i, pX, pY, pZ);
1405
                    }
1406
                }
1407
            }
1408
            case TURRET_BEHAVIOR_AIM_STRONGEST: {
1409
                new
1410
                    Float:a,
1411
                    Float:h,
1412
                    Float:bigSum = 0.0;
1413
                    
1414
                foreach(new i : rTurretTargets[turretid]) {
1415
                    GetPlayerArmour(i, a);
1416
                    GetPlayerHealth(i, h);
1417
                    if (floatcmp(bigSum, a + h) == -1) {
1418
                        // Player with highest health and armour so far
1419
                        bigSum = a + h;
1420
                        targetid = i;
1421
                        targDist = GetPlayerDistanceFromPoint(i, turPX, turPY, newPZ);
1422
                        GetPlayerPos(i, pX, pY, pZ);
1423
                    }
1424
                }
1425
            }
1426
            case TURRET_BEHAVIOR_AIM_SINGLE: {
1427
                if (IsPlayerConnected(rTurretData[turretid][e_turr_target])) {
1428
                    targetid = rTurretData[turretid][e_turr_target];
1429
                    targDist = GetPlayerDistanceFromPoint(targetid, turPX, turPY, newPZ);
1430
                    GetPlayerPos(targetid, pX, pY, pZ);
1431
                }
1432
            }
1433
            case TURRET_BEHAVIOR_AIM_CUSTOM: {
1434
                if (funcidx("OnTurretCustomShoot") != -1) {
1435
                    new
1436
                        rtn = CallLocalFunction("OnTurretCustomShoot", "d", turretid);
1437
                        
1438
                    if (IsPlayerConnected(rtn)) {
1439
                        targetid = rtn;
1440
                        targDist = GetPlayerDistanceFromPoint(targetid, turPX, turPY, newPZ);
1441
                        GetPlayerPos(targetid, pX, pY, pZ);
1442
                    }
1443
                }
1444
            }
1445
            case TURRET_BEHAVIOR_AIM_RANDOM: {
1446
                if (Iter_Count(rTurretTargets[turretid]) > 0) {
1447
                    targetid = Iter_Random(rTurretTargets[turretid]);
1448
                    targDist = GetPlayerDistanceFromPoint(targetid, turPX, turPY, newPZ);
1449
                    GetPlayerPos(targetid, pX, pY, pZ);
1450
                }
1451
            }
1452
        }
1453
            
1454
        if (targetid != INVALID_PLAYER_ID) {
1455
            // Set turret facing target
1456
            new
1457
                Float:vX,
1458
                Float:vY,
1459
                Float:vZ,
1460
                Float:Speed;
1461
                
1462
            if (rTurretData[turretid][e_turr_target_focus])
1463
                // Set turret's target focus (all other players will be immune to damage)
1464
                target_focusid = targetid;
1465
                
1466
            if (rTurretData[turretid][e_turr_3dspace])
1467
                // Turret aims in 3d space
1468
                // Bullet's destination z value changes
1469
                newPZ = newPZ + (rTurretData[turretid][e_turr_range] * ((pZ - newPZ) / (targDist)));
1470
1471
            turRZ = atan2(pY - turPY, pX - turPX);
1472
            
1473
            if (IsPlayerInAnyVehicle(targetid))
1474
                GetVehicleVelocity(GetPlayerVehicleID(targetid), vX, vY, vZ);
1475
            else
1476
                GetPlayerVelocity(targetid, vX, vY, vZ);
1477
                
1478
            // Approximate player speed in units per second?
1479-
}
1479+
1480
            
1481
            if (floatcmp(Speed, 0.0) != 0) {
1482
                if (!rTurretData[turretid][e_turr_predict]) {
1483
                    newPX = turPX + (rTurretData[turretid][e_turr_range] * floatsin(-(turRZ + 270), degrees));
1484
                    newPY = turPY + (rTurretData[turretid][e_turr_range] * floatcos(-(turRZ + 270), degrees));
1485
                } else {
1486
                    // Predictive aim (inaccurate atm, needs improvement)
1487
                    new
1488
                        Float:pA;
1489
                        
1490
                    GetPlayerFacingAngle(targetid, pA);
1491
                    pA = -pA + 360;
1492
                    
1493
                    new
1494
                        Float:targetTravelDist = Speed / (1000 / float(rTurretData[turretid][e_turr_rate])),
1495
                        Float:bulletCross = floatsqroot(floatpower(targetTravelDist, 2) + floatpower(targDist, 2) - 2 * targetTravelDist * targDist * floatcos(pA - turRZ, degrees)),
1496
                        Float:angleAdjust = asin((floatsin(pA - turRZ) * targetTravelDist) / bulletCross);
1497
                        
1498
                    //printf("Speed: %.2f | TravelDist: %.2f | Cross: %.2f | pA: %.2f | Angle: %.2f", Speed, targetTravelDist, bulletCross, pA, angleAdjust);
1499
1500
                    if (angleAdjust == angleAdjust) {
1501
                        turRZ = turRZ + angleAdjust;
1502
                        newPX = turPX + (rTurretData[turretid][e_turr_range] * floatsin(-(turRZ + 270), degrees));
1503
                        newPY = turPY + (rTurretData[turretid][e_turr_range] * floatcos(-(turRZ + 270), degrees));
1504
                    } else {
1505
                        // Rare case of NaN angle
1506
                        newPX = pX;
1507
                        newPY = pY;
1508
                    }
1509
                }
1510
            } else {
1511
                // When standing still, targets don't get hit by very fast bullets (speed >= 20 && rate >= 250)
1512
                // Temporary solution
1513
                newPX = pX;
1514
                newPY = pY;
1515
            }
1516
            SetDynamicObjectRot(rTurretData[turretid][e_turr_objectid], turRX, turRY, turRZ);
1517
        } else {
1518
            // Turret doesn't change direction
1519
            newPX = turPX + (rTurretData[turretid][e_turr_range] * floatsin(-(turRZ + 270), degrees));
1520
            newPY = turPY + (rTurretData[turretid][e_turr_range] * floatcos(-(turRZ + 270), degrees));
1521
        }
1522
    } else {
1523
        newPX = turPX + (rTurretData[turretid][e_turr_range] * floatsin(-(turRZ + 270), degrees));
1524
        newPY = turPY + (rTurretData[turretid][e_turr_range] * floatcos(-(turRZ + 270), degrees));
1525
    }
1526
        
1527
    bulletid = 
1528
        CreateDynamicObject(rTurretData[turretid][e_turr_bullet_model],
1529
            turPX + (1.0 * floatsin(-(turRZ + 270), degrees)),
1530
            turPY + (1.0 * floatcos(-(turRZ + 270), degrees)),
1531
            turPZ + 1.15, 0, 270.0, turRZ,
1532
            rTurretData[turretid][e_turr_world],
1533
            rTurretData[turretid][e_turr_interior],
1534
            .streamdistance = rTurretData[turretid][e_turr_stream]);
1535
            
1536
    if (rTurretData[turretid][e_turr_bullet_color] != 0)
1537
        SetDynamicObjectMaterial(bulletid, 0, -1, "none", "none", rTurretData[turretid][e_turr_bullet_color]);
1538
1539
    areaid = CreateDynamicSphere(0, 0, 0, rTurretData[turretid][e_turr_bullet_radius], rTurretData[turretid][e_turr_world], rTurretData[turretid][e_turr_interior]);
1540
    AttachDynamicAreaToObject(areaid, bulletid);
1541
        
1542
    // Bullet object array data (identifier, turretid, areaid)
1543
    Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, bulletid, E_STREAMER_EXTRA_ID, TURRET_BULLET_OBJECT_INT);
1544
    Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, bulletid, E_STREAMER_EXTRA_ID, turretid);
1545
    Streamer_AppendArrayData(STREAMER_TYPE_OBJECT, bulletid, E_STREAMER_EXTRA_ID, areaid);
1546
    
1547
    // Area array data (identifier, turretid, bullet objectid)
1548
    Streamer_AppendArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, TURRET_BULLET_AREA_INT);
1549
    Streamer_AppendArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, turretid);
1550
    Streamer_AppendArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, bulletid);
1551
    Streamer_AppendArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, target_focusid);
1552
1553
    MoveDynamicObject(bulletid, newPX, newPY, newPZ, rTurretData[turretid][e_turr_speed]);
1554
        
1555
    if (!rTurretData[turretid][e_turr_playsound])
1556
        foreach(new i : rTurretPlayersInBigArea[turretid])
1557
            Streamer_Update(i);
1558
    else {
1559
        foreach(new i : rTurretPlayersInBigArea[turretid]) {
1560
            Streamer_Update(i);
1561
            if (IsPlayerInRangeOfPoint(i, 20.0, turPX, turPY, turPZ))
1562
                PlayerPlaySound(i, rTurretData[turretid][e_turr_soundid], 0, 0, 0);
1563
            else {
1564
                GetPlayerPos(i, pX, pY, pZ);
1565
                PlayerPlaySound(i, rTurretData[turretid][e_turr_soundid], pX, pY, pZ + 7);
1566
            }
1567
        }
1568
    }
1569
    return 1;
1570
}
1571
1572
public Timer_ResetTurretKilled(playerid)
1573
{
1574
    rTurret_Killed{playerid} = false;
1575
    rTurret_KilledBy[playerid] = INVALID_TURRET;
1576
}
1577
1578
// ======================================================
1579
// British English Macros
1580
// ======================================================
1581
1582
#define TURRET_BEHAVIOUR_STATIONARY                 TURRET_BEHAVIOR_STATIONARY
1583
#define TURRET_BEHAVIOUR_ROTATE_CLOCKWISE           TURRET_BEHAVIOR_ROTATE_CLOCKWISE
1584
#define TURRET_BEHAVIOUR_ROTATE_COUNTERCLOCKWISE    TURRET_BEHAVIOR_ROTATE_COUNTERCLOCKWISE
1585
#define TURRET_BEHAVIOUR_AIM_CLOSEST                TURRET_BEHAVIOR_AIM_CLOSEST
1586
#define TURRET_BEHAVIOUR_AIM_FARTHEST               TURRET_BEHAVIOR_AIM_FARTHEST
1587
#define TURRET_BEHAVIOUR_AIM_WEAKEST                TURRET_BEHAVIOR_AIM_WEAKEST
1588
#define TURRET_BEHAVIOUR_AIM_STRONGEST              TURRET_BEHAVIOR_AIM_STRONGEST
1589
#define TURRET_BEHAVIOUR_AIM_RANDOM                 TURRET_BEHAVIOR_AIM_RANDOM
1590
#define TURRET_BEHAVIOUR_AIM_SINGLE                 TURRET_BEHAVIOR_AIM_SINGLE
1591
#define TURRET_BEHAVIOUR_AIM_CUSTOM                 TURRET_BEHAVIOR_AIM_CUSTOM
1592
1593
#define GetTurretBehaviour                          GetTurretBehavior
1594
#define SetTurretBehaviour                          SetTurretBehavior
1595
#define GetTurretBulletColour                       GetTurretBulletColor
1596
#define SetTurretBulletColour                       SetTurretBulletColor