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