1 #include "g_local.h"
2
3
4 #define Z_RADUISLISTSIZE 2000
5
6
7
8 void ai_run_melee(edict_t *self);
9 qboolean FindTarget (edict_t *self);
10 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist);
11 void SV_NewChaseDir (edict_t *actor, vec3_t eOrigin, float dist);
12 #if 0
13 void z_aiMoveTo(edict_t *self, float dist)
14 {
15 // sanity check
16 if (!(self->monsterinfo.scriptState & MSS_AIMOVETO))
17 return;
18 #if 0
19 if (!SV_StepDirection (self, self->ideal_yaw, dist))
20 {
21 SV_NewChaseDir (self, self->monsterinfo.aiMoveTo, dist);
22 }
23 #endif
24 }
25 #endif
26
27
28 /*
29 =============
30 zSchoolAllVisiable
31
32 Creates a list of all entities in the raduis of Z_RADUISLISTSIZE
33 ==============
34 */
zCreateRaduisList(edict_t * self)35 void zCreateRaduisList(edict_t *self)
36 {
37 edict_t *head, *list;
38 vec3_t vec;
39
40 if(self->zRaduisList)
41 { // already created for this think, don't bother doing it again...
42 return;
43 }
44
45 head = NULL;
46 list = self;
47
48 while(1)
49 {
50 head = findradius(head, self->s.origin, Z_RADUISLISTSIZE);
51 if(head == NULL)
52 break;
53
54 if(head != self)
55 {
56 list->zRaduisList = head;
57 VectorSubtract(self->s.origin, head->s.origin, vec);
58 head->zDistance = VectorLength(vec);
59 list = head;
60 }
61 }
62
63 list->zRaduisList = NULL;
64 };
65
66
67
68 /*
69 =============
70 zSchoolAllVisiable
71
72 Create list of monsters of the same schooling type that are ahead of you.
73 ==============
74 */
zSchoolAllVisiable(edict_t * self)75 int zSchoolAllVisiable(edict_t *self)
76 {
77 int max;
78 edict_t *head, *list;
79
80 max = 0;
81
82 zCreateRaduisList(self);
83 head = self->zRaduisList;
84 list = self;
85
86 while (head)
87 {
88 if(strcmp(head->classname, self->classname) == 0 && (self->monsterinfo.aiflags & AI_SCHOOLING) && (head->health > 0) &&
89 (head->zDistance <= self->monsterinfo.zSchoolSightRadius) && (visible(self, head)) && (infront(self, head)))
90 {
91 list->zSchoolChain = head;
92 list = head;
93 max++;
94 }
95 head = head->zRaduisList;
96 }
97
98 list->zSchoolChain = NULL;
99
100 return max;
101 }
102
103
104
105
106 /*
107 =============
108 zFindRoamYaw
109
110 Check direction moving in does not hit a wall... if it does change direction.
111 ==============
112 */
zFindRoamYaw(edict_t * self,float distcheck)113 int zFindRoamYaw(edict_t *self, float distcheck)
114 {
115 vec3_t forward, end, angles;
116 trace_t tr;
117 float current = anglemod(self->s.angles[YAW]);
118
119 if(current <= self->ideal_yaw - 1 || current > self->ideal_yaw + 1)
120 {
121 if(fabs(current - self->ideal_yaw) <= 359.0)
122 {
123 return 0;
124 }
125 }
126
127 AngleVectors (self->s.angles, forward, NULL, NULL);
128 VectorMA (self->s.origin, distcheck, forward, end);
129
130 tr = gi.trace (self->s.origin, self->mins, self->maxs, end, self, MASK_SOLID);
131
132 if (tr.fraction < 1.0)
133 {
134 if(random() > 0.75)
135 {
136 self->ideal_yaw = vectoyaw(forward);
137 self->ideal_yaw = self->ideal_yaw + 180;
138 }
139 else
140 {
141 float dir = random() > 0.5 ? -45 : 45;
142 float maxtrys = 100;
143
144 VectorCopy(self->s.angles, angles);
145
146 while(tr.fraction < 1.0 && maxtrys)
147 {
148 // blocked, change ideal yaw...
149 self->ideal_yaw = vectoyaw(forward);
150 self->ideal_yaw = self->ideal_yaw + (random() * dir);
151 // self->ideal_yaw = self->ideal_yaw + (-45 + (random() * 90));
152
153 angles[YAW] = anglemod (self->ideal_yaw);
154 AngleVectors (angles, forward, NULL, NULL);
155 VectorMA (self->s.origin, distcheck, forward, end);
156
157 tr = gi.trace (self->s.origin, self->mins, self->maxs, end, self, MASK_SOLID);
158 maxtrys--;
159 }
160 }
161
162 return 1;
163 }
164
165 return 0;
166 };
167
168
169
170 /*
171 =============
172 zSchoolMonsters
173
174 Roaming schooling ai.
175 ==============
176 */
zSchoolMonsters(edict_t * self,float dist,int runStyle,float * currentSpeed)177 int zSchoolMonsters(edict_t *self, float dist, int runStyle, float *currentSpeed)
178 {
179 int maxInsight;
180 int newRunStyle;
181
182 maxInsight = zSchoolAllVisiable(self);
183
184 // If you're not out in front
185 if(maxInsight > 0)
186 {
187 float totalSpeed;
188 float totalBearing;
189 float distanceToNearest, distanceToLeader, dist;
190 edict_t *nearestEntity, *list;
191 vec3_t vec;
192
193 totalSpeed = 0;
194 totalBearing = 0;
195 distanceToNearest = 10000;
196 distanceToLeader = 0;
197 list = self->zSchoolChain;
198
199 while(list)
200 {
201 // Gather data on those you see
202 totalSpeed += list->speed;
203 totalBearing += anglemod(list->s.angles[YAW]);
204
205 VectorSubtract(self->s.origin, list->s.origin, vec);
206 dist = VectorLength(vec);
207
208 if(dist < distanceToNearest)
209 {
210 distanceToNearest = dist;
211 nearestEntity = list;
212 }
213
214 if(dist > distanceToLeader)
215 {
216 distanceToLeader = dist;
217 }
218
219 list = list->zSchoolChain;
220 }
221
222 // Rule 1) Match average speed of those in the list
223 self->speed = (totalSpeed / maxInsight) * 1.5;
224
225 // Rule 2) Move towards the perceived center of gravity of the herd
226 self->ideal_yaw = totalBearing / maxInsight;
227
228 // check if hitting something
229 if(!zFindRoamYaw(self, 10))
230 {
231 // Rule 3) Maintain a minimum distance from those around you
232 if(distanceToNearest <= self->monsterinfo.zSchoolMinimumDistance)
233 {
234 self->ideal_yaw = nearestEntity->s.angles[YAW];
235 self->speed = nearestEntity->speed;
236 }
237 }
238
239 }
240 else
241 { //You are in front, so slow down a bit
242 edict_t *head;
243
244 self->speed = (self->speed * self->monsterinfo.zSchoolDecayRate);
245
246 // check direction
247 zFindRoamYaw(self, 100);
248
249 // change directions of the monsters following you...
250 zCreateRaduisList(self);
251 head = self->zRaduisList;
252
253 while (head)
254 {
255 if(strcmp(head->classname, self->classname) == 0 && (head->health > 0) &&
256 (head->zDistance <= self->monsterinfo.zSchoolSightRadius) && (visible(self, head)))
257
258 {
259 head->ideal_yaw = self->ideal_yaw + (-20 + (random() * 40));
260 }
261 head = head->zRaduisList;
262 }
263 }
264
265 // if(self.rm_schoolFlags & 1)
266 // { // check to see is I keep away from "other" entities...
267 // zSchoolCheckForOtherEntities(checkOtherRaduis);
268 // }
269
270 if(self->speed > self->monsterinfo.zSchoolMaxSpeed)
271 {
272 self->speed = self->monsterinfo.zSchoolMaxSpeed;
273 }
274
275 if(self->speed < self->monsterinfo.zSchoolMinSpeed)
276 {
277 self->speed = self->monsterinfo.zSchoolMinSpeed;
278 }
279
280 if(self->speed <= self->monsterinfo.zSpeedStandMax)
281 {
282 newRunStyle = 0;
283
284 if(newRunStyle != runStyle)
285 {
286 *currentSpeed = 1;
287 }
288 else
289 {
290 *currentSpeed = (self->speed - self->monsterinfo.zSchoolMinSpeed) + 1;
291 }
292 }
293 else if(self->speed <= self->monsterinfo.zSpeedWalkMax)
294 {
295 newRunStyle = 1;
296
297 if(newRunStyle != runStyle)
298 {
299 *currentSpeed = 1;
300 }
301 else
302 {
303 *currentSpeed = (self->speed - self->monsterinfo.zSpeedStandMax) + 1;
304 }
305 }
306 else
307 {
308 newRunStyle = 2;
309
310 if(newRunStyle != runStyle)
311 {
312 *currentSpeed = 1;
313 }
314 else
315 {
316 *currentSpeed = (self->speed - self->monsterinfo.zSpeedWalkMax) + 1;
317 }
318 }
319
320 return newRunStyle;
321 }
322
323
324
325 /*
326 =============
327 ai_schoolStand
328
329 Used for standing around and looking for players / schooling monsters of the same type.
330 Distance is for slight position adjustments needed by the animations
331 ==============
332 */
ai_schoolStand(edict_t * self,float dist)333 void ai_schoolStand (edict_t *self, float dist)
334 {
335 float speed;
336
337 if(!(self->monsterinfo.aiflags & AI_SCHOOLING))
338 {
339 ai_stand(self, dist);
340 return;
341 }
342
343 // init school var's for this frame
344 self->zRaduisList = NULL;
345
346 if(self->enemy || FindTarget(self))
347 {
348 ai_stand(self, dist);
349 return;
350 }
351 else
352 {
353 // run schooling routines
354 switch(zSchoolMonsters(self, dist, 0, &speed))
355 {
356 case 1:
357 self->monsterinfo.walk (self);
358 break;
359
360 case 2:
361 self->monsterinfo.run (self);
362 break;
363 }
364 }
365
366 // do the normal stand stuff
367 if (dist)
368 M_walkmove (self, self->ideal_yaw, dist);
369 // M_walkmove (self, self->ideal_yaw, dist * speed);
370 }
371
372
373
374
375
376 /*
377 =============
378 ai_schoolRun
379
380 The monster has an enemy it is trying to kill
381 =============
382 */
ai_schoolRun(edict_t * self,float dist)383 void ai_schoolRun (edict_t *self, float dist)
384 {
385 float speed;
386
387 if(!(self->monsterinfo.aiflags & AI_SCHOOLING))
388 {
389 ai_run(self, dist);
390 return;
391 }
392
393 // init school var's for this frame
394 self->zRaduisList = NULL;
395
396 if(self->enemy || FindTarget(self))
397 {
398 ai_run(self, dist);
399 return;
400 }
401 else
402 {
403 // run schooling routines
404 switch(zSchoolMonsters(self, dist, 2, &speed))
405 {
406 case 0:
407 self->monsterinfo.stand (self);
408 break;
409
410 case 1:
411 self->monsterinfo.walk (self);
412 break;
413 }
414 }
415
416 // do the normal run stuff
417 SV_StepDirection (self, self->ideal_yaw, dist);
418 // SV_StepDirection (self, self->ideal_yaw, dist * speed);
419 }
420
421
422
423 /*
424 =============
425 ai_schoolWalk
426
427 The monster is walking it's beat
428 =============
429 */
ai_schoolWalk(edict_t * self,float dist)430 void ai_schoolWalk (edict_t *self, float dist)
431 {
432 float speed;
433
434 if(!(self->monsterinfo.aiflags & AI_SCHOOLING))
435 {
436 ai_walk(self, dist);
437 return;
438 }
439
440 // init school var's for this frame
441 self->zRaduisList = NULL;
442
443 if(self->enemy || FindTarget(self))
444 {
445 ai_walk(self, dist);
446 return;
447 }
448 else
449 {
450 // run schooling routines
451 switch(zSchoolMonsters(self, dist, 1, &speed))
452 {
453 case 0:
454 self->monsterinfo.stand (self);
455 break;
456
457 case 2:
458 self->monsterinfo.run (self);
459 break;
460 }
461 }
462
463 // do the normal walk stuff
464 SV_StepDirection (self, self->ideal_yaw, dist);
465 // SV_StepDirection (self, self->ideal_yaw, dist * speed);
466 }
467
468
469
470 /*
471 =============
472 ai_schoolCharge
473
474 Turns towards target and advances
475 Use this call with a distnace of 0 to replace ai_face
476 ==============
477 */
ai_schoolCharge(edict_t * self,float dist)478 void ai_schoolCharge (edict_t *self, float dist)
479 {
480 /*
481 if(!(self->monsterinfo.aiflags & AI_SCHOOLING))
482 {
483 ai_charge(self, dist);
484 return;
485 }
486 */
487 ai_charge(self, dist);
488 }
489
490
491
492