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