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