]> icculus.org git repositories - divverent/darkplaces.git/blob - world.c
fix for 'falling' in a corner bug, thanks to Elric for finding the fix for this.
[divverent/darkplaces.git] / world.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // world.c -- world query functions
21
22 #include "quakedef.h"
23
24 /*
25
26 entities never clip against themselves, or their owner
27
28 line of sight checks trace->crosscontent, but bullets don't
29
30 */
31
32 cvar_t sv_useareanodes = {CVAR_NOTIFY, "sv_useareanodes", "1"};
33
34 void SV_World_Init(void)
35 {
36         Cvar_RegisterVariable(&sv_useareanodes);
37 }
38
39
40 void ClearLink (link_t *l);
41 void RemoveLink (link_t *l);
42 void InsertLinkBefore (link_t *l, link_t *before);
43 void InsertLinkAfter (link_t *l, link_t *after);
44
45 #define EDICT_FROM_AREA(l) ((edict_t *)((qbyte *)l - (int)&(((edict_t *)0)->area)))
46
47 //============================================================================
48
49 // ClearLink is used for new headnodes
50 void ClearLink (link_t *l)
51 {
52         l->prev = l->next = l;
53 }
54
55 void RemoveLink (link_t *l)
56 {
57         l->next->prev = l->prev;
58         l->prev->next = l->next;
59 }
60
61 void InsertLinkBefore (link_t *l, link_t *before)
62 {
63         l->next = before;
64         l->prev = before->prev;
65         l->prev->next = l;
66         l->next->prev = l;
67 }
68 void InsertLinkAfter (link_t *l, link_t *after)
69 {
70         l->next = after->next;
71         l->prev = after;
72         l->prev->next = l;
73         l->next->prev = l;
74 }
75
76
77 typedef struct
78 {
79         // bounding box of entire move area
80         vec3_t          boxmins, boxmaxs;
81
82         // size of the moving object
83         vec3_t          mins, maxs;
84
85         // size when clipping against monsters
86         vec3_t          mins2, maxs2;
87
88         // size when clipping against brush models
89         vec3_t          hullmins, hullmaxs;
90
91         // start and end origin of move
92         vec3_t          start, end;
93
94         // trace results
95         trace_t         trace;
96
97         // type of move (like ignoring monsters, or similar)
98         int                     type;
99
100         // the edict that is moving (if any)
101         edict_t         *passedict;
102 }
103 moveclip_t;
104
105
106 /*
107 ===============================================================================
108
109 ENTITY AREA CHECKING
110
111 ===============================================================================
112 */
113
114 typedef struct areanode_s
115 {
116         int             axis;           // -1 = leaf node
117         float   dist;
118         struct areanode_s       *children[2];
119         link_t  trigger_edicts;
120         link_t  solid_edicts;
121 } areanode_t;
122
123 #define AREA_DEPTH      4
124 #define AREA_NODES      32
125
126 static  areanode_t      sv_areanodes[AREA_NODES];
127 static  int                     sv_numareanodes;
128
129 /*
130 ===============
131 SV_CreateAreaNode
132
133 ===============
134 */
135 areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs)
136 {
137         areanode_t      *anode;
138         vec3_t          size;
139         vec3_t          mins1, maxs1, mins2, maxs2;
140
141         anode = &sv_areanodes[sv_numareanodes];
142         sv_numareanodes++;
143
144         ClearLink (&anode->trigger_edicts);
145         ClearLink (&anode->solid_edicts);
146
147         if (depth == AREA_DEPTH)
148         {
149                 anode->axis = -1;
150                 anode->children[0] = anode->children[1] = NULL;
151                 return anode;
152         }
153
154         VectorSubtract (maxs, mins, size);
155         if (size[0] > size[1])
156                 anode->axis = 0;
157         else
158                 anode->axis = 1;
159
160         anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]);
161         VectorCopy (mins, mins1);
162         VectorCopy (mins, mins2);
163         VectorCopy (maxs, maxs1);
164         VectorCopy (maxs, maxs2);
165
166         maxs1[anode->axis] = mins2[anode->axis] = anode->dist;
167
168         anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2);
169         anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1);
170
171         return anode;
172 }
173
174 /*
175 ===============
176 SV_ClearWorld
177
178 ===============
179 */
180 void SV_ClearWorld (void)
181 {
182         Collision_Init ();
183
184         memset (sv_areanodes, 0, sizeof(sv_areanodes));
185         sv_numareanodes = 0;
186         Mod_CheckLoaded(sv.worldmodel);
187         SV_CreateAreaNode (0, sv.worldmodel->normalmins, sv.worldmodel->normalmaxs);
188 }
189
190
191 /*
192 ===============
193 SV_UnlinkEdict
194
195 ===============
196 */
197 void SV_UnlinkEdict (edict_t *ent)
198 {
199         if (!ent->area.prev)
200                 return;         // not linked in anywhere
201         RemoveLink (&ent->area);
202         ent->area.prev = ent->area.next = NULL;
203 }
204
205
206 /*
207 ====================
208 SV_TouchLinks
209 ====================
210 */
211 void SV_TouchLinks ( edict_t *ent, areanode_t *node )
212 {
213         link_t          *l, *next;
214         edict_t         *touch;
215         int                     old_self, old_other;
216
217 loc0:
218 // touch linked edicts
219         for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = next)
220         {
221                 next = l->next;
222                 touch = EDICT_FROM_AREA(l);
223                 if (touch == ent)
224                         continue;
225                 if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER)
226                         continue;
227                 if (ent->v.absmin[0] > touch->v.absmax[0]
228                  || ent->v.absmin[1] > touch->v.absmax[1]
229                  || ent->v.absmin[2] > touch->v.absmax[2]
230                  || ent->v.absmax[0] < touch->v.absmin[0]
231                  || ent->v.absmax[1] < touch->v.absmin[1]
232                  || ent->v.absmax[2] < touch->v.absmin[2])
233                         continue;
234                 old_self = pr_global_struct->self;
235                 old_other = pr_global_struct->other;
236
237                 pr_global_struct->self = EDICT_TO_PROG(touch);
238                 pr_global_struct->other = EDICT_TO_PROG(ent);
239                 pr_global_struct->time = sv.time;
240                 PR_ExecuteProgram (touch->v.touch, "");
241
242                 pr_global_struct->self = old_self;
243                 pr_global_struct->other = old_other;
244         }
245
246 // recurse down both sides
247         if (node->axis == -1)
248                 return;
249
250         if (ent->v.absmax[node->axis] > node->dist)
251         {
252                 if (ent->v.absmin[node->axis] < node->dist)
253                         SV_TouchLinks(ent, node->children[1]); // order reversed to reduce code
254                 node = node->children[0];
255                 goto loc0;
256         }
257         else
258         {
259                 if (ent->v.absmin[node->axis] < node->dist)
260                 {
261                         node = node->children[1];
262                         goto loc0;
263                 }
264         }
265 }
266
267
268 /*
269 ===============
270 SV_LinkEdict
271
272 ===============
273 */
274 void SV_LinkEdict (edict_t *ent, qboolean touch_triggers)
275 {
276         model_t         *model;
277         areanode_t      *node;
278
279         if (ent->area.prev)
280                 SV_UnlinkEdict (ent);   // unlink from old position
281
282         if (ent == sv.edicts)
283                 return;         // don't add the world
284
285         if (ent->free)
286                 return;
287
288 // set the abs box
289
290         if (ent->v.solid == SOLID_BSP)
291         {
292                 if (ent->v.modelindex < 0 || ent->v.modelindex > MAX_MODELS)
293                         PR_RunError("SOLID_BSP with invalid modelindex!\n");
294                 model = sv.models[(int) ent->v.modelindex];
295                 if (model != NULL)
296                 {
297                         if (model->type != mod_brush)
298                                 PR_RunError("SOLID_BSP with non-BSP model\n");
299
300                         if (ent->v.angles[0] || ent->v.angles[2] || ent->v.avelocity[0] || ent->v.avelocity[2])
301                         {
302                                 VectorAdd(ent->v.origin, model->rotatedmins, ent->v.absmin);
303                                 VectorAdd(ent->v.origin, model->rotatedmaxs, ent->v.absmax);
304                         }
305                         else if (ent->v.angles[1] || ent->v.avelocity[1])
306                         {
307                                 VectorAdd(ent->v.origin, model->yawmins, ent->v.absmin);
308                                 VectorAdd(ent->v.origin, model->yawmaxs, ent->v.absmax);
309                         }
310                         else
311                         {
312                                 VectorAdd(ent->v.origin, model->normalmins, ent->v.absmin);
313                                 VectorAdd(ent->v.origin, model->normalmaxs, ent->v.absmax);
314                         }
315                 }
316                 else
317                 {
318                         // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
319                         VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin);
320                         VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax);
321                 }
322         }
323         else
324         {
325                 VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin);
326                 VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax);
327         }
328
329 //
330 // to make items easier to pick up and allow them to be grabbed off
331 // of shelves, the abs sizes are expanded
332 //
333         if ((int)ent->v.flags & FL_ITEM)
334         {
335                 ent->v.absmin[0] -= 15;
336                 ent->v.absmin[1] -= 15;
337                 ent->v.absmin[2] -= 1;
338                 ent->v.absmax[0] += 15;
339                 ent->v.absmax[1] += 15;
340                 ent->v.absmax[2] += 1;
341         }
342         else
343         {
344                 // because movement is clipped an epsilon away from an actual edge,
345                 // we must fully check even when bounding boxes don't quite touch
346                 ent->v.absmin[0] -= 1;
347                 ent->v.absmin[1] -= 1;
348                 ent->v.absmin[2] -= 1;
349                 ent->v.absmax[0] += 1;
350                 ent->v.absmax[1] += 1;
351                 ent->v.absmax[2] += 1;
352         }
353
354         if (ent->v.solid == SOLID_NOT)
355                 return;
356
357 // find the first node that the ent's box crosses
358         node = sv_areanodes;
359         while (1)
360         {
361                 if (node->axis == -1)
362                         break;
363                 if (ent->v.absmin[node->axis] > node->dist)
364                         node = node->children[0];
365                 else if (ent->v.absmax[node->axis] < node->dist)
366                         node = node->children[1];
367                 else
368                         break;          // crosses the node
369         }
370
371 // link it in
372
373         if (ent->v.solid == SOLID_TRIGGER)
374                 InsertLinkBefore (&ent->area, &node->trigger_edicts);
375         else
376                 InsertLinkBefore (&ent->area, &node->solid_edicts);
377
378 // if touch_triggers, touch all entities at this node and descend for more
379         if (touch_triggers)
380                 SV_TouchLinks ( ent, sv_areanodes );
381 }
382
383
384
385 /*
386 ===============================================================================
387
388 POINT TESTING IN HULLS
389
390 ===============================================================================
391 */
392
393 /*
394 ============
395 SV_TestEntityPosition
396
397 This could be a lot more efficient...
398 ============
399 */
400 int SV_TestEntityPosition (edict_t *ent)
401 {
402         return SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent).startsolid;
403 }
404
405
406 /*
407 ===============================================================================
408
409 LINE TESTING IN HULLS
410
411 ===============================================================================
412 */
413
414 /*
415 ==================
416 SV_ClipMoveToEntity
417
418 Handles selection or creation of a clipping hull, and offseting (and
419 eventually rotation) of the end points
420 ==================
421 */
422 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
423 {
424         int i;
425         trace_t trace;
426         model_t *model;
427
428         i = ent->v.modelindex;
429         if ((unsigned int) i >= MAX_MODELS)
430                 PR_RunError("SV_ClipMoveToEntity: invalid modelindex\n");
431         model = sv.models[i];
432         if (i != 0 && model == NULL)
433                 PR_RunError("SV_ClipMoveToEntity: invalid modelindex\n");
434
435         if ((int) ent->v.solid == SOLID_BSP)
436         {
437                 Mod_CheckLoaded(model);
438                 if (model->type != mod_brush)
439                 {
440                         Con_Printf ("SV_ClipMoveToEntity: SOLID_BSP with a non bsp model, entity dump:\n");
441                         ED_Print (ent);
442                         Host_Error ("SV_ClipMoveToEntity: SOLID_BSP with a non bsp model\n");
443                 }
444                 if (ent->v.movetype != MOVETYPE_PUSH)
445                         Host_Error ("SV_ClipMoveToEntity: SOLID_BSP without MOVETYPE_PUSH");
446         }
447
448         Collision_ClipTrace(&trace, ent, model, ent->v.origin, ent->v.angles, ent->v.mins, ent->v.maxs, start, mins, maxs, end);
449
450         return trace;
451 }
452
453 //===========================================================================
454
455 /*
456 ====================
457 SV_ClipToLinks
458
459 Mins and maxs enclose the entire area swept by the move
460 ====================
461 */
462 void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip )
463 {
464         link_t          *l, *next;
465         edict_t         *touch;
466         trace_t         trace;
467
468 loc0:
469         if (clip->trace.allsolid)
470                 return;
471 // touch linked edicts
472         for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next)
473         {
474                 next = l->next;
475                 touch = EDICT_FROM_AREA(l);
476                 if (touch->v.solid == SOLID_NOT)
477                         continue;
478                 if (touch == clip->passedict)
479                         continue;
480                 if (touch->v.solid == SOLID_TRIGGER)
481                 {
482                         ED_Print(touch);
483                         Host_Error ("Trigger in clipping list");
484                 }
485
486                 if (clip->type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP)
487                         continue;
488
489                 if (clip->boxmins[0] > touch->v.absmax[0]
490                  || clip->boxmaxs[0] < touch->v.absmin[0]
491                  || clip->boxmins[1] > touch->v.absmax[1]
492                  || clip->boxmaxs[1] < touch->v.absmin[1]
493                  || clip->boxmins[2] > touch->v.absmax[2]
494                  || clip->boxmaxs[2] < touch->v.absmin[2])
495                         continue;
496
497                 if (clip->passedict)
498                 {
499                         if (clip->passedict->v.size[0] && !touch->v.size[0])
500                                 continue;       // points never interact
501                         if (PROG_TO_EDICT(touch->v.owner) == clip->passedict)
502                                 continue;       // don't clip against own missiles
503                         if (PROG_TO_EDICT(clip->passedict->v.owner) == touch)
504                                 continue;       // don't clip against owner
505                         // LordHavoc: corpse code
506                         if (clip->passedict->v.solid == SOLID_CORPSE && (touch->v.solid == SOLID_SLIDEBOX || touch->v.solid == SOLID_CORPSE))
507                                 continue;
508                         if (clip->passedict->v.solid == SOLID_SLIDEBOX && touch->v.solid == SOLID_CORPSE)
509                                 continue;
510                 }
511
512                 // might interact, so do an exact clip
513                 if (touch->v.solid == SOLID_BSP)
514                         trace = SV_ClipMoveToEntity (touch, clip->start, clip->hullmins, clip->hullmaxs, clip->end);
515                 else if ((int)touch->v.flags & FL_MONSTER)
516                         trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end);
517                 else
518                         trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end);
519                 // LordHavoc: take the 'best' answers from the new trace and combine with existing data
520                 if (trace.allsolid)
521                         clip->trace.allsolid = true;
522                 if (trace.startsolid)
523                 {
524                         clip->trace.startsolid = true;
525                         if (!clip->trace.ent)
526                                 clip->trace.ent = trace.ent;
527                 }
528                 if (trace.inopen)
529                         clip->trace.inopen = true;
530                 if (trace.inwater)
531                         clip->trace.inwater = true;
532                 if (trace.fraction < clip->trace.fraction)
533                 {
534                         clip->trace.fraction = trace.fraction;
535                         VectorCopy(trace.endpos, clip->trace.endpos);
536                         clip->trace.plane = trace.plane;
537                         clip->trace.endcontents = trace.endcontents;
538                         clip->trace.ent = trace.ent;
539                 }
540         }
541
542 // recurse down both sides
543         if (node->axis == -1)
544                 return;
545
546         if (clip->boxmaxs[node->axis] > node->dist)
547         {
548                 if (clip->boxmins[node->axis] < node->dist)
549                         SV_ClipToLinks(node->children[1], clip);
550                 node = node->children[0];
551                 goto loc0;
552         }
553         else if (clip->boxmins[node->axis] < node->dist)
554         {
555                 node = node->children[1];
556                 goto loc0;
557         }
558 }
559
560
561 /*
562 ==================
563 SV_MoveBounds
564 ==================
565 */
566 void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs)
567 {
568         if (sv_useareanodes.integer)
569         {
570                 int i;
571
572                 for (i=0 ; i<3 ; i++)
573                 {
574                         if (end[i] > start[i])
575                         {
576                                 boxmins[i] = start[i] + mins[i] - 1;
577                                 boxmaxs[i] = end[i] + maxs[i] + 1;
578                         }
579                         else
580                         {
581                                 boxmins[i] = end[i] + mins[i] - 1;
582                                 boxmaxs[i] = start[i] + maxs[i] + 1;
583                         }
584                 }
585         }
586         else
587         {
588                 // debug to test against everything
589                 boxmins[0] = boxmins[1] = boxmins[2] = -999999999;
590                 boxmaxs[0] = boxmaxs[1] = boxmaxs[2] =  999999999;
591         }
592 }
593
594 /*
595 ==================
596 SV_Move
597 ==================
598 */
599 trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict)
600 {
601         moveclip_t      clip;
602         vec3_t          bigmins, bigmaxs;
603         int                     i;
604
605         memset ( &clip, 0, sizeof ( moveclip_t ) );
606
607         VectorCopy(start, clip.start);
608         VectorCopy(end, clip.end);
609         VectorCopy(mins, clip.mins);
610         VectorCopy(maxs, clip.maxs);
611         clip.type = type;
612         clip.passedict = passedict;
613
614         Collision_RoundUpToHullSize(sv.worldmodel, clip.mins, clip.maxs, clip.hullmins, clip.hullmaxs);
615
616         if (type == MOVE_MISSILE)
617         {
618                 // LordHavoc: modified this, was = -15, now = clip.mins[i] - 15
619                 for (i=0 ; i<3 ; i++)
620                 {
621                         clip.mins2[i] = clip.mins[i] - 15;
622                         clip.maxs2[i] = clip.maxs[i] + 15;
623                 }
624         }
625         else
626         {
627                 VectorCopy (clip.mins, clip.mins2);
628                 VectorCopy (clip.maxs, clip.maxs2);
629         }
630
631         bigmins[0] = min(clip.mins2[0], clip.hullmins[0]);
632         bigmaxs[0] = max(clip.maxs2[0], clip.hullmaxs[0]);
633         bigmins[1] = min(clip.mins2[1], clip.hullmins[1]);
634         bigmaxs[1] = max(clip.maxs2[1], clip.hullmaxs[1]);
635         bigmins[2] = min(clip.mins2[2], clip.hullmins[2]);
636         bigmaxs[2] = max(clip.maxs2[2], clip.hullmaxs[2]);
637
638         // clip to world
639         clip.trace = SV_ClipMoveToEntity (sv.edicts, start, mins, maxs, end);
640
641         // clip to entities
642         // create the bounding box of the entire move
643         SV_MoveBounds ( start, bigmins, bigmaxs, end, clip.boxmins, clip.boxmaxs );
644
645         SV_ClipToLinks ( sv_areanodes, &clip );
646
647         return clip.trace;
648 }
649