]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/w_porto.qc
Move tuba, seeker, rifle, port, nex, hook, hlac, hagar, fireball, devastator & crylin...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / w_porto.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(
3 /* WEP_##id */ PORTO,
4 /* function */ w_porto,
5 /* ammotype */ 0,
6 /* impulse  */ 0,
7 /* flags    */ WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON,
8 /* rating   */ 0,
9 /* model    */ "porto" ,
10 /* netname  */ "porto",
11 /* fullname */ _("Port-O-Launch")
12 );
13
14 #define PORTO_SETTINGS(w_cvar,w_prop) \
15         w_cvar(WEP_PORTO, porto, MO_BOTH, animtime) \
16         w_cvar(WEP_PORTO, porto, MO_BOTH, lifetime) \
17         w_cvar(WEP_PORTO, porto, MO_BOTH, refire) \
18         w_cvar(WEP_PORTO, porto, MO_BOTH, speed) \
19         w_cvar(WEP_PORTO, porto, MO_NONE, secondary) \
20         w_prop(WEP_PORTO, porto, reloading_ammo, reload_ammo) \
21         w_prop(WEP_PORTO, porto, reloading_time, reload_time) \
22         w_prop(WEP_PORTO, porto, switchdelay_raise, switchdelay_raise) \
23         w_prop(WEP_PORTO, porto, switchdelay_drop, switchdelay_drop)
24
25 #ifdef SVQC
26 PORTO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
27 .entity porto_current;
28 .vector porto_v_angle; // holds "held" view angles
29 .float porto_v_angle_held;
30 .vector right_vector;
31 #endif
32 #else
33 #ifdef SVQC
34 void spawnfunc_weapon_porto (void) { weapon_defaultspawnfunc(WEP_PORTO); }
35
36 void W_Porto_Success (void)
37 {
38         if(self.realowner == world)
39         {
40                 objerror("Cannot succeed successfully: no owner\n");
41                 return;
42         }
43
44         self.realowner.porto_current = world;
45         remove(self);
46 }
47
48 string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo);
49 void W_Porto_Fail (float failhard)
50 {
51         if(self.realowner == world)
52         {
53                 objerror("Cannot fail successfully: no owner\n");
54                 return;
55         }
56
57         // no portals here!
58         if(self.cnt < 0)
59         {
60                 Portal_ClearWithID(self.realowner, self.portal_id);
61         }
62
63         self.realowner.porto_current = world;
64
65         if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !(self.realowner.weapons & WEPSET_PORTO))
66         {
67                 setsize (self, '-16 -16 0', '16 16 32');
68                 setorigin(self, self.origin + trace_plane_normal);
69                 if(move_out_of_solid(self))
70                 {
71                         self.flags = FL_ITEM;
72                         self.velocity = trigger_push_calculatevelocity(self.origin, self.realowner, 128);
73                         tracetoss(self, self);
74                         if(vlen(trace_endpos - self.realowner.origin) < 128)
75                         {
76                                 W_ThrowNewWeapon(self.realowner, WEP_PORTO, 0, self.origin, self.velocity);
77                                 centerprint(self.realowner, "^1Portal deployment failed.\n\n^2Catch it to try again!");
78                         }
79                 }
80         }
81         remove(self);
82 }
83
84 void W_Porto_Remove (entity p)
85 {
86         if(p.porto_current.realowner == p && p.porto_current.classname == "porto")
87         {
88                 entity oldself;
89                 oldself = self;
90                 self = p.porto_current;
91                 W_Porto_Fail(1);
92                 self = oldself;
93         }
94 }
95
96 void W_Porto_Think (void)
97 {
98         trace_plane_normal = '0 0 0';
99         if(self.realowner.playerid != self.playerid)
100                 remove(self);
101         else
102                 W_Porto_Fail(0);
103 }
104
105 void W_Porto_Touch (void)
106 {
107         vector norm;
108
109         // do not use PROJECTILE_TOUCH here
110         // FIXME but DO handle warpzones!
111
112         if(other.classname == "portal")
113                 return; // handled by the portal
114
115         norm = trace_plane_normal;
116         if(trace_ent.iscreature)
117         {
118                 traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_z, MOVE_WORLDONLY, self);
119                 if(trace_fraction >= 1)
120                         return;
121                 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
122                         return;
123                 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
124                         return;
125         }
126
127         if(self.realowner.playerid != self.playerid)
128         {
129                 sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
130                 remove(self);
131         }
132         else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
133         {
134                 spamsound(self, CH_SHOTS, "porto/bounce.wav", VOL_BASE, ATTEN_NORM);
135                 // just reflect
136                 self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * trace_plane_normal);
137                 self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * trace_plane_normal));
138         }
139         else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
140         {
141                 sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
142                 W_Porto_Fail(0);
143                 if(self.cnt < 0)
144                         Portal_ClearAll_PortalsOnly(self.realowner);
145         }
146         else if(self.cnt == 0)
147         {
148                 // in-portal only
149                 if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
150                 {
151                         sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
152                         trace_plane_normal = norm;
153                         centerprint(self.realowner, "^1In^7-portal created.");
154                         W_Porto_Success();
155                 }
156                 else
157                 {
158                         sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
159                         trace_plane_normal = norm;
160                         W_Porto_Fail(0);
161                 }
162         }
163         else if(self.cnt == 1)
164         {
165                 // out-portal only
166                 if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
167                 {
168                         sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
169                         trace_plane_normal = norm;
170                         centerprint(self.realowner, "^4Out^7-portal created.");
171                         W_Porto_Success();
172                 }
173                 else
174                 {
175                         sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
176                         trace_plane_normal = norm;
177                         W_Porto_Fail(0);
178                 }
179         }
180         else if(self.effects & EF_RED)
181         {
182                 self.effects += EF_BLUE - EF_RED;
183                 if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
184                 {
185                         sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
186                         trace_plane_normal = norm;
187                         centerprint(self.realowner, "^1In^7-portal created.");
188                         self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * norm);
189                         self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * norm));
190                         CSQCProjectile(self, TRUE, PROJECTILE_PORTO_BLUE, TRUE); // change type
191                 }
192                 else
193                 {
194                         sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
195                         trace_plane_normal = norm;
196                         Portal_ClearAll_PortalsOnly(self.realowner);
197                         W_Porto_Fail(0);
198                 }
199         }
200         else
201         {
202                 if(self.realowner.portal_in.portal_id == self.portal_id)
203                 {
204                         if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
205                         {
206                                 sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
207                                 trace_plane_normal = norm;
208                                 centerprint(self.realowner, "^4Out^7-portal created.");
209                                 W_Porto_Success();
210                         }
211                         else
212                         {
213                                 sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
214                                 Portal_ClearAll_PortalsOnly(self.realowner);
215                                 W_Porto_Fail(0);
216                         }
217                 }
218                 else
219                 {
220                         sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
221                         Portal_ClearAll_PortalsOnly(self.realowner);
222                         W_Porto_Fail(0);
223                 }
224         }
225 }
226
227 void W_Porto_Attack (float type)
228 {
229         entity gren;
230
231         W_SetupShot (self, FALSE, 4, "porto/fire.wav", CH_WEAPON_A, 0);
232         // always shoot from the eye
233         w_shotdir = v_forward;
234         w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
235
236         //pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
237
238         gren = spawn ();
239         gren.cnt = type;
240         gren.owner = gren.realowner = self;
241         gren.playerid = self.playerid;
242         gren.classname = "porto";
243         gren.bot_dodge = TRUE;
244         gren.bot_dodgerating = 200;
245         gren.movetype = MOVETYPE_BOUNCEMISSILE;
246         PROJECTILE_MAKETRIGGER(gren);
247         gren.effects = EF_RED;
248         gren.scale = 4;
249         setorigin(gren, w_shotorg);
250         setsize(gren, '0 0 0', '0 0 0');
251         
252         gren.nextthink = time + WEP_CVAR_BOTH(porto, (type <= 0), lifetime);
253         gren.think = W_Porto_Think;
254         gren.touch = W_Porto_Touch;
255         
256         if(self.items & IT_STRENGTH)
257                 W_SetupProjectileVelocity(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed) * autocvar_g_balance_powerup_strength_force, 0);
258         else
259                 W_SetupProjectileVelocity(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed), 0);
260
261         gren.angles = vectoangles (gren.velocity);
262         gren.flags = FL_PROJECTILE;
263
264         gren.portal_id = time;
265         self.porto_current = gren;
266         gren.playerid = self.playerid;
267         fixedmakevectors(fixedvectoangles(gren.velocity));
268         gren.right_vector = v_right;
269
270         gren.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
271
272         if(type > 0)
273                 CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_BLUE, TRUE);
274         else
275                 CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_RED, TRUE);
276
277         other = gren; MUTATOR_CALLHOOK(EditProjectile);
278 }
279
280 float w_nexball_weapon(float req);
281 float w_porto(float req)
282 {
283         //vector v_angle_save;
284
285         if (g_nexball) { return w_nexball_weapon(req); }
286         
287         switch(req)
288         {
289                 case WR_AIM:
290                 {
291                         self.BUTTON_ATCK = FALSE;
292                         self.BUTTON_ATCK2 = FALSE;
293                         if(!WEP_CVAR(porto, secondary))
294                                 if(bot_aim(WEP_CVAR_PRI(porto, speed), 0, WEP_CVAR_PRI(porto, lifetime), FALSE))
295                                         self.BUTTON_ATCK = TRUE;
296                                         
297                         return TRUE;
298                 }
299                 case WR_CONFIG:
300                 {
301                         PORTO_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
302                         return TRUE;
303                 }
304                 case WR_THINK:
305                 {
306                         if(WEP_CVAR(porto, secondary))
307                         {
308                                 if (self.BUTTON_ATCK)
309                                 if (!self.porto_current)
310                                 if (!self.porto_forbidden)
311                                 if (weapon_prepareattack(0, WEP_CVAR_PRI(porto, refire)))
312                                 {
313                                         W_Porto_Attack(0);
314                                         weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
315                                 }
316
317                                 if (self.BUTTON_ATCK2)
318                                 if (!self.porto_current)
319                                 if (!self.porto_forbidden)
320                                 if (weapon_prepareattack(1, WEP_CVAR_SEC(porto, refire)))
321                                 {
322                                         W_Porto_Attack(1);
323                                         weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready);
324                                 }
325                         }
326                         else
327                         {
328                                 if(self.porto_v_angle_held)
329                                 {
330                                         if(!self.BUTTON_ATCK2)
331                                         {
332                                                 self.porto_v_angle_held = 0;
333
334                                                 ClientData_Touch(self);
335                                         }
336                                 }
337                                 else
338                                 {
339                                         if(self.BUTTON_ATCK2)
340                                         {
341                                                 self.porto_v_angle = self.v_angle;
342                                                 self.porto_v_angle_held = 1;
343
344                                                 ClientData_Touch(self);
345                                         }
346                                 }
347                                 if(self.porto_v_angle_held)
348                                         makevectors(self.porto_v_angle); // override the previously set angles
349
350                                 if (self.BUTTON_ATCK)
351                                 if (!self.porto_current)
352                                 if (!self.porto_forbidden)
353                                 if (weapon_prepareattack(0, WEP_CVAR_PRI(porto, refire)))
354                                 {
355                                         W_Porto_Attack(-1);
356                                         weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
357                                 }
358                         }
359                         
360                         return TRUE;
361                 }
362                 case WR_INIT:
363                 {
364                         precache_model ("models/weapons/g_porto.md3");
365                         precache_model ("models/weapons/v_porto.md3");
366                         precache_model ("models/weapons/h_porto.iqm");
367                         precache_model ("models/portal.md3");
368                         precache_sound ("porto/bounce.wav");
369                         precache_sound ("porto/create.wav");
370                         precache_sound ("porto/expire.wav");
371                         precache_sound ("porto/explode.wav");
372                         precache_sound ("porto/fire.wav");
373                         precache_sound ("porto/unsupported.wav");
374                         PORTO_SETTINGS(WEP_SKIPCVAR, WEP_SET_PROP)
375                         return TRUE;
376                 }
377                 case WR_SETUP:
378                 {
379                         self.current_ammo = ammo_none;
380                         return TRUE;
381                 }
382                 case WR_RESETPLAYER:
383                 {
384                         self.porto_current = world;
385                         return TRUE;
386                 }
387         }
388         return TRUE;
389 }
390 #endif
391 #ifdef CSQC
392 float w_porto(float req)
393 {
394         switch(req)
395         {
396                 case WR_IMPACTEFFECT:
397                 {
398                         print("Since when does Porto send DamageInfo?\n");
399                         return TRUE;
400                 }
401                 case WR_INIT:
402                 {
403                         // nothing to do
404                         return TRUE;
405                 }
406         }
407         return TRUE;
408 }
409 #endif
410 #endif