]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/effects/qc/gibs.qc
Fix FL_WEAPON flag overlapping FL_JUMPRELEASED. This unintentional change was introdu...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / effects / qc / gibs.qc
1 #include "gibs.qh"
2
3 REGISTER_NET_TEMP(net_gibsplash)
4
5 #ifdef SVQC
6
7 .int state;
8
9 bool Violence_GibSplash_SendEntity(entity this, entity to, int sf)
10 {
11         int channel = MSG_ONE;
12         msg_entity = to;
13         WriteHeader(channel, net_gibsplash);
14         WriteByte(channel, this.state); // actually type
15         WriteByte(channel, bound(1, this.cnt * 16, 255)); // gibbage amount multiplier
16         WriteShort(channel, floor(this.origin.x / 4)); // not using a coord here, as gibs don't need this accuracy
17         WriteShort(channel, floor(this.origin.y / 4)); // not using a coord here, as gibs don't need this accuracy
18         WriteShort(channel, floor(this.origin.z / 4)); // not using a coord here, as gibs don't need this accuracy
19         WriteShort(channel, this.oldorigin.x); // actually compressed velocity
20         return true;
21 }
22
23 void Violence_GibSplash_At(vector org, vector dir, float type, float amount, entity gibowner, entity attacker)
24 {
25         if(g_cts) // no gibs in CTS
26                 return;
27
28         entity e = new_pure(gibsplash);
29         e.cnt = amount;
30         e.state = type; // should stay smaller than 15
31         if(!sound_allowed(MSG_BROADCAST, gibowner) || !sound_allowed(MSG_BROADCAST, attacker))
32                 e.state |= 0x40; // "silence" bit
33         e.state |= 8 * gibowner.species; // gib type, ranges from 0 to 15
34
35         // if this is a copied dead body, send the num of its player instead
36         // TODO: remove this field, read from model txt files
37         if(gibowner.classname == "body")
38                 e.team = etof(gibowner.enemy);
39         else
40                 e.team = etof(gibowner);
41
42         // origin is just data to be sent
43         //setorigin(e, org);
44         e.origin = org;
45         e.velocity = dir;
46
47         e.oldorigin_x = compressShortVector(e.velocity);
48
49         FOREACH_CLIENT(IS_REAL_CLIENT(it), Violence_GibSplash_SendEntity(e, it, 0));
50         delete(e);
51 }
52
53 void Violence_GibSplash(entity source, float type, float amount, entity attacker)
54 {
55         Violence_GibSplash_At(source.origin + source.view_ofs, source.velocity, type, amount, source, attacker);
56 }
57 #endif
58
59 #ifdef CSQC
60
61 .vector colormod;
62 .bool silent;
63
64 #include "rubble.qh"
65 #include <common/physics/movetypes/movetypes.qh>
66
67 .float scale;
68 .float alpha;
69 .float cnt;
70 .float gravity;
71
72 void Gib_Delete(entity this)
73 {
74         delete(this);
75 }
76
77 string species_prefix(int specnum);
78
79 void Gib_setmodel(entity gib, string mdlname, int specnum)
80 {
81         switch(specnum)
82         {
83                 case SPECIES_ROBOT_RUSTY:
84                 case SPECIES_ROBOT_SHINY:
85                 case SPECIES_ROBOT_SOLID:
86                         if(specnum != SPECIES_ROBOT_SOLID || mdlname == "models/gibs/chunk.mdl")
87                         {
88                                 if(mdlname == "models/gibs/bloodyskull.md3")
89                                         setmodel(gib, MDL_GIB_ROBO);
90                                 else
91                                         setmodel(gib, MDL_GIB_ROBO_RANDOM());
92                                 if(specnum == SPECIES_ROBOT_SHINY)
93                                 {
94                                         gib.skin = 1;
95                                         gib.colormod = '2 2 2';
96                                 }
97                                 gib.scale = 1;
98                                 break;
99                         }
100                 default:
101                         _setmodel(gib, mdlname);
102                         gib.skin = specnum;
103                         break;
104         }
105 }
106
107 void new_te_bloodshower (int ef, vector org, float explosionspeed, int howmany)
108 {
109         float i, pmod;
110         pmod = autocvar_cl_particles_quality;
111         for (i = 0; i < 50 * pmod; ++i)
112                 __pointparticles(ef, org, randomvec() * explosionspeed, howmany / 50);
113 }
114
115 void SUB_RemoveOnNoImpact(entity this, entity toucher)
116 {
117         if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
118                 Gib_Delete(this);
119 }
120
121 void Gib_Touch(entity this, entity toucher)
122 {
123         // TODO maybe bounce of walls, make more gibs, etc.
124
125         if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
126         {
127                 Gib_Delete(this);
128                 return;
129         }
130
131         if(!this.silent)
132                 sound(this, CH_PAIN, SND_GIB_SPLAT_RANDOM(), VOL_BASE, ATTEN_NORM);
133         __pointparticles(_particleeffectnum(strcat(species_prefix(this.cnt), "blood")), this.origin + '0 0 1', '0 0 30', 10);
134
135         Gib_Delete(this);
136 }
137
138 void Gib_Draw(entity this)
139 {
140         vector oldorg;
141         oldorg = this.origin;
142
143         Movetype_Physics_MatchTicrate(this, autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
144         if(wasfreed(this))
145                 return;
146
147         if(gettouch(this) == Gib_Touch) // don't do this for the "chunk" thingie...
148                 // TODO somehow make it spray in a direction dependent on this.angles
149                 __trailparticles(this, _particleeffectnum(strcat(species_prefix(this.cnt), EFFECT_TR_SLIGHTBLOOD.eent_eff_name)), oldorg, this.origin);
150         else
151                 __trailparticles(this, _particleeffectnum(strcat(species_prefix(this.cnt), EFFECT_TR_BLOOD.eent_eff_name)), oldorg, this.origin);
152
153         this.renderflags = 0;
154
155         // make gibs die faster at low view quality
156         // if view_quality is 0.5, we want to have them die twice as fast
157         this.nextthink -= frametime * (1 / bound(0.01, view_quality, 1.00) - 1);
158
159         this.alpha = bound(0, this.nextthink - time, 1);
160
161         if(this.alpha < ALPHA_MIN_VISIBLE)
162         {
163                 this.drawmask = 0;
164                 Gib_Delete(this);
165         }
166 }
167
168 void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector vrand, int specnum, bool destroyontouch, bool issilent)
169 {
170         entity gib;
171
172         // TODO remove some gibs according to cl_nogibs
173         gib = ListNewChildRubble(CasingsNGibs, new(gib));
174         set_movetype(gib, MOVETYPE_BOUNCE);
175         gib.gravity = 1;
176         gib.solid = SOLID_CORPSE;
177         gib.cnt = specnum;
178         gib.silent = issilent;
179         gib.colormap = 4; // red
180         gib.colormap |= BIT(10); // RENDER_COLORMAPPED
181         Gib_setmodel(gib, mdlname, specnum);
182
183         setsize (gib, '-8 -8 -8', '8 8 8');
184
185         gib.draw = Gib_Draw;
186         IL_PUSH(g_drawables, gib);
187         if(destroyontouch)
188                 settouch(gib, Gib_Touch);
189         else
190                 settouch(gib, SUB_RemoveOnNoImpact);
191
192         // don't spawn gibs inside solid - just don't
193         if(org != safeorg)
194         {
195                 tracebox(safeorg, gib.mins, gib.maxs, org, MOVE_NOMONSTERS, gib);
196                 org = trace_endpos;
197         }
198
199         setorigin(gib, org);
200         gib.velocity = vconst * autocvar_cl_gibs_velocity_scale + vrand * autocvar_cl_gibs_velocity_random + '0 0 1' * autocvar_cl_gibs_velocity_up;
201         gib.avelocity = prandomvec() * vlen(gib.velocity) * autocvar_cl_gibs_avelocity_scale;
202         gib.move_time = time;
203         gib.damageforcescale = autocvar_cl_gibs_damageforcescale;
204
205         gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15);
206         gib.drawmask = MASK_NORMAL;
207
208         LimitedChildrenRubble(CasingsNGibs, "gib", autocvar_cl_gibs_maxcount, Gib_Delete, NULL);
209 }
210
211 NET_HANDLE(net_gibsplash, bool isNew)
212 {
213         Net_Accept(net_gibsplash);
214
215         string gentle_prefix = "morphed_";
216
217         int type = ReadByte(); // gibbage type
218         int amount = ReadByte() / 16.0; // gibbage amount
219         vector org;
220         org.x = ReadShort() * 4 + 2;
221         org.y = ReadShort() * 4 + 2;
222         org.z = ReadShort() * 4 + 2;
223         vector vel = decompressShortVector(ReadShort());
224
225         return = true;
226
227         float cl_gentle_gibs = autocvar_cl_gentle_gibs;
228         if(cl_gentle_gibs || autocvar_cl_gentle)
229                 type |= 0x80; // set gentle bit
230
231         if(type & 0x80)
232         {
233                 if(cl_gentle_gibs == 2)
234                         gentle_prefix = "";
235                 else if(cl_gentle_gibs == 3)
236                         gentle_prefix = "happy_";
237         }
238         else if(autocvar_cl_particlegibs)
239         {
240                 type |= 0x80;
241                 gentle_prefix = "particlegibs_";
242         }
243
244         if (!(cl_gentle_gibs || autocvar_cl_gentle))
245                 amount *= 1 - autocvar_cl_nogibs;
246
247         if(autocvar_ekg)
248                 amount *= 5;
249
250         if(amount <= 0 || !isNew)
251                 return;
252
253         setorigin(this, org); // for the sounds
254
255         int specnum = (type & 0x78) / 8; // blood/gibmodel type: using four bits (0..7, bit indexes 3,4,5)
256         bool issilent = (type & 0x40);
257         type = type & 0x87; // remove the species bits: bit 7 = gentle, bit 0,1,2 = kind of gib
258         string specstr = species_prefix(specnum);
259
260         switch(type)
261         {
262                 case 0x01:
263                         if(!issilent)
264                                 sound (this, CH_PAIN, SND_GIB, VOL_BASE, ATTEN_NORM);
265
266                         if(prandom() < amount)
267                                 TossGib ("models/gibs/eye.md3", org, org, vel, prandomvec() * 150, specnum, 0, issilent);
268                         new_te_bloodshower(_particleeffectnum(strcat(specstr, "bloodshower")), org, 1200, amount);
269                         if(prandom() < amount)
270                                 TossGib ("models/gibs/bloodyskull.md3", org, org + 16 * prandomvec(), vel, prandomvec() * 100, specnum, 0, issilent);
271
272                         for(int c = 0; c < amount; ++c)
273                         {
274                                 int randomvalue = amount - c;
275
276                                 if(prandom() < randomvalue)
277                                         TossGib ("models/gibs/arm.md3", org, org + 16 * prandomvec() + '0 0 8', vel, prandomvec() * (prandom() * 120 + 90), specnum,0, issilent);
278                                 if(prandom() < randomvalue)
279                                         TossGib ("models/gibs/arm.md3", org, org + 16 * prandomvec() + '0 0 8', vel, prandomvec() * (prandom() * 120 + 90), specnum,0, issilent);
280                                 if(prandom() < randomvalue)
281                                         TossGib ("models/gibs/chest.md3", org, org + 16 * prandomvec(), vel, prandomvec() * (prandom() * 120 + 80), specnum,0, issilent);
282                                 if(prandom() < randomvalue)
283                                         TossGib ("models/gibs/smallchest.md3", org, org + 16 * prandomvec(), vel, prandomvec() * (prandom() * 120 + 80), specnum,0, issilent);
284                                 if(prandom() < randomvalue)
285                                         TossGib ("models/gibs/leg1.md3", org, org + 16 * prandomvec() + '0 0 -5', vel, prandomvec() * (prandom() * 120 + 85), specnum,0, issilent);
286                                 if(prandom() < randomvalue)
287                                         TossGib ("models/gibs/leg2.md3", org, org + 16 * prandomvec() + '0 0 -5', vel, prandomvec() * (prandom() * 120 + 85), specnum,0, issilent);
288
289                                 // these splat on impact
290                                 if(prandom() < randomvalue)
291                                         TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
292                                 if(prandom() < randomvalue)
293                                         TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
294                                 if(prandom() < randomvalue)
295                                         TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
296                                 if(prandom() < randomvalue)
297                                         TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
298                         }
299                         break;
300                 case 0x02:
301                         __pointparticles(_particleeffectnum(strcat(specstr, "blood")), org, vel, amount * 16);
302                         break;
303                 case 0x03:
304                         if(prandom() < amount)
305                                 TossGib ("models/gibs/chunk.mdl", org, org, vel, prandomvec() * (prandom() * 30 + 20), specnum, 1, issilent); // TODO maybe adjust to more randomization?
306                         break;
307                 case 0x81:
308                         __pointparticles(_particleeffectnum(strcat(gentle_prefix, "damage_dissolve")), org, vel, amount);
309                         break;
310                 case 0x82:
311                         __pointparticles(_particleeffectnum(strcat(gentle_prefix, "damage_hit")), org, vel, amount * 16);
312                         break;
313                 case 0x83:
314                         // no gibs in gentle mode, sorry
315                         break;
316         }
317         delete(this);
318 }
319 #endif