Merge branch 'master' into Mario/turrets
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / turrets / util.qc
1 /*
2 * Return a angle within +/- 360.
3 */
4 float anglemods(float v)
5 {
6         v = v - 360 * floor(v / 360);
7
8         if(v >= 180)
9                 return v - 360;
10         else if(v <= -180)
11                 return v + 360;
12         else
13                 return v;
14 }
15
16 /*
17 * Return the short angle
18 */
19 float shortangle_f(float ang1, float ang2)
20 {
21         if(ang1 > ang2)
22         {
23                 if(ang1 > 180)
24                         return ang1 - 360;
25         }
26         else
27         {
28                 if(ang1 < -180)
29                         return ang1 + 360;
30         }
31
32         return ang1;
33 }
34
35 vector shortangle_v(vector ang1, vector ang2)
36 {
37         vector vtmp;
38
39         vtmp_x = shortangle_f(ang1_x,ang2_x);
40         vtmp_y = shortangle_f(ang1_y,ang2_y);
41         vtmp_z = shortangle_f(ang1_z,ang2_z);
42
43         return vtmp;
44 }
45
46 vector shortangle_vxy(vector ang1, vector ang2)
47 {
48         vector vtmp = '0 0 0';
49
50         vtmp_x = shortangle_f(ang1_x,ang2_x);
51         vtmp_y = shortangle_f(ang1_y,ang2_y);
52
53         return vtmp;
54 }
55
56
57 /*
58 * Get "real" origin, in worldspace, even if ent is attached to something else.
59 */
60 vector real_origin(entity ent)
61 {
62         entity e;
63         vector v = ((ent.absmin + ent.absmax) * 0.5);
64
65         e = ent.tag_entity;
66         while(e)
67         {
68                 v = v + ((e.absmin + e.absmax) * 0.5);
69                 e = e.tag_entity;
70         }
71
72         return v;
73 }
74
75 /*
76 * Return the angle between two enteties
77 */
78 vector angleofs(entity from, entity to)
79 {
80         vector v_res;
81
82         v_res = normalize(to.origin - from.origin);
83         v_res = vectoangles(v_res);
84         v_res = v_res - from.angles;
85
86         if (v_res_x < 0)        v_res_x += 360;
87         if (v_res_x > 180)      v_res_x -= 360;
88
89         if (v_res_y < 0)        v_res_y += 360;
90         if (v_res_y > 180)      v_res_y -= 360;
91
92         return v_res;
93 }
94
95 vector angleofs3(vector from, vector from_a, entity to)
96 {
97         vector v_res;
98
99         v_res = normalize(to.origin - from);
100         v_res = vectoangles(v_res);
101         v_res = v_res - from_a;
102
103         if (v_res_x < 0)        v_res_x += 360;
104         if (v_res_x > 180)      v_res_x -= 360;
105
106         if (v_res_y < 0)        v_res_y += 360;
107         if (v_res_y > 180)      v_res_y -= 360;
108
109         return v_res;
110 }
111
112 /*
113 * Update self.tur_shotorg by getting up2date bone info
114 * NOTICE this func overwrites the global v_forward, v_right and v_up vectors.
115 */
116 float turret_tag_fire_update()
117 {
118         if(!self.tur_head)
119         {
120                 error("Call to turret_tag_fire_update with self.tur_head missing!\n");
121                 self.tur_shotorg = '0 0 0';
122                 return false;
123         }
124
125         self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));
126         v_forward = normalize(v_forward);
127
128         return true;
129 }
130
131 /*
132 * Railgun-like beam, but has thickness and suppots slowing of target
133 */
134 void FireImoBeam (vector start, vector end, vector smin, vector smax,
135                                   float bforce, float f_dmg, float f_velfactor, float deathtype)
136
137 {
138         vector hitloc, force, endpoint, dir;
139         entity ent;
140
141         dir = normalize(end - start);
142         force = dir * bforce;
143
144         // go a little bit into the wall because we need to hit this wall later
145         end = end + dir;
146
147         // trace multiple times until we hit a wall, each obstacle will be made unsolid.
148         // note down which entities were hit so we can damage them later
149         while (1)
150         {
151                 tracebox(start, smin, smax, end, false, self);
152
153                 // if it is world we can't hurt it so stop now
154                 if (trace_ent == world || trace_fraction == 1)
155                         break;
156
157                 if (trace_ent.solid == SOLID_BSP)
158                         break;
159
160                 // make the entity non-solid so we can hit the next one
161                 trace_ent.railgunhit = true;
162                 trace_ent.railgunhitloc = end;
163                 trace_ent.railgunhitsolidbackup = trace_ent.solid;
164
165                 // stop if this is a wall
166
167                 // make the entity non-solid
168                 trace_ent.solid = SOLID_NOT;
169         }
170
171         endpoint = trace_endpos;
172
173         // find all the entities the railgun hit and restore their solid state
174         ent = findfloat(world, railgunhit, true);
175         while (ent)
176         {
177                 // restore their solid type
178                 ent.solid = ent.railgunhitsolidbackup;
179                 ent = findfloat(ent, railgunhit, true);
180         }
181
182         // find all the entities the railgun hit and hurt them
183         ent = findfloat(world, railgunhit, true);
184         while (ent)
185         {
186                 // get the details we need to call the damage function
187                 hitloc = ent.railgunhitloc;
188                 ent.railgunhitloc = '0 0 0';
189                 ent.railgunhitsolidbackup = SOLID_NOT;
190                 ent.railgunhit = false;
191
192                 // apply the damage
193                 if (ent.takedamage)
194                 {
195                         Damage (ent, self, self, f_dmg, deathtype, hitloc, force);
196                         ent.velocity = ent.velocity * f_velfactor;
197                         //ent.alpha = 0.25 + random() * 0.75;
198                 }
199
200                 // advance to the next entity
201                 ent = findfloat(ent, railgunhit, true);
202         }
203         trace_endpos = endpoint;
204 }
205
206 #ifdef TURRET_DEBUG
207 void SUB_Remove();
208 void marker_think()
209 {
210         if(self.cnt)
211         if(self.cnt < time)
212         {
213                 self.think = SUB_Remove;
214                 self.nextthink = time;
215                 return;
216         }
217
218         self.frame += 1;
219         if(self.frame > 29)
220                 self.frame = 0;
221
222         self.nextthink = time;
223 }
224
225 void mark_error(vector where,float lifetime)
226 {
227         entity err;
228
229         err = spawn();
230         err.classname = "error_marker";
231         setmodel(err,"models/marker.md3");
232         setorigin(err,where);
233         err.movetype = MOVETYPE_NONE;
234         err.think = marker_think;
235         err.nextthink = time;
236         err.skin = 0;
237         if(lifetime)
238                 err.cnt = lifetime + time;
239 }
240
241 void mark_info(vector where,float lifetime)
242 {
243         entity err;
244
245         err = spawn();
246         err.classname = "info_marker";
247         setmodel(err,"models/marker.md3");
248         setorigin(err,where);
249         err.movetype = MOVETYPE_NONE;
250         err.think = marker_think;
251         err.nextthink = time;
252         err.skin = 1;
253         if(lifetime)
254                 err.cnt = lifetime + time;
255 }
256
257 entity mark_misc(vector where,float lifetime)
258 {
259         entity err;
260
261         err = spawn();
262         err.classname = "mark_misc";
263         setmodel(err,"models/marker.md3");
264         setorigin(err,where);
265         err.movetype = MOVETYPE_NONE;
266         err.think = marker_think;
267         err.nextthink = time;
268         err.skin = 3;
269         if(lifetime)
270                 err.cnt = lifetime + time;
271         return err;
272 }
273
274 /*
275 * Paint a v_color colord circle on target onwho
276 * that fades away over f_time
277 */
278 void paint_target(entity onwho, float f_size, vector v_color, float f_time)
279 {
280         entity e;
281
282         e = spawn();
283         setmodel(e, "models/turrets/c512.md3"); // precision set above
284         e.scale = (f_size/512);
285         //setsize(e, '0 0 0', '0 0 0');
286         //setattachment(e,onwho,"");
287         setorigin(e,onwho.origin + '0 0 1');
288         e.alpha = 0.15;
289         e.movetype = MOVETYPE_FLY;
290
291         e.velocity = (v_color * 32); // + '0 0 1' * 64;
292
293         e.colormod = v_color;
294         SUB_SetFade(e,time,f_time);
295 }
296
297 void paint_target2(entity onwho, float f_size, vector v_color, float f_time)
298 {
299         entity e;
300
301         e = spawn();
302         setmodel(e, "models/turrets/c512.md3"); // precision set above
303         e.scale = (f_size/512);
304         setsize(e, '0 0 0', '0 0 0');
305
306         setorigin(e,onwho.origin + '0 0 1');
307         e.alpha = 0.15;
308         e.movetype = MOVETYPE_FLY;
309
310         e.velocity = (v_color * 32); // + '0 0 1' * 64;
311         e.avelocity_x = -128;
312
313         e.colormod = v_color;
314         SUB_SetFade(e,time,f_time);
315 }
316
317 void paint_target3(vector where, float f_size, vector v_color, float f_time)
318 {
319         entity e;
320         e = spawn();
321         setmodel(e, "models/turrets/c512.md3"); // precision set above
322         e.scale = (f_size/512);
323         setsize(e, '0 0 0', '0 0 0');
324         setorigin(e,where+ '0 0 1');
325         e.movetype = MOVETYPE_NONE;
326         e.velocity = '0 0 0';
327         e.colormod = v_color;
328         SUB_SetFade(e,time,f_time);
329 }
330 #endif