]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_hook.qc
Merge remote-tracking branch 'origin/samual/serverlist'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_hook.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(
3 /* WEP_##id  */ HOOK,
4 /* function  */ w_hook,
5 /* ammotype  */ IT_CELLS|IT_FUEL,
6 /* impulse   */ 0,
7 /* flags     */ WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
8 /* rating    */ 0,
9 /* model     */ "hookgun",
10 /* shortname */ "hook",
11 /* fullname  */ _("Grappling Hook")
12 );
13 #else
14 #ifdef SVQC
15 .float dmg;
16 .float dmg_edge;
17 .float dmg_radius;
18 .float dmg_force;
19 .float dmg_power;
20 .float dmg_duration;
21 .float dmg_last;
22 .float hook_refire;
23 .float hook_time_hooked;
24 .float hook_time_fueldecrease;
25
26 void W_Hook_ExplodeThink (void)
27 {
28         float dt, dmg_remaining_next, f;
29
30         dt = time - self.teleport_time;
31         dmg_remaining_next = pow(bound(0, 1 - dt / self.dmg_duration, 1), self.dmg_power);
32
33         f = self.dmg_last - dmg_remaining_next;
34         self.dmg_last = dmg_remaining_next;
35
36         RadiusDamage (self, self.realowner, self.dmg * f, self.dmg_edge * f, self.dmg_radius, self.realowner, self.dmg_force * f, self.projectiledeathtype, world);
37         self.projectiledeathtype |= HITTYPE_BOUNCE;
38         //RadiusDamage (self, world, self.dmg * f, self.dmg_edge * f, self.dmg_radius, world, self.dmg_force * f, self.projectiledeathtype, world);
39
40         if(dt < self.dmg_duration)
41                 self.nextthink = time + 0.05; // soon
42         else
43                 remove(self);
44 }
45
46 void W_Hook_Explode2 (void)
47 {
48         self.event_damage = func_null;
49         self.touch = func_null;
50         self.effects |= EF_NODRAW;
51
52         self.think = W_Hook_ExplodeThink;
53         self.nextthink = time;
54         self.dmg = autocvar_g_balance_hook_secondary_damage;
55         self.dmg_edge = autocvar_g_balance_hook_secondary_edgedamage;
56         self.dmg_radius = autocvar_g_balance_hook_secondary_radius;
57         self.dmg_force = autocvar_g_balance_hook_secondary_force;
58         self.dmg_power = autocvar_g_balance_hook_secondary_power;
59         self.dmg_duration = autocvar_g_balance_hook_secondary_duration;
60         self.teleport_time = time;
61         self.dmg_last = 1;
62         self.movetype = MOVETYPE_NONE;
63 }
64
65 void W_Hook_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
66 {
67         if (self.health <= 0)
68                 return;
69
70         if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
71                 return; // g_projectiles_damage says to halt
72
73         self.health = self.health - damage;
74
75         if (self.health <= 0)
76                 W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2);
77 }
78
79 void W_Hook_Touch2 (void)
80 {
81         PROJECTILE_TOUCH;
82         self.use();
83 }
84
85 void W_Hook_Attack2()
86 {
87         entity gren;
88
89         W_DecreaseAmmo(ammo_cells, autocvar_g_balance_hook_secondary_ammo, FALSE);
90         W_SetupShot (self, FALSE, 4, "weapons/hookbomb_fire.wav", CH_WEAPON_A, autocvar_g_balance_hook_secondary_damage);
91
92         gren = spawn ();
93         gren.owner = gren.realowner = self;
94         gren.classname = "hookbomb";
95         gren.bot_dodge = TRUE;
96         gren.bot_dodgerating = autocvar_g_balance_hook_secondary_damage;
97         gren.movetype = MOVETYPE_TOSS;
98         PROJECTILE_MAKETRIGGER(gren);
99         gren.projectiledeathtype = WEP_HOOK | HITTYPE_SECONDARY;
100         setorigin(gren, w_shotorg);
101         setsize(gren, '0 0 0', '0 0 0');
102
103         gren.nextthink = time + autocvar_g_balance_hook_secondary_lifetime;
104         gren.think = adaptor_think2use_hittype_splash;
105         gren.use = W_Hook_Explode2;
106         gren.touch = W_Hook_Touch2;
107
108         gren.takedamage = DAMAGE_YES;
109         gren.health = autocvar_g_balance_hook_secondary_health;
110         gren.damageforcescale = autocvar_g_balance_hook_secondary_damageforcescale;
111         gren.event_damage = W_Hook_Damage;
112         gren.damagedbycontents = TRUE;
113         gren.missile_flags = MIF_SPLASH | MIF_ARC;
114
115         gren.velocity = '0 0 1' * autocvar_g_balance_hook_secondary_speed;
116         if(autocvar_g_projectiles_newton_style)
117                 gren.velocity = gren.velocity + self.velocity;
118
119         gren.gravity = autocvar_g_balance_hook_secondary_gravity;
120         //W_SetupProjectileVelocity(gren); // just falling down!
121
122         gren.angles = '0 0 0';
123         gren.flags = FL_PROJECTILE;
124
125         CSQCProjectile(gren, TRUE, PROJECTILE_HOOKBOMB, TRUE);
126
127         other = gren; MUTATOR_CALLHOOK(EditProjectile);
128 }
129
130 void spawnfunc_weapon_hook (void)
131 {
132         if(g_grappling_hook) // offhand hook
133         {
134                 startitem_failed = TRUE;
135                 remove(self);
136                 return;
137         }
138         weapon_defaultspawnfunc(WEP_HOOK);
139 }
140
141 float w_hook(float req)
142 {
143         float hooked_time_max, hooked_fuel;
144
145         if (req == WR_AIM)
146         {
147                 // ... sorry ...
148         }
149         else if (req == WR_THINK)
150         {
151                 if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
152                 {
153                         if(!self.hook)
154                         if (!(self.hook_state & HOOK_WAITING_FOR_RELEASE))
155                         if (!(self.hook_state & HOOK_FIRING))
156                         if (time > self.hook_refire)
157                         if (weapon_prepareattack(0, -1))
158                         {
159                                 W_DecreaseAmmo(ammo_fuel, autocvar_g_balance_hook_primary_fuel, FALSE);
160                                 self.hook_state |= HOOK_FIRING;
161                                 weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hook_primary_animtime, w_ready);
162                         }
163                 }
164
165                 if (self.BUTTON_ATCK2)
166                 {
167                         if (weapon_prepareattack(1, autocvar_g_balance_hook_secondary_refire))
168                         {
169                                 W_Hook_Attack2();
170                                 weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hook_secondary_animtime, w_ready);
171                         }
172                 }
173
174                 if(self.hook)
175                 {
176                         // if hooked, no bombs, and increase the timer
177                         self.hook_refire = max(self.hook_refire, time + autocvar_g_balance_hook_primary_refire * W_WeaponRateFactor());
178
179                         // hook also inhibits health regeneration, but only for 1 second
180                         if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
181                                 self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
182                 }
183
184                 if(self.hook && self.hook.state == 1)
185                 {
186                         hooked_time_max = autocvar_g_balance_hook_primary_hooked_time_max;
187                         if (hooked_time_max > 0)
188                         {
189                                 if ( time > self.hook_time_hooked + hooked_time_max )
190                                         self.hook_state |= HOOK_REMOVING;
191                         }
192
193                         hooked_fuel = autocvar_g_balance_hook_primary_hooked_fuel;
194                         if (hooked_fuel > 0)
195                         {
196                                 if ( time > self.hook_time_fueldecrease )
197                                 {
198                                         if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
199                                         {
200                                                 if ( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel )
201                                                 {
202                                                         W_DecreaseAmmo(ammo_fuel, (time - self.hook_time_fueldecrease) * hooked_fuel, FALSE);
203                                                         self.hook_time_fueldecrease = time;
204                                                         // decrease next frame again
205                                                 }
206                                                 else
207                                                 {
208                                                         self.ammo_fuel = 0;
209                                                         self.hook_state |= HOOK_REMOVING;
210                                                         W_SwitchWeapon_Force(self, w_getbestweapon(self));
211                                                 }
212                                         }
213                                 }
214                         }
215                 }
216                 else
217                 {
218                         self.hook_time_hooked = time;
219                         self.hook_time_fueldecrease = time + autocvar_g_balance_hook_primary_hooked_time_free;
220                 }
221
222                 if (self.BUTTON_CROUCH)
223                 {
224                         self.hook_state &= ~HOOK_PULLING;
225                         if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
226                                 self.hook_state &= ~HOOK_RELEASING;
227                         else
228                                 self.hook_state |= HOOK_RELEASING;
229                 }
230                 else
231                 {
232                         self.hook_state |= HOOK_PULLING;
233                         self.hook_state &= ~HOOK_RELEASING;
234
235                         if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
236                         {
237                                 // already fired
238                                 if(self.hook)
239                                         self.hook_state |= HOOK_WAITING_FOR_RELEASE;
240                         }
241                         else
242                         {
243                                 self.hook_state |= HOOK_REMOVING;
244                                 self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
245                         }
246                 }
247         }
248         else if (req == WR_PRECACHE)
249         {
250                 precache_model ("models/weapons/g_hookgun.md3");
251                 precache_model ("models/weapons/v_hookgun.md3");
252                 precache_model ("models/weapons/h_hookgun.iqm");
253                 precache_sound ("weapons/hook_impact.wav"); // done by g_hook.qc
254                 precache_sound ("weapons/hook_fire.wav");
255                 precache_sound ("weapons/hookbomb_fire.wav");
256         }
257         else if (req == WR_SETUP)
258         {
259                 weapon_setup(WEP_HOOK);
260                 self.current_ammo = ammo_fuel;
261                 self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
262         }
263         else if (req == WR_CHECKAMMO1)
264         {
265                 if(self.hook)
266                         return self.ammo_fuel > 0;
267                 else
268                         return self.ammo_fuel >= autocvar_g_balance_hook_primary_fuel;
269         }
270         else if (req == WR_CHECKAMMO2)
271         {
272                 return self.ammo_cells >= autocvar_g_balance_hook_secondary_ammo;
273         }
274         else if (req == WR_RESETPLAYER)
275         {
276                 self.hook_refire = time;
277         }
278         else if (req == WR_SUICIDEMESSAGE)
279         {
280                 return FALSE;
281         }
282         else if (req == WR_KILLMESSAGE)
283         {
284                 return WEAPON_HOOK_MURDER;
285         }
286         return TRUE;
287 }
288 #endif
289 #ifdef CSQC
290 float w_hook(float req)
291 {
292         if(req == WR_IMPACTEFFECT)
293         {
294                 vector org2;
295                 org2 = w_org + w_backoff * 2;
296                 pointparticles(particleeffectnum("hookbomb_explode"), org2, '0 0 0', 1);
297                 if(!w_issilent)
298                         sound(self, CH_SHOTS, "weapons/hookbomb_impact.wav", VOL_BASE, ATTEN_NORM);
299         }
300         else if(req == WR_PRECACHE)
301         {
302                 precache_sound("weapons/hookbomb_impact.wav");
303         }
304         return TRUE;
305 }
306 #endif
307 #endif