1 /*
2 * Copyright(c) 1997-2001 Id Software, Inc.
3 * Copyright(c) 2002 The Quakeforge Project.
4 * Copyright(c) 2006 Quetoo.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or(at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include "g_local.h"
23
24 #define STEPSIZE 18
25
26 /*
27 M_CheckBottom
28
29 Returns false if any part of the bottom of the entity is off an edge that
30 is not a staircase.
31
32 */
33 int c_yes, c_no;
34
M_CheckBottom(edict_t * ent)35 qboolean M_CheckBottom(edict_t *ent){
36 vec3_t mins, maxs, start, stop;
37 trace_t trace;
38 int x, y;
39 float mid, bottom;
40
41 VectorAdd(ent->s.origin, ent->mins, mins);
42 VectorAdd(ent->s.origin, ent->maxs, maxs);
43
44 // if all of the points under the corners are solid world, don't bother
45 // with the tougher checks
46 // the corners must be within 16 of the midpoint
47 start[2] = mins[2] - 1;
48 for(x = 0; x <= 1; x++)
49 for(y = 0; y <= 1; y++){
50 start[0] = x ? maxs[0] : mins[0];
51 start[1] = y ? maxs[1] : mins[1];
52 if(gi.pointcontents(start) != CONTENTS_SOLID)
53 goto realcheck;
54 }
55
56 c_yes++;
57 return true; // we got out easy
58
59 realcheck:
60 c_no++;
61 //
62 // check it for real...
63 //
64 start[2] = mins[2];
65
66 // the midpoint must be within 16 of the bottom
67 start[0] = stop[0] =(mins[0] + maxs[0]) * 0.5;
68 start[1] = stop[1] =(mins[1] + maxs[1]) * 0.5;
69 stop[2] = start[2] - 2 * STEPSIZE;
70 trace = gi.trace(start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
71
72 if(trace.fraction == 1.0)
73 return false;
74 mid = bottom = trace.endpos[2];
75
76 // the corners must be within 16 of the midpoint
77 for(x = 0; x <= 1; x++)
78 for(y = 0; y <= 1; y++){
79 start[0] = stop[0] = x ? maxs[0] : mins[0];
80 start[1] = stop[1] = y ? maxs[1] : mins[1];
81
82 trace = gi.trace(start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
83
84 if(trace.fraction != 1.0 && trace.endpos[2] > bottom)
85 bottom = trace.endpos[2];
86 if(trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
87 return false;
88 }
89
90 c_yes++;
91 return true;
92 }
93
94
95 /*
96 SV_movestep
97
98 Called by monster program code.
99 The move will be adjusted for slopes and stairs, but if the move isn't
100 possible, no move is done, false is returned, and
101 pr_global_struct->trace_normal is set to the normal of the blocking wall
102 */
103 //FIXME since we need to test end position contents here, can we avoid doing
104 //it again later in catagorize position?
SV_movestep(edict_t * ent,vec3_t move,qboolean relink)105 qboolean SV_movestep(edict_t *ent, vec3_t move, qboolean relink){
106 float dz;
107 vec3_t oldorg, neworg, end;
108 trace_t trace;
109 int i;
110 float stepsize;
111 vec3_t test;
112 int contents;
113
114 // try the move
115 VectorCopy(ent->s.origin, oldorg);
116 VectorAdd(ent->s.origin, move, neworg);
117
118 // flying monsters don't step up
119 if(ent->flags &(FL_SWIM | FL_FLY)){
120 // try one move with vertical motion, then one without
121 for(i = 0; i < 2; i++){
122 VectorAdd(ent->s.origin, move, neworg);
123 if(i == 0 && ent->enemy){
124 if(!ent->goalentity)
125 ent->goalentity = ent->enemy;
126 dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
127 if(ent->goalentity->client){
128 if(dz > 40)
129 neworg[2] -= 8;
130 if(!((ent->flags & FL_SWIM) &&(ent->waterlevel < 2)))
131 if(dz < 30)
132 neworg[2] += 8;
133 } else {
134 if(dz > 8)
135 neworg[2] -= 8;
136 else if(dz > 0)
137 neworg[2] -= dz;
138 else if(dz < -8)
139 neworg[2] += 8;
140 else
141 neworg[2] += dz;
142 }
143 }
144 trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
145
146 // fly monsters don't enter water voluntarily
147 if(ent->flags & FL_FLY){
148 if(!ent->waterlevel){
149 test[0] = trace.endpos[0];
150 test[1] = trace.endpos[1];
151 test[2] = trace.endpos[2] + ent->mins[2] + 1;
152 contents = gi.pointcontents(test);
153 if(contents & MASK_WATER)
154 return false;
155 }
156 }
157
158 // swim monsters don't exit water voluntarily
159 if(ent->flags & FL_SWIM){
160 if(ent->waterlevel < 2){
161 test[0] = trace.endpos[0];
162 test[1] = trace.endpos[1];
163 test[2] = trace.endpos[2] + ent->mins[2] + 1;
164 contents = gi.pointcontents(test);
165 if(!(contents & MASK_WATER))
166 return false;
167 }
168 }
169
170 if(trace.fraction == 1){
171 VectorCopy(trace.endpos, ent->s.origin);
172 if(relink){
173 gi.linkentity(ent);
174 G_TouchTriggers(ent);
175 }
176 return true;
177 }
178
179 if(!ent->enemy)
180 break;
181 }
182
183 return false;
184 }
185
186 // push down from a step height above the wished position
187 if(!(ent->monsterinfo.aiflags & AI_NOSTEP))
188 stepsize = STEPSIZE;
189 else
190 stepsize = 1;
191
192 neworg[2] += stepsize;
193 VectorCopy(neworg, end);
194 end[2] -= stepsize * 2;
195
196 trace = gi.trace(neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
197
198 if(trace.allsolid)
199 return false;
200
201 if(trace.startsolid){
202 neworg[2] -= stepsize;
203 trace = gi.trace(neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
204 if(trace.allsolid || trace.startsolid)
205 return false;
206 }
207
208
209 // don't go in to water
210 if(ent->waterlevel == 0){
211 test[0] = trace.endpos[0];
212 test[1] = trace.endpos[1];
213 test[2] = trace.endpos[2] + ent->mins[2] + 1;
214 contents = gi.pointcontents(test);
215
216 if(contents & MASK_WATER)
217 return false;
218 }
219
220 if(trace.fraction == 1){
221 // if monster had the ground pulled out, go ahead and fall
222 if(ent->flags & FL_PARTIALGROUND){
223 VectorAdd(ent->s.origin, move, ent->s.origin);
224 if(relink){
225 gi.linkentity(ent);
226 G_TouchTriggers(ent);
227 }
228 ent->groundentity = NULL;
229 return true;
230 }
231
232 return false; // walked off an edge
233 }
234
235 // check point traces down for dangling corners
236 VectorCopy(trace.endpos, ent->s.origin);
237
238 if(!M_CheckBottom(ent)){
239 if(ent->flags & FL_PARTIALGROUND){ // entity had floor mostly pulled out from underneath it
240 // and is trying to correct
241 if(relink){
242 gi.linkentity(ent);
243 G_TouchTriggers(ent);
244 }
245 return true;
246 }
247 VectorCopy(oldorg, ent->s.origin);
248 return false;
249 }
250
251 if(ent->flags & FL_PARTIALGROUND){
252 ent->flags &= ~FL_PARTIALGROUND;
253 }
254 ent->groundentity = trace.ent;
255 ent->groundentity_linkcount = trace.ent->linkcount;
256
257 // the move is ok
258 if(relink){
259 gi.linkentity(ent);
260 G_TouchTriggers(ent);
261 }
262 return true;
263 }
264
265
266
267 /*
268 M_ChangeYaw
269
270 */
M_ChangeYaw(edict_t * ent)271 void M_ChangeYaw(edict_t *ent){
272 float ideal;
273 float current;
274 float move;
275 float speed;
276
277 current = anglemod(ent->s.angles[YAW]);
278 ideal = ent->ideal_yaw;
279
280 if(current == ideal)
281 return;
282
283 move = ideal - current;
284 speed = ent->yaw_speed;
285 if(ideal > current){
286 if(move >= 180)
287 move = move - 360;
288 } else {
289 if(move <= -180)
290 move = move + 360;
291 }
292 if(move > 0){
293 if(move > speed)
294 move = speed;
295 } else {
296 if(move < -speed)
297 move = -speed;
298 }
299
300 ent->s.angles[YAW] = anglemod(current + move);
301 }
302
303
304 /*
305 SV_StepDirection
306
307 Turns to the movement direction, and walks the current distance if
308 facing it.
309
310 */
SV_StepDirection(edict_t * ent,float yaw,float dist)311 qboolean SV_StepDirection(edict_t *ent, float yaw, float dist){
312 vec3_t move, oldorigin;
313 float delta;
314
315 ent->ideal_yaw = yaw;
316 M_ChangeYaw(ent);
317
318 yaw = yaw * M_PI * 2 / 360;
319 move[0] = cos(yaw) * dist;
320 move[1] = sin(yaw) * dist;
321 move[2] = 0;
322
323 VectorCopy(ent->s.origin, oldorigin);
324 if(SV_movestep(ent, move, false)){
325 delta = ent->s.angles[YAW] - ent->ideal_yaw;
326 if(delta > 45 && delta < 315){ // not turned far enough, so don't take the step
327 VectorCopy(oldorigin, ent->s.origin);
328 }
329 gi.linkentity(ent);
330 G_TouchTriggers(ent);
331 return true;
332 }
333 gi.linkentity(ent);
334 G_TouchTriggers(ent);
335 return false;
336 }
337
338 /*
339 SV_FixCheckBottom
340
341 */
SV_FixCheckBottom(edict_t * ent)342 void SV_FixCheckBottom(edict_t *ent){
343 ent->flags |= FL_PARTIALGROUND;
344 }
345
346
347
348 /*
349 SV_NewChaseDir
350
351 */
352 #define DI_NODIR -1
SV_NewChaseDir(edict_t * actor,edict_t * enemy,float dist)353 void SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist){
354 float deltax, deltay;
355 float d[3];
356 float tdir, olddir, turnaround;
357
358 //FIXME: how did we get here with no enemy
359 if(!enemy)
360 return;
361
362 olddir = anglemod((int)(actor->ideal_yaw / 45) * 45);
363 turnaround = anglemod(olddir - 180);
364
365 deltax = enemy->s.origin[0] - actor->s.origin[0];
366 deltay = enemy->s.origin[1] - actor->s.origin[1];
367 if(deltax > 10)
368 d[1] = 0;
369 else if(deltax < -10)
370 d[1] = 180;
371 else
372 d[1] = DI_NODIR;
373 if(deltay < -10)
374 d[2] = 270;
375 else if(deltay > 10)
376 d[2] = 90;
377 else
378 d[2] = DI_NODIR;
379
380 // try direct route
381 if(d[1] != DI_NODIR && d[2] != DI_NODIR){
382 if(d[1] == 0)
383 tdir = d[2] == 90 ? 45 : 315;
384 else
385 tdir = d[2] == 90 ? 135 : 215;
386
387 if(tdir != turnaround && SV_StepDirection(actor, tdir, dist))
388 return;
389 }
390
391 // try other directions
392 if(((rand()&3) & 1) || abs(deltay) > abs(deltax)){
393 tdir = d[1];
394 d[1] = d[2];
395 d[2] = tdir;
396 }
397
398 if(d[1] != DI_NODIR && d[1] != turnaround
399 && SV_StepDirection(actor, d[1], dist))
400 return;
401
402 if(d[2] != DI_NODIR && d[2] != turnaround
403 && SV_StepDirection(actor, d[2], dist))
404 return;
405
406 /* there is no direct path to the player, so pick another direction */
407
408 if(olddir != DI_NODIR && SV_StepDirection(actor, olddir, dist))
409 return;
410
411 if(rand()&1) /*randomly determine direction of search*/
412 {
413 for(tdir = 0; tdir <= 315; tdir += 45)
414 if(tdir != turnaround && SV_StepDirection(actor, tdir, dist))
415 return;
416 } else {
417 for(tdir = 315; tdir >= 0; tdir -= 45)
418 if(tdir != turnaround && SV_StepDirection(actor, tdir, dist))
419 return;
420 }
421
422 if(turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist))
423 return;
424
425 actor->ideal_yaw = olddir; // can't move
426
427 // if a bridge was pulled out from underneath a monster, it may not have
428 // a valid standing position at all
429
430 if(!M_CheckBottom(actor))
431 SV_FixCheckBottom(actor);
432 }
433
434 /*
435 SV_CloseEnough
436
437 */
SV_CloseEnough(edict_t * ent,edict_t * goal,float dist)438 qboolean SV_CloseEnough(edict_t *ent, edict_t *goal, float dist){
439 int i;
440
441 for(i = 0; i < 3; i++){
442 if(goal->absmin[i] > ent->absmax[i] + dist)
443 return false;
444 if(goal->absmax[i] < ent->absmin[i] - dist)
445 return false;
446 }
447 return true;
448 }
449
450
451 /*
452 M_MoveToGoal
453 */
M_MoveToGoal(edict_t * ent,float dist)454 void M_MoveToGoal(edict_t *ent, float dist){
455 edict_t *goal;
456
457 goal = ent->goalentity;
458
459 if(!ent->groundentity && !(ent->flags &(FL_FLY | FL_SWIM)))
460 return;
461
462 // if the next step hits the enemy, return immediately
463 if(ent->enemy && SV_CloseEnough(ent, ent->enemy, dist))
464 return;
465
466 // bump around...
467 if((rand()&3) == 1 || !SV_StepDirection(ent, ent->ideal_yaw, dist)){
468 if(ent->inuse)
469 SV_NewChaseDir(ent, goal, dist);
470 }
471 }
472
473
474 /*
475 M_walkmove
476 */
M_walkmove(edict_t * ent,float yaw,float dist)477 qboolean M_walkmove(edict_t *ent, float yaw, float dist){
478 vec3_t move;
479
480 if(!ent->groundentity && !(ent->flags &(FL_FLY | FL_SWIM)))
481 return false;
482
483 yaw = yaw * M_PI * 2 / 360;
484
485 move[0] = cos(yaw) * dist;
486 move[1] = sin(yaw) * dist;
487 move[2] = 0;
488
489 return SV_movestep(ent, move, true);
490 }
491