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