added in_pitch_min and in_pitch_max cvars to limit pitch (default: -90 to +90)
[divverent/darkplaces.git] / cgame.c
1
2 #include <string.h>
3 #include "cgame_api.h"
4 #include "cg_math.h"
5
6
7 #ifndef NULL
8 #define NULL ((void *)0)
9 #endif
10
11 static double gametime, frametime;
12
13 struct localentity_s;
14 typedef struct localentity_s
15 {
16         int active; // true if the entity is alive (not freed)
17         float freetime; // time this entity was freed
18         float dietime;
19         vec3_t velocity;
20         vec3_t avelocity;
21         vec3_t worldmins;
22         vec3_t worldmaxs;
23         vec3_t entitymins;
24         vec3_t entitymaxs;
25         vec3_t lastimpactorigin; // updated by physics code, used by gib blood stains
26         float bouncescale;
27         float airfrictionscale;
28         float gravityscale;
29         void (*framethink)(struct localentity_s *e);
30         //int solid;
31         //void (*touch)(struct localentity_s *self, struct localentity_s *other);
32         void (*touchnetwork)(struct localentity_s *self);
33         cgdrawentity_t draw;
34 }
35 localentity_t;
36
37 #define MAX_LOCALENTITIES 1024
38 static localentity_t *localentity;
39
40 static cgphysentity_t *phys_entity;
41 static int phys_entities;
42
43 static float cg_gravity;
44
45 static void readvector(vec3_t v)
46 {
47         v[0] = CGVM_MSG_ReadFloat();
48         v[1] = CGVM_MSG_ReadFloat();
49         v[2] = CGVM_MSG_ReadFloat();
50 }
51
52 static localentity_t *entspawn(void)
53 {
54         int i;
55         localentity_t *l;
56         for (i = 0;i < MAX_LOCALENTITIES;i++)
57         {
58                 l = localentity + i;
59                 if (!l->active && l->freetime < gametime)
60                 {
61                         memset(l, 0, sizeof(*l));
62                         l->active = true;
63                         return l;
64                 }
65         }
66         for (i = 0;i < MAX_LOCALENTITIES;i++)
67         {
68                 l = localentity + i;
69                 if (!l->active)
70                 {
71                         memset(l, 0, sizeof(*l));
72                         l->active = true;
73                         return l;
74                 }
75         }
76         return NULL;
77 }
78
79 static void entremove(localentity_t *e)
80 {
81         memset(e, 0, sizeof(*e));
82         e->freetime = (float)gametime + 1.0f;
83 }
84
85 static void phys_setupphysentities(void)
86 {
87         phys_entities = 0;
88         /*
89         for (i = 0;i < MAX_LOCALENTITIES;i++)
90         {
91                 l = localentities + i;
92                 if (l->active && l->solid)
93                 {
94                 }
95         }
96         */
97 }
98
99 static void phys_moveentities(void)
100 {
101         int i;
102         localentity_t *l;
103         for (i = 0;i < MAX_LOCALENTITIES;i++)
104         {
105                 l = localentity + i;
106                 if (l->active)
107                 {
108                         if (l->framethink)
109                                 l->framethink(l);
110                         if (l->active && l->draw.model)
111                                 CGVM_Draw_Entity(&l->draw);
112                 }
113         }
114 }
115
116 static void phys_updateentities(void)
117 {
118         phys_setupphysentities();
119         phys_moveentities();
120 }
121
122 static void phys_update(localentity_t *e)
123 {
124         vec3_t impactpos, impactnormal, end;
125         int impactentnum;
126         float t, f, frac, bounce;
127         t = (float)frametime;
128         if (t == 0)
129                 return;
130         VectorMA(e->draw.angles, t, e->avelocity, e->draw.angles);
131         VectorMA(e->draw.origin, t, e->velocity, end);
132         frac = CGVM_TracePhysics(e->draw.origin, end, e->worldmins, e->worldmaxs, e->entitymins, e->entitymaxs, phys_entity, phys_entities, impactpos, impactnormal, &impactentnum);
133         VectorCopy(impactpos, e->draw.origin);
134         if (frac < 1)
135         {
136                 bounce = DotProduct(e->velocity, impactnormal) * -e->bouncescale;
137                 VectorMA(e->velocity, bounce, impactnormal, e->velocity);
138                 if (impactnormal[2] >= 0.7 && DotProduct(e->velocity, e->velocity) < 100*100)
139                 {
140                         VectorClear(e->velocity);
141                         VectorClear(e->avelocity);
142                 }
143
144                 if (e->touchnetwork)
145                         e->touchnetwork(e);
146                 // FIXME: do some kind of touch code here if physentities get implemented
147
148                 VectorCopy(impactpos, e->lastimpactorigin);
149         }
150
151         if (e->airfrictionscale)
152         {
153                 if (DotProduct(e->velocity, e->velocity) < 10*10)
154                 {
155                         VectorClear(e->velocity);
156                         VectorClear(e->avelocity);
157                 }
158                 else
159                 {
160                         f = 1 - (t * e->airfrictionscale);
161                         if (f > 0)
162                         {
163                                 VectorScale(e->velocity, f, e->velocity);
164                                 if (DotProduct(e->avelocity, e->avelocity) < 10*10)
165                                 {
166                                         VectorClear(e->avelocity);
167                                 }
168                                 else
169                                 {
170                                         VectorScale(e->avelocity, f, e->avelocity);
171                                 }
172                         }
173                         else
174                         {
175                                 VectorClear(e->velocity);
176                                 VectorClear(e->avelocity);
177                         }
178                 }
179         }
180         if (e->gravityscale)
181                 e->velocity[2] += cg_gravity * e->gravityscale * t;
182 }
183
184 static void explosiondebris_framethink(localentity_t *self)
185 {
186         if (gametime > self->dietime)
187         {
188                 self->draw.scale -= (float)(frametime * 3.0);
189                 if (self->draw.scale < 0.05f)
190                 {
191                         entremove(self);
192                         return;
193                 }
194         }
195         phys_update(self);
196 }
197
198 static void gib_framethink(localentity_t *self)
199 {
200         if (gametime > self->dietime)
201         {
202                 self->draw.scale -= (float)frametime * 3.0f;
203                 if (self->draw.scale < 0.05f)
204                 {
205                         entremove(self);
206                         return;
207                 }
208         }
209         /*
210         if (gametime > self->trailnexttime)
211         {
212                 self->trailnexttime = gametime + 0.1f;
213                 CGVM_BloodParticle(self->draw.origin, self->velocity);
214         }
215         */
216         phys_update(self);
217 }
218
219 static void gib_touchnetwork(localentity_t *self)
220 {
221         if (VectorDistance2(self->draw.origin, self->lastimpactorigin) >= 5*5)
222                 CGVM_Stain(self->draw.origin, 64, 64, 24, 24, 48, 192, 48, 48, 48);
223 }
224
225 static void net_explosion(unsigned char num)
226 {
227         int i;
228         float r;
229         vec3_t org;
230         double time;
231         localentity_t *e;
232         // need the time to know when the rubble should fade
233         time = CGVM_Time();
234         // read the network data
235         readvector(org);
236
237         for (i = 0;i < 40;i++)
238         {
239                 e = entspawn();
240                 if (!e)
241                         return;
242
243                 VectorCopy(org, e->draw.origin);
244                 e->draw.angles[0] = CGVM_RandomRange(0, 360);
245                 e->draw.angles[1] = CGVM_RandomRange(0, 360);
246                 e->draw.angles[2] = CGVM_RandomRange(0, 360);
247                 VectorRandom(e->velocity);
248                 VectorScale(e->velocity, 300, e->velocity);
249                 e->velocity[2] -= (float)cg_gravity * 0.1f;
250                 e->avelocity[0] = CGVM_RandomRange(0, 1440);
251                 e->avelocity[1] = CGVM_RandomRange(0, 1440);
252                 e->avelocity[2] = CGVM_RandomRange(0, 1440);
253                 r = CGVM_RandomRange(0, 3);
254                 if (r < 1)
255                         e->draw.model = CGVM_Model("progs/rubble1.mdl");
256                 else if (r < 2)
257                         e->draw.model = CGVM_Model("progs/rubble2.mdl");
258                 else
259                         e->draw.model = CGVM_Model("progs/rubble3.mdl");
260                 e->draw.alpha = 1;
261                 e->draw.scale = 1;
262                 e->draw.frame1 = 0;
263                 e->draw.frame2 = 0;
264                 e->draw.framelerp = 0;
265                 e->draw.skinnum = 5;
266                 VectorSet(e->worldmins, 0, 0, -8);
267                 VectorSet(e->worldmaxs, 0, 0, -8);
268                 VectorSet(e->entitymins, -8, -8, -8);
269                 VectorSet(e->entitymaxs, 8, 8, 8);
270                 e->bouncescale = 1.4f;
271                 e->gravityscale = 1;
272                 e->airfrictionscale = 1;
273                 e->framethink = explosiondebris_framethink;
274                 e->dietime = (float)time + 5.0f;
275         }
276 }
277
278 static void net_gibshower(unsigned char num)
279 {
280         int i, count;
281         float r, velocityscale;
282         vec3_t org;
283         double time;
284         localentity_t *e;
285         // need the time to know when the gibs should fade
286         time = CGVM_Time();
287         // read the network data
288         count = CGVM_MSG_ReadByte();
289         velocityscale = (float)(CGVM_MSG_ReadByte() * 100);
290         readvector(org);
291
292         for (i = 0;i < count;i++)
293         {
294                 e = entspawn();
295                 if (!e)
296                         return;
297
298                 VectorCopy(org, e->draw.origin);
299                 e->draw.angles[0] = CGVM_RandomRange(0, 360);
300                 e->draw.angles[1] = CGVM_RandomRange(0, 360);
301                 e->draw.angles[2] = CGVM_RandomRange(0, 360);
302                 VectorRandom(e->velocity);
303                 VectorScale(e->velocity, velocityscale, e->velocity);
304                 e->velocity[2] -= (float)(cg_gravity * 0.1);
305                 e->avelocity[0] = CGVM_RandomRange(0, 1440);
306                 e->avelocity[1] = CGVM_RandomRange(0, 1440);
307                 e->avelocity[2] = CGVM_RandomRange(0, 1440);
308                 r = CGVM_RandomRange(0, 3);
309                 if (r < 1)
310                         e->draw.model = CGVM_Model("progs/gib1.mdl");
311                 else if (r < 2)
312                         e->draw.model = CGVM_Model("progs/gib2.mdl");
313                 else
314                         e->draw.model = CGVM_Model("progs/gib3.mdl");
315                 e->draw.alpha = 1;
316                 e->draw.scale = 1;
317                 e->draw.frame1 = 0;
318                 e->draw.frame2 = 0;
319                 e->draw.framelerp = 0;
320                 e->draw.skinnum = 0;
321                 VectorSet(e->worldmins, 0, 0, -8);
322                 VectorSet(e->worldmaxs, 0, 0, -8);
323                 VectorSet(e->entitymins, -8, -8, -8);
324                 VectorSet(e->entitymaxs, 8, 8, 8);
325                 e->bouncescale = 1.5;
326                 e->gravityscale = 1;
327                 e->airfrictionscale = 1;
328                 e->framethink = gib_framethink;
329                 e->touchnetwork = gib_touchnetwork;
330                 e->dietime = (float)time + CGVM_RandomRange(3.0f, 5.0f);
331         }
332 }
333
334 // called by engine
335 void CG_Init(void)
336 {
337         localentity = CGVM_Malloc(sizeof(localentity_t) * MAX_LOCALENTITIES);
338         phys_entity = CGVM_Malloc(sizeof(cgphysentity_t) * MAX_LOCALENTITIES);
339         CGVM_RegisterNetworkCode(1, net_explosion);
340         CGVM_RegisterNetworkCode(2, net_gibshower);
341         gametime = 0;
342 }
343
344 // called by engine
345 void CG_Frame(double time)
346 {
347         cg_gravity = -CGVM_GetCvarFloat("sv_gravity");
348         frametime = time - gametime;
349         gametime = time;
350         phys_updateentities();
351 }
352