1 // =============================
2 // Explosion Force Calculation
3 // =============================
4 float explosion_calcpush_getmultiplier(vector explosion_v, vector target_v)
7 a = explosion_v * (explosion_v - target_v);
10 // target is too fast to be hittable by this
13 a /= (explosion_v * explosion_v);
14 // we know we can divide by this, or above a would be == 0
20 vector explosion_calcpush(vector explosion_v, float explosion_m, vector target_v, float target_m, float elasticity)
22 // solution of the equations:
23 // v' = v + a vp // central hit
24 // m*v' + mp*vp' = m*v + mp*vp // conservation of momentum
25 // m*v'^2 + mp*vp'^2 = m*v^2 + mp*vp^2 // conservation of energy (ELASTIC hit)
26 // -> a = 0 // case 1: did not hit
27 // -> a = 2*mp*(vp^2 - vp.v) / ((m+mp) * vp^2) // case 2: did hit
28 // // non-elastic hits are somewhere between these two
30 // this would be physically correct, but we don't do that
31 return explosion_v * explosion_calcpush_getmultiplier(explosion_v, target_v) * (
35 target_m + explosion_m
37 ); // note: this factor is at least 0, at most 2
41 // simplified formula, tuned so that if the target has velocity 0, we get exactly the original force
42 vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor)
44 // if below 1, the formulas make no sense (and would cause superjumps)
51 // speedfactor * (1 + e) * m / (1 + m) == 1
52 m = 1 / ((1 + 0) * speedfactor - 1);
54 v = explosion_calcpush(explosion_f * speedfactor, m, target_v, 1, 0);
55 // the factor we then get is:
57 print(sprintf("MASS: %f\nv: %v -> %v\nENERGY BEFORE == %f + %f = %f\nENERGY AFTER >= %f\n",
59 target_v, target_v + v,
60 target_v * target_v, m * explosion_f * speedfactor * explosion_f * speedfactor, target_v * target_v + m * explosion_f * speedfactor * explosion_f * speedfactor,
61 (target_v + v) * (target_v + v)));
64 return explosion_f * explosion_calcpush_getmultiplier(explosion_f * speedfactor, target_v);
68 // =========================
69 // Shot Spread Calculation
70 // =========================
72 vector cliptoplane(vector v, vector p)
74 return v - (v * p) * p;
77 vector solve_cubic_pq(float p, float q)
80 D = q*q/4.0 + p*p*p/27.0;
84 a = 1.0/3.0 * acos(-q/2.0 * sqrt(-27.0/(p*p*p)));
85 u = sqrt(-4.0/3.0 * p);
93 '1 0 0' * cos(a + 2.0/3.0*M_PI)
95 '0 1 0' * cos(a + 4.0/3.0*M_PI)
108 return '1 1 0' * v + '0 0 1' * u;
110 return '0 1 1' * v + '1 0 0' * u;
115 u = cbrt(-q/2.0 + sqrt(D));
116 v = cbrt(-q/2.0 - sqrt(D));
117 return '1 1 1' * (u + v);
120 vector solve_cubic_abcd(float a, float b, float c, float d)
127 q = (27*a*a*d - 9*a*b*c + 2*b*b*b);
128 v = solve_cubic_pq(p, q);
129 v = (v - b * '1 1 1') * (1.0 / (3.0 * a));
131 v += '1 0 -1' * (v_z - v_x); // swap x, z
135 vector findperpendicular(vector v)
141 return normalize(cliptoplane(p, v));
144 vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle)
147 vector v1 = '0 0 0', v2;
150 spread *= spreadfactor; //g_weaponspreadfactor;
153 sstyle = spreadstyle; //autocvar_g_projectiles_spread_style;
157 // this is the baseline for the spread value!
158 // standard deviation: sqrt(2/5)
159 // density function: sqrt(1-r^2)
160 return forward + randomvec() * spread;
164 // same thing, basically
165 return normalize(forward + cliptoplane(randomvec() * spread, forward));
169 // circle spread... has at sigma=1 a standard deviation of sqrt(1/2)
170 sigma = spread * 0.89442719099991587855; // match baseline stddev
171 v1 = findperpendicular(forward);
172 v2 = cross(forward, v1);
173 // random point on unit circle
174 dx = random() * 2 * M_PI;
177 // radius in our dist function
180 return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
182 else if(sstyle == 3) // gauss 3d
184 sigma = spread * 0.44721359549996; // match baseline stddev
185 // note: 2D gaussian has sqrt(2) times the stddev of 1D, so this factor is right
187 v1_x += gsl_ran_gaussian(sigma);
188 v1_y += gsl_ran_gaussian(sigma);
189 v1_z += gsl_ran_gaussian(sigma);
192 else if(sstyle == 4) // gauss 2d
194 sigma = spread * 0.44721359549996; // match baseline stddev
195 // note: 2D gaussian has sqrt(2) times the stddev of 1D, so this factor is right
196 v1_x = gsl_ran_gaussian(sigma);
197 v1_y = gsl_ran_gaussian(sigma);
198 v1_z = gsl_ran_gaussian(sigma);
199 return normalize(forward + cliptoplane(v1, forward));
201 else if(sstyle == 5) // 1-r
203 sigma = spread * 1.154700538379252; // match baseline stddev
204 v1 = findperpendicular(forward);
205 v2 = cross(forward, v1);
206 // random point on unit circle
207 dx = random() * 2 * M_PI;
210 // radius in our dist function
212 r = solve_cubic_abcd(-2, 3, 0, -r) * '0 1 0';
213 return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
215 else if(sstyle == 6) // 1-r^2
217 sigma = spread * 1.095445115010332; // match baseline stddev
218 v1 = findperpendicular(forward);
219 v2 = cross(forward, v1);
220 // random point on unit circle
221 dx = random() * 2 * M_PI;
224 // radius in our dist function
228 return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
230 else if(sstyle == 7) // (1-r) (2-r)
232 sigma = spread * 1.224744871391589; // match baseline stddev
233 v1 = findperpendicular(forward);
234 v2 = cross(forward, v1);
235 // random point on unit circle
236 dx = random() * 2 * M_PI;
239 // radius in our dist function
243 return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
246 error("g_projectiles_spread_style must be 0 (sphere), 1 (flattened sphere), 2 (circle), 3 (gauss 3D), 4 (gauss plane), 5 (linear falloff), 6 (quadratic falloff), 7 (stronger falloff)!");
249 * how to derive falloff functions:
250 * rho(r) := (2-r) * (1-r);
253 * rhor(r) := r * rho(r);
254 * cr(t) := integrate(rhor(r), r, a, t);
255 * scr(t) := integrate(rhor(r) * r^2, r, a, t);
256 * variance : scr(b) / cr(b);
257 * solve(cr(r) = rand * cr(b), r), programmmode:false;
258 * sqrt(0.4 / variance), numer;