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