]> icculus.org git repositories - divverent/darkplaces.git/blob - sv_phys.c
Initial revision
[divverent/darkplaces.git] / sv_phys.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 // sv_phys.c
21
22 #include "quakedef.h"
23
24 /*
25
26
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
28
29 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects 
30
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38 solid_edge items only clip against bsp models.
39
40 */
41
42 cvar_t  sv_friction = {"sv_friction","4",false,true};
43 cvar_t  sv_stopspeed = {"sv_stopspeed","100"};
44 cvar_t  sv_gravity = {"sv_gravity","800",false,true};
45 cvar_t  sv_maxvelocity = {"sv_maxvelocity","2000"};
46 cvar_t  sv_nostep = {"sv_nostep","0"};
47
48 static  vec3_t  vec_origin = {0.0, 0.0, 0.0};
49
50 #define MOVE_EPSILON    0.01
51
52 void SV_Physics_Toss (edict_t *ent);
53
54 /*
55 ================
56 SV_CheckAllEnts
57 ================
58 */
59 void SV_CheckAllEnts (void)
60 {
61         int                     e;
62         edict_t         *check;
63
64 // see if any solid entities are inside the final position
65         check = NEXT_EDICT(sv.edicts);
66         for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
67         {
68                 if (check->free)
69                         continue;
70                 if (check->v.movetype == MOVETYPE_PUSH
71                 || check->v.movetype == MOVETYPE_NONE
72                 || check->v.movetype == MOVETYPE_FOLLOW
73                 || check->v.movetype == MOVETYPE_NOCLIP)
74                         continue;
75
76                 if (SV_TestEntityPosition (check))
77                         Con_Printf ("entity in invalid position\n");
78         }
79 }
80
81 /*
82 ================
83 SV_CheckVelocity
84 ================
85 */
86 void SV_CheckVelocity (edict_t *ent)
87 {
88         int             i;
89         float   wishspeed;
90
91 //
92 // bound velocity
93 //
94         for (i=0 ; i<3 ; i++)
95         {
96                 if (IS_NAN(ent->v.velocity[i]))
97                 {
98                         Con_Printf ("Got a NaN velocity on %s\n", pr_strings + ent->v.classname);
99                         ent->v.velocity[i] = 0;
100                 }
101                 if (IS_NAN(ent->v.origin[i]))
102                 {
103                         Con_Printf ("Got a NaN origin on %s\n", pr_strings + ent->v.classname);
104                         ent->v.origin[i] = 0;
105                 }
106                 // LordHavoc: maxvelocity fix, see below
107 /*
108                 if (ent->v.velocity[i] > sv_maxvelocity.value)
109                         ent->v.velocity[i] = sv_maxvelocity.value;
110                 else if (ent->v.velocity[i] < -sv_maxvelocity.value)
111                         ent->v.velocity[i] = -sv_maxvelocity.value;
112 */
113         }
114
115         // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
116         wishspeed = DotProduct(ent->v.velocity, ent->v.velocity);
117         if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
118         {
119                 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
120                 ent->v.velocity[0] *= wishspeed;
121                 ent->v.velocity[1] *= wishspeed;
122                 ent->v.velocity[2] *= wishspeed;
123                 wishspeed = sv_maxvelocity.value;
124         }
125 }
126
127 /*
128 =============
129 SV_RunThink
130
131 Runs thinking code if time.  There is some play in the exact time the think
132 function will be called, because it is called before any movement is done
133 in a frame.  Not used for pushmove objects, because they must be exact.
134 Returns false if the entity removed itself.
135 =============
136 */
137 qboolean SV_RunThink (edict_t *ent)
138 {
139         float   thinktime;
140
141         thinktime = ent->v.nextthink;
142         if (thinktime <= 0 || thinktime > sv.time + host_frametime)
143                 return true;
144                 
145         if (thinktime < sv.time)
146                 thinktime = sv.time;    // don't let things stay in the past.
147                                                                 // it is possible to start that way
148                                                                 // by a trigger with a local time.
149         ent->v.nextthink = 0;
150         pr_global_struct->time = thinktime;
151         pr_global_struct->self = EDICT_TO_PROG(ent);
152         pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
153         PR_ExecuteProgram (ent->v.think);
154         return !ent->free;
155 }
156
157 /*
158 ==================
159 SV_Impact
160
161 Two entities have touched, so run their touch functions
162 ==================
163 */
164 void SV_Impact (edict_t *e1, edict_t *e2)
165 {
166         int             old_self, old_other;
167         
168         old_self = pr_global_struct->self;
169         old_other = pr_global_struct->other;
170         
171         pr_global_struct->time = sv.time;
172         if (e1->v.touch && e1->v.solid != SOLID_NOT)
173         {
174                 pr_global_struct->self = EDICT_TO_PROG(e1);
175                 pr_global_struct->other = EDICT_TO_PROG(e2);
176                 PR_ExecuteProgram (e1->v.touch);
177         }
178         
179         if (e2->v.touch && e2->v.solid != SOLID_NOT)
180         {
181                 pr_global_struct->self = EDICT_TO_PROG(e2);
182                 pr_global_struct->other = EDICT_TO_PROG(e1);
183                 PR_ExecuteProgram (e2->v.touch);
184         }
185
186         pr_global_struct->self = old_self;
187         pr_global_struct->other = old_other;
188 }
189
190
191 /*
192 ==================
193 ClipVelocity
194
195 Slide off of the impacting object
196 returns the blocked flags (1 = floor, 2 = step / wall)
197 ==================
198 */
199 #define STOP_EPSILON    0.1
200
201 int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
202 {
203         float   backoff;
204         float   change;
205         int             i, blocked;
206         
207         blocked = 0;
208         if (normal[2] > 0)
209                 blocked |= 1;           // floor
210         if (!normal[2])
211                 blocked |= 2;           // step
212         
213         backoff = DotProduct (in, normal) * overbounce;
214
215         for (i=0 ; i<3 ; i++)
216         {
217                 change = normal[i]*backoff;
218                 out[i] = in[i] - change;
219                 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
220                         out[i] = 0;
221         }
222         
223         return blocked;
224 }
225
226
227 /*
228 ============
229 SV_FlyMove
230
231 The basic solid body movement clip that slides along multiple planes
232 Returns the clipflags if the velocity was modified (hit something solid)
233 1 = floor
234 2 = wall / step
235 4 = dead stop
236 If steptrace is not NULL, the trace of any vertical wall hit will be stored
237 ============
238 */
239 #define MAX_CLIP_PLANES 5
240 int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace)
241 {
242         int                     bumpcount, numbumps;
243         vec3_t          dir;
244         float           d;
245         int                     numplanes;
246         vec3_t          planes[MAX_CLIP_PLANES];
247         vec3_t          primal_velocity, original_velocity, new_velocity;
248         int                     i, j;
249         trace_t         trace;
250         vec3_t          end;
251         float           time_left;
252         int                     blocked;
253         
254         numbumps = 4;
255         
256         blocked = 0;
257         VectorCopy (ent->v.velocity, original_velocity);
258         VectorCopy (ent->v.velocity, primal_velocity);
259         numplanes = 0;
260         
261         time_left = time;
262
263         for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
264         {
265                 if (!ent->v.velocity[0] && !ent->v.velocity[1] && !ent->v.velocity[2])
266                         break;
267
268                 for (i=0 ; i<3 ; i++)
269                         end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i];
270
271                 trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
272
273                 if (trace.allsolid)
274                 {       // entity is trapped in another solid
275                         VectorCopy (vec3_origin, ent->v.velocity);
276                         return 3;
277                 }
278
279                 if (trace.fraction > 0)
280                 {       // actually covered some distance
281                         VectorCopy (trace.endpos, ent->v.origin);
282                         VectorCopy (ent->v.velocity, original_velocity);
283                         numplanes = 0;
284                 }
285
286                 if (trace.fraction == 1)
287                          break;         // moved the entire distance
288
289                 if (!trace.ent)
290                         Sys_Error ("SV_FlyMove: !trace.ent");
291
292                 if (trace.plane.normal[2] > 0.7)
293                 {
294                         blocked |= 1;           // floor
295                         if (trace.ent->v.solid == SOLID_BSP)
296                         {
297                                 ent->v.flags =  (int)ent->v.flags | FL_ONGROUND;
298                                 ent->v.groundentity = EDICT_TO_PROG(trace.ent);
299                         }
300                 }
301                 if (!trace.plane.normal[2])
302                 {
303                         blocked |= 2;           // step
304                         if (steptrace)
305                                 *steptrace = trace;     // save for player extrafriction
306                 }
307
308 //
309 // run the impact function
310 //
311                 SV_Impact (ent, trace.ent);
312                 if (ent->free)
313                         break;          // removed by the impact function
314
315                 
316                 time_left -= time_left * trace.fraction;
317                 
318         // cliped to another plane
319                 if (numplanes >= MAX_CLIP_PLANES)
320                 {       // this shouldn't really happen
321                         VectorCopy (vec3_origin, ent->v.velocity);
322                         return 3;
323                 }
324
325                 VectorCopy (trace.plane.normal, planes[numplanes]);
326                 numplanes++;
327
328 //
329 // modify original_velocity so it parallels all of the clip planes
330 //
331                 for (i=0 ; i<numplanes ; i++)
332                 {
333                         ClipVelocity (original_velocity, planes[i], new_velocity, 1);
334                         for (j=0 ; j<numplanes ; j++)
335                                 if (j != i)
336                                 {
337                                         if (DotProduct (new_velocity, planes[j]) < 0)
338                                                 break;  // not ok
339                                 }
340                         if (j == numplanes)
341                                 break;
342                 }
343                 
344                 if (i != numplanes)
345                 {       // go along this plane
346                         VectorCopy (new_velocity, ent->v.velocity);
347                 }
348                 else
349                 {       // go along the crease
350                         if (numplanes != 2)
351                         {
352 //                              Con_Printf ("clip velocity, numplanes == %i\n",numplanes);
353                                 VectorCopy (vec3_origin, ent->v.velocity);
354                                 return 7;
355                         }
356                         CrossProduct (planes[0], planes[1], dir);
357                         d = DotProduct (dir, ent->v.velocity);
358                         VectorScale (dir, d, ent->v.velocity);
359                 }
360
361 //
362 // if original velocity is against the original velocity, stop dead
363 // to avoid tiny occilations in sloping corners
364 //
365                 if (DotProduct (ent->v.velocity, primal_velocity) <= 0)
366                 {
367                         VectorCopy (vec3_origin, ent->v.velocity);
368                         return blocked;
369                 }
370         }
371
372         return blocked;
373 }
374
375
376 /*
377 ============
378 SV_AddGravity
379
380 ============
381 */
382 void SV_AddGravity (edict_t *ent)
383 {
384         float   ent_gravity;
385
386         eval_t  *val;
387
388         val = GETEDICTFIELDVALUE(ent, eval_gravity);
389         if (val && val->_float)
390                 ent_gravity = val->_float;
391         else
392                 ent_gravity = 1.0;
393         ent->v.velocity[2] -= ent_gravity * sv_gravity.value * host_frametime;
394 }
395
396
397 /*
398 ===============================================================================
399
400 PUSHMOVE
401
402 ===============================================================================
403 */
404
405 /*
406 ============
407 SV_PushEntity
408
409 Does not change the entities velocity at all
410 ============
411 */
412 trace_t SV_PushEntity (edict_t *ent, vec3_t push)
413 {
414         trace_t trace;
415         vec3_t  end;
416                 
417         VectorAdd (ent->v.origin, push, end);
418
419         if (ent->v.movetype == MOVETYPE_FLYMISSILE)
420                 trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent);
421         else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT)
422         // only clip against bmodels
423                 trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent);
424         else
425                 trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent);       
426         
427         VectorCopy (trace.endpos, ent->v.origin);
428         SV_LinkEdict (ent, true);
429
430         if (trace.ent)
431                 SV_Impact (ent, trace.ent);             
432
433         return trace;
434 }                                       
435
436
437 /*
438 ============
439 SV_PushMove
440
441 ============
442 */
443 void SV_PushMove (edict_t *pusher, float movetime)
444 {
445         int                     i, e;
446         edict_t         *check, *block;
447         vec3_t          mins, maxs, move;
448         vec3_t          entorig, pushorig;
449         int                     num_moved;
450         edict_t         *moved_edict[MAX_EDICTS];
451         vec3_t          moved_from[MAX_EDICTS];
452         float           savesolid;
453
454         if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2])
455         {
456                 pusher->v.ltime += movetime;
457                 return;
458         }
459
460         for (i=0 ; i<3 ; i++)
461         {
462                 move[i] = pusher->v.velocity[i] * movetime;
463                 mins[i] = pusher->v.absmin[i] + move[i];
464                 maxs[i] = pusher->v.absmax[i] + move[i];
465         }
466
467         VectorCopy (pusher->v.origin, pushorig);
468         
469 // move the pusher to it's final position
470
471         VectorAdd (pusher->v.origin, move, pusher->v.origin);
472         pusher->v.ltime += movetime;
473         SV_LinkEdict (pusher, false);
474
475
476 // see if any solid entities are inside the final position
477         num_moved = 0;
478         check = NEXT_EDICT(sv.edicts);
479         for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
480         {
481                 if (check->free)
482                         continue;
483                 if (check->v.movetype == MOVETYPE_PUSH
484                  || check->v.movetype == MOVETYPE_NONE
485                  || check->v.movetype == MOVETYPE_FOLLOW
486                  || check->v.movetype == MOVETYPE_NOCLIP)
487                         continue;
488
489                 // if the entity is standing on the pusher, it will definitely be moved
490                 if (!(((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher))
491                 {
492                         if (check->v.absmin[0] >= maxs[0]
493                          || check->v.absmin[1] >= maxs[1]
494                          || check->v.absmin[2] >= maxs[2]
495                          || check->v.absmax[0] <= mins[0]
496                          || check->v.absmax[1] <= mins[1]
497                          || check->v.absmax[2] <= mins[2])
498                                 continue;
499
500                         // see if the ent's bbox is inside the pusher's final position
501                         if (!SV_TestEntityPosition (check))
502                                 continue;
503                 }
504
505                 // remove the onground flag for non-players
506                 if (check->v.movetype != MOVETYPE_WALK)
507                         check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
508                 
509                 VectorCopy (check->v.origin, entorig);
510                 VectorCopy (check->v.origin, moved_from[num_moved]);
511                 moved_edict[num_moved] = check;
512                 num_moved++;
513
514                 // LordHavoc: pusher fixes (teleport train bug, etc)
515                 savesolid = pusher->v.solid;
516                 if (savesolid == SOLID_BSP || savesolid == SOLID_BBOX || savesolid == SOLID_SLIDEBOX)
517                 {
518                         // try moving the contacted entity
519                         pusher->v.solid = SOLID_NOT;
520                         SV_PushEntity (check, move);
521                         pusher->v.solid = savesolid; // was SOLID_BSP
522
523                         // if it is still inside the pusher, block
524                         if (block = SV_TestEntityPosition (check))
525                         {       // fail the move
526                                 if (check->v.mins[0] == check->v.maxs[0])
527                                         continue;
528                                 if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
529                                 {       // corpse
530                                         check->v.mins[0] = check->v.mins[1] = 0;
531                                         VectorCopy (check->v.mins, check->v.maxs);
532                                         continue;
533                                 }
534                                 
535                                 VectorCopy (entorig, check->v.origin);
536                                 SV_LinkEdict (check, true);
537
538                                 VectorCopy (pushorig, pusher->v.origin);
539                                 SV_LinkEdict (pusher, false);
540                                 pusher->v.ltime -= movetime;
541
542                                 // if the pusher has a "blocked" function, call it
543                                 // otherwise, just stay in place until the obstacle is gone
544                                 if (pusher->v.blocked)
545                                 {
546                                         pr_global_struct->self = EDICT_TO_PROG(pusher);
547                                         pr_global_struct->other = EDICT_TO_PROG(check);
548                                         PR_ExecuteProgram (pusher->v.blocked);
549                                 }
550                                 
551                                 // move back any entities we already moved
552                                 for (i=0 ; i<num_moved ; i++)
553                                 {
554                                         VectorCopy (moved_from[i], moved_edict[i]->v.origin);
555                                         SV_LinkEdict (moved_edict[i], false);
556                                 }
557                                 return;
558                         }       
559                 }
560         }
561
562         
563 }
564
565 /*
566 ============
567 SV_PushRotate
568
569 ============
570 */
571 void SV_PushRotate (edict_t *pusher, float movetime)
572 {
573         int                     i, e;
574         edict_t         *check, *block;
575         vec3_t          move, a, amove;
576         vec3_t          entorig, pushorig;
577         int                     num_moved;
578         edict_t         *moved_edict[MAX_EDICTS];
579         vec3_t          moved_from[MAX_EDICTS];
580         vec3_t          org, org2;
581         vec3_t          forward, right, up;
582         float           savesolid;
583
584         if (!pusher->v.avelocity[0] && !pusher->v.avelocity[1] && !pusher->v.avelocity[2])
585         {
586                 pusher->v.ltime += movetime;
587                 return;
588         }
589
590         for (i=0 ; i<3 ; i++)
591                 amove[i] = pusher->v.avelocity[i] * movetime;
592
593         VectorSubtract (vec3_origin, amove, a);
594         AngleVectors (a, forward, right, up);
595
596         VectorCopy (pusher->v.angles, pushorig);
597         
598 // move the pusher to it's final position
599
600         VectorAdd (pusher->v.angles, amove, pusher->v.angles);
601         pusher->v.ltime += movetime;
602         SV_LinkEdict (pusher, false);
603
604
605 // see if any solid entities are inside the final position
606         num_moved = 0;
607         check = NEXT_EDICT(sv.edicts);
608         for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
609         {
610                 if (check->free)
611                         continue;
612                 if (check->v.movetype == MOVETYPE_PUSH
613                 || check->v.movetype == MOVETYPE_NONE
614                 || check->v.movetype == MOVETYPE_FOLLOW
615                 || check->v.movetype == MOVETYPE_NOCLIP)
616                         continue;
617
618         // if the entity is standing on the pusher, it will definately be moved
619                 if ( ! ( ((int)check->v.flags & FL_ONGROUND)
620                 && PROG_TO_EDICT(check->v.groundentity) == pusher) )
621                 {
622                         if ( check->v.absmin[0] >= pusher->v.absmax[0]
623                         || check->v.absmin[1] >= pusher->v.absmax[1]
624                         || check->v.absmin[2] >= pusher->v.absmax[2]
625                         || check->v.absmax[0] <= pusher->v.absmin[0]
626                         || check->v.absmax[1] <= pusher->v.absmin[1]
627                         || check->v.absmax[2] <= pusher->v.absmin[2] )
628                                 continue;
629
630                 // see if the ent's bbox is inside the pusher's final position
631                         if (!SV_TestEntityPosition (check))
632                                 continue;
633                 }
634
635         // remove the onground flag for non-players
636                 if (check->v.movetype != MOVETYPE_WALK)
637                         check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
638                 
639                 VectorCopy (check->v.origin, entorig);
640                 VectorCopy (check->v.origin, moved_from[num_moved]);
641                 moved_edict[num_moved] = check;
642                 num_moved++;
643
644                 // calculate destination position
645                 VectorSubtract (check->v.origin, pusher->v.origin, org);
646                 org2[0] = DotProduct (org, forward);
647                 org2[1] = -DotProduct (org, right);
648                 org2[2] = DotProduct (org, up);
649                 VectorSubtract (org2, org, move);
650
651                 // try moving the contacted entity 
652                 savesolid = pusher->v.solid; // LordHavoc: restore to correct solid type
653                 pusher->v.solid = SOLID_NOT;
654                 SV_PushEntity (check, move);
655                 pusher->v.solid = savesolid; // LordHavoc: restore to correct solid type
656
657         // if it is still inside the pusher, block
658                 block = SV_TestEntityPosition (check);
659                 if (block)
660                 {       // fail the move
661                         if (check->v.mins[0] == check->v.maxs[0])
662                                 continue;
663                         if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
664                         {       // corpse
665                                 check->v.mins[0] = check->v.mins[1] = 0;
666                                 VectorCopy (check->v.mins, check->v.maxs);
667                                 continue;
668                         }
669                         
670                         VectorCopy (entorig, check->v.origin);
671                         SV_LinkEdict (check, true);
672
673                         VectorCopy (pushorig, pusher->v.angles);
674                         SV_LinkEdict (pusher, false);
675                         pusher->v.ltime -= movetime;
676
677                         // if the pusher has a "blocked" function, call it
678                         // otherwise, just stay in place until the obstacle is gone
679                         if (pusher->v.blocked)
680                         {
681                                 pr_global_struct->self = EDICT_TO_PROG(pusher);
682                                 pr_global_struct->other = EDICT_TO_PROG(check);
683                                 PR_ExecuteProgram (pusher->v.blocked);
684                         }
685                         
686                 // move back any entities we already moved
687                         for (i=0 ; i<num_moved ; i++)
688                         {
689                                 VectorCopy (moved_from[i], moved_edict[i]->v.origin);
690                                 VectorSubtract (moved_edict[i]->v.angles, amove, moved_edict[i]->v.angles);
691                                 SV_LinkEdict (moved_edict[i], false);
692                         }
693                         return;
694                 }
695                 else
696                 {
697                         VectorAdd (check->v.angles, amove, check->v.angles);
698                 }
699         }
700
701         
702 }
703 //#endif
704
705 /*
706 ================
707 SV_Physics_Pusher
708
709 ================
710 */
711 void SV_Physics_Pusher (edict_t *ent)
712 {
713         float   thinktime;
714         float   oldltime;
715         float   movetime;
716
717         oldltime = ent->v.ltime;
718         
719         thinktime = ent->v.nextthink;
720         if (thinktime < ent->v.ltime + host_frametime)
721         {
722                 movetime = thinktime - ent->v.ltime;
723                 if (movetime < 0)
724                         movetime = 0;
725         }
726         else
727                 movetime = host_frametime;
728
729         if (movetime)
730         {
731                 if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2])
732                         SV_PushRotate (ent, movetime);
733                 else
734                         SV_PushMove (ent, movetime);    // advances ent->v.ltime if not blocked
735         }
736                 
737         if (thinktime > oldltime && thinktime <= ent->v.ltime)
738         {
739                 ent->v.nextthink = 0;
740                 pr_global_struct->time = sv.time;
741                 pr_global_struct->self = EDICT_TO_PROG(ent);
742                 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
743                 PR_ExecuteProgram (ent->v.think);
744                 if (ent->free)
745                         return;
746         }
747
748 }
749
750
751 /*
752 ===============================================================================
753
754 CLIENT MOVEMENT
755
756 ===============================================================================
757 */
758
759 /*
760 =============
761 SV_CheckStuck
762
763 This is a big hack to try and fix the rare case of getting stuck in the world
764 clipping hull.
765 =============
766 */
767 void SV_CheckStuck (edict_t *ent)
768 {
769         int             i, j;
770         int             z;
771         vec3_t  org;
772
773         if (!SV_TestEntityPosition(ent))
774         {
775                 VectorCopy (ent->v.origin, ent->v.oldorigin);
776                 return;
777         }
778
779         VectorCopy (ent->v.origin, org);
780         VectorCopy (ent->v.oldorigin, ent->v.origin);
781         if (!SV_TestEntityPosition(ent))
782         {
783                 Con_DPrintf ("Unstuck.\n");
784                 SV_LinkEdict (ent, true);
785                 return;
786         }
787         
788         for (z=0 ; z< 18 ; z++)
789                 for (i=-1 ; i <= 1 ; i++)
790                         for (j=-1 ; j <= 1 ; j++)
791                         {
792                                 ent->v.origin[0] = org[0] + i;
793                                 ent->v.origin[1] = org[1] + j;
794                                 ent->v.origin[2] = org[2] + z;
795                                 if (!SV_TestEntityPosition(ent))
796                                 {
797                                         Con_DPrintf ("Unstuck.\n");
798                                         SV_LinkEdict (ent, true);
799                                         return;
800                                 }
801                         }
802                         
803         VectorCopy (org, ent->v.origin);
804         Con_DPrintf ("player is stuck.\n");
805 }
806
807
808 /*
809 =============
810 SV_CheckWater
811 =============
812 */
813 qboolean SV_CheckWater (edict_t *ent)
814 {
815         vec3_t  point;
816         int             cont;
817
818         point[0] = ent->v.origin[0];
819         point[1] = ent->v.origin[1];
820         point[2] = ent->v.origin[2] + ent->v.mins[2] + 1;       
821         
822         ent->v.waterlevel = 0;
823         ent->v.watertype = CONTENTS_EMPTY;
824         cont = SV_PointContents (point);
825         if (cont <= CONTENTS_WATER)
826         {
827                 ent->v.watertype = cont;
828                 ent->v.waterlevel = 1;
829                 point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2])*0.5;
830                 cont = SV_PointContents (point);
831                 if (cont <= CONTENTS_WATER)
832                 {
833                         ent->v.waterlevel = 2;
834                         point[2] = ent->v.origin[2] + ent->v.view_ofs[2];
835                         cont = SV_PointContents (point);
836                         if (cont <= CONTENTS_WATER)
837                                 ent->v.waterlevel = 3;
838                 }
839         }
840         
841         return ent->v.waterlevel > 1;
842 }
843
844 /*
845 ============
846 SV_WallFriction
847
848 ============
849 */
850 void SV_WallFriction (edict_t *ent, trace_t *trace)
851 {
852         vec3_t          forward, right, up;
853         float           d, i;
854         vec3_t          into, side;
855         
856         AngleVectors (ent->v.v_angle, forward, right, up);
857         d = DotProduct (trace->plane.normal, forward);
858         
859         d += 0.5;
860         if (d >= 0)
861                 return;
862                 
863 // cut the tangential velocity
864         i = DotProduct (trace->plane.normal, ent->v.velocity);
865         VectorScale (trace->plane.normal, i, into);
866         VectorSubtract (ent->v.velocity, into, side);
867         
868         ent->v.velocity[0] = side[0] * (1 + d);
869         ent->v.velocity[1] = side[1] * (1 + d);
870 }
871
872 /*
873 =====================
874 SV_TryUnstick
875
876 Player has come to a dead stop, possibly due to the problem with limited
877 float precision at some angle joins in the BSP hull.
878
879 Try fixing by pushing one pixel in each direction.
880
881 This is a hack, but in the interest of good gameplay...
882 ======================
883 */
884 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
885 {
886         int             i;
887         vec3_t  oldorg;
888         vec3_t  dir;
889         int             clip;
890         trace_t steptrace;
891         
892         VectorCopy (ent->v.origin, oldorg);
893         VectorCopy (vec3_origin, dir);
894
895         for (i=0 ; i<8 ; i++)
896         {
897 // try pushing a little in an axial direction
898                 switch (i)
899                 {
900                         case 0: dir[0] = 2; dir[1] = 0; break;
901                         case 1: dir[0] = 0; dir[1] = 2; break;
902                         case 2: dir[0] = -2; dir[1] = 0; break;
903                         case 3: dir[0] = 0; dir[1] = -2; break;
904                         case 4: dir[0] = 2; dir[1] = 2; break;
905                         case 5: dir[0] = -2; dir[1] = 2; break;
906                         case 6: dir[0] = 2; dir[1] = -2; break;
907                         case 7: dir[0] = -2; dir[1] = -2; break;
908                 }
909                 
910                 SV_PushEntity (ent, dir);
911
912 // retry the original move
913                 ent->v.velocity[0] = oldvel[0];
914                 ent->v. velocity[1] = oldvel[1];
915                 ent->v. velocity[2] = 0;
916                 clip = SV_FlyMove (ent, 0.1, &steptrace);
917
918                 if ( fabs(oldorg[1] - ent->v.origin[1]) > 4
919                 || fabs(oldorg[0] - ent->v.origin[0]) > 4 )
920                 {
921 //Con_DPrintf ("unstuck!\n");
922                         return clip;
923                 }
924                         
925 // go back to the original pos and try again
926                 VectorCopy (oldorg, ent->v.origin);
927         }
928         
929         VectorCopy (vec3_origin, ent->v.velocity);
930         return 7;               // still not moving
931 }
932
933 /*
934 =====================
935 SV_WalkMove
936
937 Only used by players
938 ======================
939 */
940 #define STEPSIZE        18
941 void SV_WalkMove (edict_t *ent)
942 {
943         vec3_t          upmove, downmove;
944         vec3_t          oldorg, oldvel;
945         vec3_t          nosteporg, nostepvel;
946         int                     clip;
947         int                     oldonground;
948         trace_t         steptrace, downtrace;
949         
950 //
951 // do a regular slide move unless it looks like you ran into a step
952 //
953         oldonground = (int)ent->v.flags & FL_ONGROUND;
954         ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
955         
956         VectorCopy (ent->v.origin, oldorg);
957         VectorCopy (ent->v.velocity, oldvel);
958         
959         clip = SV_FlyMove (ent, host_frametime, &steptrace);
960
961         if ( !(clip & 2) )
962                 return;         // move didn't block on a step
963
964         if (!oldonground && ent->v.waterlevel == 0)
965                 return;         // don't stair up while jumping
966         
967         if (ent->v.movetype != MOVETYPE_WALK)
968                 return;         // gibbed by a trigger
969         
970         if (sv_nostep.value)
971                 return;
972         
973         if ( (int)sv_player->v.flags & FL_WATERJUMP )
974                 return;
975
976         VectorCopy (ent->v.origin, nosteporg);
977         VectorCopy (ent->v.velocity, nostepvel);
978
979 //
980 // try moving up and forward to go up a step
981 //
982         VectorCopy (oldorg, ent->v.origin);     // back to start pos
983
984         VectorCopy (vec3_origin, upmove);
985         VectorCopy (vec3_origin, downmove);
986         upmove[2] = STEPSIZE;
987         downmove[2] = -STEPSIZE + oldvel[2]*host_frametime;
988
989 // move up
990         SV_PushEntity (ent, upmove);    // FIXME: don't link?
991
992 // move forward
993         ent->v.velocity[0] = oldvel[0];
994         ent->v. velocity[1] = oldvel[1];
995         ent->v. velocity[2] = 0;
996         clip = SV_FlyMove (ent, host_frametime, &steptrace);
997
998 // check for stuckness, possibly due to the limited precision of floats
999 // in the clipping hulls
1000         if (clip)
1001         {
1002                 if ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125
1003                 && fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 )
1004                 {       // stepping up didn't make any progress
1005                         clip = SV_TryUnstick (ent, oldvel);
1006                 }
1007         }
1008         
1009 // extra friction based on view angle
1010         if ( clip & 2 )
1011                 SV_WallFriction (ent, &steptrace);
1012
1013 // move down
1014         downtrace = SV_PushEntity (ent, downmove);      // FIXME: don't link?
1015
1016         if (downtrace.plane.normal[2] > 0.7)
1017         {
1018                 if (ent->v.solid == SOLID_BSP)
1019                 {
1020                         ent->v.flags =  (int)ent->v.flags | FL_ONGROUND;
1021                         ent->v.groundentity = EDICT_TO_PROG(downtrace.ent);
1022                 }
1023         }
1024         else
1025         {
1026 // if the push down didn't end up on good ground, use the move without
1027 // the step up.  This happens near wall / slope combinations, and can
1028 // cause the player to hop up higher on a slope too steep to climb      
1029                 VectorCopy (nosteporg, ent->v.origin);
1030                 VectorCopy (nostepvel, ent->v.velocity);
1031         }
1032 }
1033
1034
1035 /*
1036 ================
1037 SV_Physics_Client
1038
1039 Player character actions
1040 ================
1041 */
1042 void SV_Physics_Client (edict_t *ent, int num)
1043 {
1044         if ( ! svs.clients[num-1].active )
1045                 return;         // unconnected slot
1046
1047 //
1048 // call standard client pre-think
1049 //      
1050         pr_global_struct->time = sv.time;
1051         pr_global_struct->self = EDICT_TO_PROG(ent);
1052         PR_ExecuteProgram (pr_global_struct->PlayerPreThink);
1053         
1054 //
1055 // do a move
1056 //
1057         SV_CheckVelocity (ent);
1058
1059 //
1060 // decide which move function to call
1061 //
1062         switch ((int)ent->v.movetype)
1063         {
1064         case MOVETYPE_NONE:
1065                 if (!SV_RunThink (ent))
1066                         return;
1067                 break;
1068
1069         case MOVETYPE_WALK:
1070                 if (!SV_RunThink (ent))
1071                         return;
1072                 if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )
1073                         SV_AddGravity (ent);
1074                 SV_CheckStuck (ent);
1075                 SV_WalkMove (ent);
1076                 break;
1077                 
1078         case MOVETYPE_TOSS:
1079         case MOVETYPE_BOUNCE:
1080                 SV_Physics_Toss (ent);
1081                 break;
1082
1083         case MOVETYPE_FLY:
1084                 if (!SV_RunThink (ent))
1085                         return;
1086                 SV_FlyMove (ent, host_frametime, NULL);
1087                 break;
1088                 
1089         case MOVETYPE_NOCLIP:
1090                 if (!SV_RunThink (ent))
1091                         return;
1092                 VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
1093                 break;
1094                 
1095         default:
1096                 Sys_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype);
1097         }
1098
1099 //
1100 // call standard player post-think
1101 //              
1102         SV_LinkEdict (ent, true);
1103
1104         pr_global_struct->time = sv.time;
1105         pr_global_struct->self = EDICT_TO_PROG(ent);
1106         PR_ExecuteProgram (pr_global_struct->PlayerPostThink);
1107 }
1108
1109 //============================================================================
1110
1111 /*
1112 =============
1113 SV_Physics_None
1114
1115 Non moving objects can only think
1116 =============
1117 */
1118 void SV_Physics_None (edict_t *ent)
1119 {
1120 // regular thinking
1121         SV_RunThink (ent);
1122 }
1123
1124 /*
1125 =============
1126 SV_Physics_Follow
1127
1128 Entities that are "stuck" to another entity
1129 =============
1130 */
1131 void SV_Physics_Follow (edict_t *ent)
1132 {
1133         vec3_t vf, vu, vr, angles;
1134         edict_t *e;
1135 // regular thinking
1136         SV_RunThink (ent);
1137         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1138         e = PROG_TO_EDICT(ent->v.aiment);
1139         angles[0] = -e->v.angles[0];
1140         angles[1] = e->v.angles[1];
1141         angles[2] = e->v.angles[2];
1142         AngleVectors (angles, vf, vr, vu);
1143         VectorMA (e->v.origin, ent->v.view_ofs[0], vf, ent->v.origin);
1144         VectorMA (ent->v.origin, ent->v.view_ofs[1], vr, ent->v.origin);
1145         VectorMA (ent->v.origin, ent->v.view_ofs[2], vu, ent->v.origin);
1146         VectorAdd (e->v.angles, ent->v.v_angle, ent->v.angles);
1147 //      VectorAdd (PROG_TO_EDICT(ent->v.aiment)->v.origin, ent->v.v_angle, ent->v.origin);
1148         SV_LinkEdict (ent, true);
1149 }
1150
1151 /*
1152 =============
1153 SV_Physics_Noclip
1154
1155 A moving object that doesn't obey physics
1156 =============
1157 */
1158 void SV_Physics_Noclip (edict_t *ent)
1159 {
1160 // regular thinking
1161         if (!SV_RunThink (ent))
1162                 return;
1163         
1164         VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
1165         VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
1166
1167         SV_LinkEdict (ent, false);
1168 }
1169
1170 /*
1171 ==============================================================================
1172
1173 TOSS / BOUNCE
1174
1175 ==============================================================================
1176 */
1177
1178 /*
1179 =============
1180 SV_CheckWaterTransition
1181
1182 =============
1183 */
1184 void SV_CheckWaterTransition (edict_t *ent)
1185 {
1186         int             cont;
1187         cont = SV_PointContents (ent->v.origin);
1188         if (!ent->v.watertype)
1189         {       // just spawned here
1190                 ent->v.watertype = cont;
1191                 ent->v.waterlevel = 1;
1192                 return;
1193         }
1194         
1195         if (cont <= CONTENTS_WATER)
1196         {
1197                 if (ent->v.watertype == CONTENTS_EMPTY)
1198                 {       // just crossed into water
1199                         SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1200                 }               
1201                 ent->v.watertype = cont;
1202                 ent->v.waterlevel = 1;
1203         }
1204         else
1205         {
1206                 if (ent->v.watertype != CONTENTS_EMPTY)
1207                 {       // just crossed into water
1208                         SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1209                 }               
1210                 ent->v.watertype = CONTENTS_EMPTY;
1211                 ent->v.waterlevel = cont;
1212         }
1213 }
1214
1215 /*
1216 =============
1217 SV_Physics_Toss
1218
1219 Toss, bounce, and fly movement.  When onground, do nothing.
1220 =============
1221 */
1222 void SV_Physics_Toss (edict_t *ent)
1223 {
1224         trace_t trace;
1225         vec3_t  move;
1226         float   backoff;
1227         // regular thinking
1228         if (!SV_RunThink (ent))
1229                 return;
1230
1231 // if onground, return without moving
1232         if ( ((int)ent->v.flags & FL_ONGROUND) )
1233                 return;
1234
1235         SV_CheckVelocity (ent);
1236
1237 // add gravity
1238         if (ent->v.movetype != MOVETYPE_FLY
1239         && ent->v.movetype != MOVETYPE_BOUNCEMISSILE // LordHavoc: enabled MOVETYPE_BOUNCEMISSILE
1240         && ent->v.movetype != MOVETYPE_FLYMISSILE)
1241                 SV_AddGravity (ent);
1242
1243 // move angles
1244         VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
1245
1246 // move origin
1247         VectorScale (ent->v.velocity, host_frametime, move);
1248         trace = SV_PushEntity (ent, move);
1249         if (trace.fraction == 1)
1250                 return;
1251         if (ent->free)
1252                 return;
1253         
1254         if (ent->v.movetype == MOVETYPE_BOUNCE)
1255                 backoff = 1.5;
1256         else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE)
1257                 backoff = 2.0;
1258         else
1259                 backoff = 1;
1260
1261         ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff);
1262
1263 // stop if on ground
1264         if (trace.plane.normal[2] > 0.7)
1265         {               
1266                 if (ent->v.velocity[2] < 60 || (ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE))
1267                 {
1268                         ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
1269                         ent->v.groundentity = EDICT_TO_PROG(trace.ent);
1270                         VectorCopy (vec3_origin, ent->v.velocity);
1271                         VectorCopy (vec3_origin, ent->v.avelocity);
1272                 }
1273         }
1274         
1275 // check for in water
1276         SV_CheckWaterTransition (ent);
1277 }
1278
1279 /*
1280 ===============================================================================
1281
1282 STEPPING MOVEMENT
1283
1284 ===============================================================================
1285 */
1286
1287 /*
1288 =============
1289 SV_Physics_Step
1290
1291 Monsters freefall when they don't have a ground entity, otherwise
1292 all movement is done with discrete steps.
1293
1294 This is also used for objects that have become still on the ground, but
1295 will fall if the floor is pulled out from under them.
1296 =============
1297 */
1298 void SV_Physics_Step (edict_t *ent)
1299 {
1300         qboolean        hitsound;
1301
1302 // freefall if not onground
1303         if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) )
1304         {
1305                 if (ent->v.velocity[2] < sv_gravity.value*-0.1)
1306                         hitsound = true;
1307                 else
1308                         hitsound = false;
1309
1310                 SV_AddGravity (ent);
1311                 SV_CheckVelocity (ent);
1312                 SV_FlyMove (ent, host_frametime, NULL);
1313                 SV_LinkEdict (ent, true);
1314
1315                 if ( (int)ent->v.flags & FL_ONGROUND )  // just hit ground
1316                 {
1317                         if (hitsound)
1318                                 SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1);
1319                 }
1320         }
1321
1322 // regular thinking
1323         SV_RunThink (ent);
1324         
1325         SV_CheckWaterTransition (ent);
1326 }
1327
1328 //============================================================================
1329
1330 /*
1331 ================
1332 SV_Physics
1333
1334 ================
1335 */
1336 extern dfunction_t *EndFrameQC;
1337 void SV_Physics (void)
1338 {
1339         int             i;
1340         edict_t *ent;
1341
1342 // let the progs know that a new frame has started
1343         pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1344         pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1345         pr_global_struct->time = sv.time;
1346         PR_ExecuteProgram (pr_global_struct->StartFrame);
1347
1348 //SV_CheckAllEnts ();
1349
1350 //
1351 // treat each object in turn
1352 //
1353         ent = sv.edicts;
1354         for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1355         {
1356                 if (ent->free)
1357                         continue;
1358
1359                 if (pr_global_struct->force_retouch)
1360                 {
1361                         SV_LinkEdict (ent, true);       // force retouch even for stationary
1362                 }
1363
1364                 if (i > 0 && i <= svs.maxclients)
1365                         SV_Physics_Client (ent, i);
1366                 else if (ent->v.movetype == MOVETYPE_PUSH)
1367                         SV_Physics_Pusher (ent);
1368                 else if (ent->v.movetype == MOVETYPE_NONE)
1369                         SV_Physics_None (ent);
1370                 else if (ent->v.movetype == MOVETYPE_FOLLOW)
1371                         SV_Physics_Follow (ent);
1372                 else if (ent->v.movetype == MOVETYPE_NOCLIP)
1373                         SV_Physics_Noclip (ent);
1374                 else if (ent->v.movetype == MOVETYPE_STEP)
1375                         SV_Physics_Step (ent);
1376                 // LordHavoc: added support for MOVETYPE_WALK on normal entities! :)
1377                 else if (ent->v.movetype == MOVETYPE_WALK)
1378                 {
1379                         if (SV_RunThink (ent))
1380                         {
1381                                 if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )
1382                                         SV_AddGravity (ent);
1383                                 SV_CheckStuck (ent);
1384                                 SV_WalkMove (ent);
1385                         }
1386                 }
1387                 else if (ent->v.movetype == MOVETYPE_TOSS 
1388                 || ent->v.movetype == MOVETYPE_BOUNCE
1389                 || ent->v.movetype == MOVETYPE_BOUNCEMISSILE
1390                 || ent->v.movetype == MOVETYPE_FLY
1391                 || ent->v.movetype == MOVETYPE_FLYMISSILE)
1392                         SV_Physics_Toss (ent);
1393                 else
1394                         Sys_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype);                        
1395         }
1396         
1397         if (pr_global_struct->force_retouch)
1398                 pr_global_struct->force_retouch--;      
1399
1400         // LordHavoc: endframe support
1401         if (EndFrameQC)
1402         {
1403                 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1404                 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1405                 pr_global_struct->time = sv.time;
1406                 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions));
1407         }
1408
1409         sv.time += host_frametime;
1410 }
1411
1412
1413 trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore)
1414 {
1415         int i;
1416         edict_t tempent, *tent;
1417         trace_t trace;
1418         vec3_t  move;
1419         vec3_t  end;
1420         double  save_frametime;
1421
1422         save_frametime = host_frametime;
1423         host_frametime = 0.05;
1424
1425         memcpy(&tempent, ent, sizeof(edict_t));
1426         tent = &tempent;
1427
1428         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1429         {
1430                 SV_CheckVelocity (tent);
1431                 SV_AddGravity (tent);
1432                 VectorMA (tent->v.angles, host_frametime, tent->v.avelocity, tent->v.angles);
1433                 VectorScale (tent->v.velocity, host_frametime, move);
1434                 VectorAdd (tent->v.origin, move, end);
1435                 trace = SV_Move (tent->v.origin, tent->v.mins, tent->v.maxs, end, MOVE_NORMAL, tent);   
1436                 VectorCopy (trace.endpos, tent->v.origin);
1437
1438                 if (trace.ent)
1439                         if (trace.ent != ignore)
1440                                 break;
1441         }
1442         host_frametime = save_frametime;
1443         return trace;
1444 }