Merge remote-tracking branch 'origin/divVerent/allow-override-item-model'
[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, 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, 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
104         gren.velocity = '0 0 1' * autocvar_g_balance_hook_secondary_speed;
105         if(autocvar_g_projectiles_newton_style)
106                 gren.velocity = gren.velocity + self.velocity;
107
108         gren.gravity = autocvar_g_balance_hook_secondary_gravity;
109         //W_SetupProjectileVelocity(gren); // just falling down!
110
111         gren.angles = '0 0 0';
112         gren.flags = FL_PROJECTILE;
113
114         CSQCProjectile(gren, TRUE, PROJECTILE_HOOKBOMB, TRUE);
115
116         other = gren; MUTATOR_CALLHOOK(EditProjectile);
117 }
118
119 void spawnfunc_weapon_hook (void)
120 {
121         if(g_grappling_hook) // offhand hook
122         {
123                 startitem_failed = TRUE;
124                 remove(self);
125                 return;
126         }
127         weapon_defaultspawnfunc(WEP_HOOK);
128 }
129
130 float w_hook(float req)
131 {
132         float hooked_time_max, hooked_fuel;
133                 
134         if (req == WR_AIM)
135         {
136                 // ... sorry ...
137         }
138         else if (req == WR_THINK)
139         {
140                 if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
141                 {
142                         if(!self.hook)
143                         if not(self.hook_state & HOOK_WAITING_FOR_RELEASE)
144                         if not(self.hook_state & HOOK_FIRING)
145                         if (time > self.hook_refire)
146                         if (weapon_prepareattack(0, -1))
147                         {
148                                 W_DecreaseAmmo(ammo_fuel, autocvar_g_balance_hook_primary_fuel, FALSE);
149                                 self.hook_state |= HOOK_FIRING;
150                                 weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hook_primary_animtime, w_ready);                         
151                         }
152                 }
153
154                 if (self.BUTTON_ATCK2)
155                 {
156                         if (weapon_prepareattack(1, autocvar_g_balance_hook_secondary_refire))
157                         {
158                                 W_Hook_Attack2();
159                                 weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hook_secondary_animtime, w_ready);
160                         }
161                 }
162
163                 if(self.hook)
164                 {
165                         // if hooked, no bombs, and increase the timer
166                         self.hook_refire = max(self.hook_refire, time + autocvar_g_balance_hook_primary_refire * W_WeaponRateFactor());
167
168                         // hook also inhibits health regeneration, but only for 1 second
169                         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
170                                 self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
171                 }
172
173                 if(self.hook && self.hook.state == 1)
174                 {
175                         hooked_time_max = autocvar_g_balance_hook_primary_hooked_time_max;                      
176                         if (hooked_time_max > 0)
177                         {
178                                 if ( time > self.hook_time_hooked + hooked_time_max )
179                                         self.hook_state |= HOOK_REMOVING;
180                         }
181                         
182                         hooked_fuel = autocvar_g_balance_hook_primary_hooked_fuel;
183                         if (hooked_fuel > 0)
184                         {
185                                 if ( time > self.hook_time_fueldecrease )
186                                 {
187                                         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
188                                         {
189                                                 if ( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel )
190                                                 {
191                                                         W_DecreaseAmmo(ammo_fuel, (time - self.hook_time_fueldecrease) * hooked_fuel, FALSE);
192                                                         self.hook_time_fueldecrease = time;
193                                                         // decrease next frame again
194                                                 }
195                                                 else
196                                                 {
197                                                         self.ammo_fuel = 0;
198                                                         self.hook_state |= HOOK_REMOVING;
199                                                         W_SwitchWeapon_Force(self, w_getbestweapon(self));
200                                                 }
201                                         }
202                                 }
203                         }
204                 }
205                 else
206                 {
207                         self.hook_time_hooked = time;                           
208                         self.hook_time_fueldecrease = time + autocvar_g_balance_hook_primary_hooked_time_free;
209                 }
210
211                 if (self.BUTTON_CROUCH)
212                 {
213                         self.hook_state &~= HOOK_PULLING;
214                         if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
215                                 self.hook_state &~= HOOK_RELEASING;
216                         else
217                                 self.hook_state |= HOOK_RELEASING;
218                 }
219                 else
220                 {
221                         self.hook_state |= HOOK_PULLING;
222                         self.hook_state &~= HOOK_RELEASING;
223
224                         if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
225                         {
226                                 // already fired
227                                 if(self.hook)
228                                         self.hook_state |= HOOK_WAITING_FOR_RELEASE;
229                         }
230                         else
231                         {
232                                 self.hook_state |= HOOK_REMOVING;
233                                 self.hook_state &~= HOOK_WAITING_FOR_RELEASE;
234                         }
235                 }
236         }
237         else if (req == WR_PRECACHE)
238         {
239                 precache_model ("models/weapons/g_hookgun.md3");
240                 precache_model ("models/weapons/v_hookgun.md3");
241                 precache_model ("models/weapons/h_hookgun.iqm");
242                 precache_sound ("weapons/hook_impact.wav"); // done by g_hook.qc
243                 precache_sound ("weapons/hook_fire.wav");
244                 precache_sound ("weapons/hookbomb_fire.wav");
245         }
246         else if (req == WR_SETUP)
247         {
248                 weapon_setup(WEP_HOOK);
249                 self.current_ammo = ammo_fuel;
250                 self.hook_state &~= HOOK_WAITING_FOR_RELEASE;
251         }
252         else if (req == WR_CHECKAMMO1)
253         {
254                 if(self.hook)
255                         return self.ammo_fuel > 0;
256                 else
257                         return self.ammo_fuel >= autocvar_g_balance_hook_primary_fuel;
258         }
259         else if (req == WR_CHECKAMMO2)
260         {
261                 return self.ammo_cells >= autocvar_g_balance_hook_secondary_ammo;
262         }
263         else if (req == WR_RESETPLAYER)
264         {
265                 self.hook_refire = time;
266         }
267         return TRUE;
268 }
269 #endif
270 #ifdef CSQC
271 float w_hook(float req)
272 {
273         if(req == WR_IMPACTEFFECT)
274         {
275                 vector org2;
276                 org2 = w_org + w_backoff * 2;
277                 pointparticles(particleeffectnum("hookbomb_explode"), org2, '0 0 0', 1);
278                 if(!w_issilent)
279                         sound(self, CH_SHOTS, "weapons/hookbomb_impact.wav", VOL_BASE, ATTN_NORM);
280         }
281         else if(req == WR_PRECACHE)
282         {
283                 precache_sound("weapons/hookbomb_impact.wav");
284         }
285         else if (req == WR_SUICIDEMESSAGE)
286                 w_deathtypestring = _("%s did the impossible");
287         else if (req == WR_KILLMESSAGE)
288                 w_deathtypestring = _("%s was caught in %s's hook gravity bomb");
289         return TRUE;
290 }
291 #endif
292 #endif