]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_hook.qc
Add hook primary/secondary to have health and such so it can be damaged by contents too
[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         self.health = self.health - damage;
60         
61         print(strcat("hookbomb health ", ftos(self.health), " after ", ftos(damage), " damage... (at time: ", ftos(time), ")\n"));
62         
63         if (self.health <= 0)
64                 W_PrepareExplosionByDamage(attacker, W_Hook_Explode2);
65 }
66
67 void W_Hook_Touch2 (void)
68 {
69         PROJECTILE_TOUCH;
70         self.use();
71 }
72
73 void W_Hook_Attack2()
74 {
75         local entity gren;
76
77         W_DecreaseAmmo(ammo_cells, autocvar_g_balance_hook_secondary_ammo, FALSE);
78         W_SetupShot (self, FALSE, 4, "weapons/hookbomb_fire.wav", CH_WEAPON_A, autocvar_g_balance_hook_secondary_damage);
79
80         gren = spawn ();
81         gren.owner = gren.realowner = self;
82         gren.classname = "hookbomb";
83         gren.bot_dodge = TRUE;
84         gren.bot_dodgerating = autocvar_g_balance_hook_secondary_damage;
85         gren.movetype = MOVETYPE_TOSS;
86         PROJECTILE_MAKETRIGGER(gren);
87         gren.projectiledeathtype = WEP_HOOK | HITTYPE_SECONDARY;
88         setorigin(gren, w_shotorg);
89         setsize(gren, '0 0 0', '0 0 0');
90
91         gren.nextthink = time + autocvar_g_balance_hook_secondary_lifetime;
92         gren.think = adaptor_think2use_hittype_splash;
93         gren.use = W_Hook_Explode2;
94         gren.touch = W_Hook_Touch2;
95         
96         gren.takedamage = DAMAGE_YES;
97         gren.health = autocvar_g_balance_hook_secondary_health;
98         gren.damageforcescale = autocvar_g_balance_hook_secondary_damageforcescale;
99         gren.event_damage = W_Hook_Damage;
100         gren.damagedbycontents = TRUE;
101
102         gren.velocity = '0 0 1' * autocvar_g_balance_hook_secondary_speed;
103         if(autocvar_g_projectiles_newton_style)
104                 gren.velocity = gren.velocity + self.velocity;
105
106         gren.gravity = autocvar_g_balance_hook_secondary_gravity;
107         //W_SetupProjectileVelocity(gren); // just falling down!
108
109         gren.angles = '0 0 0';
110         gren.flags = FL_PROJECTILE;
111
112         CSQCProjectile(gren, TRUE, PROJECTILE_HOOKBOMB, TRUE);
113
114         other = gren; MUTATOR_CALLHOOK(EditProjectile);
115 }
116
117 void spawnfunc_weapon_hook (void)
118 {
119         if(g_grappling_hook) // offhand hook
120         {
121                 startitem_failed = TRUE;
122                 remove(self);
123                 return;
124         }
125         weapon_defaultspawnfunc(WEP_HOOK);
126 }
127
128 float w_hook(float req)
129 {
130         float hooked_time_max, hooked_fuel;
131                 
132         if (req == WR_AIM)
133         {
134                 // ... sorry ...
135         }
136         else if (req == WR_THINK)
137         {
138                 if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
139                 {
140                         if(!self.hook)
141                         if not(self.hook_state & HOOK_WAITING_FOR_RELEASE)
142                         if not(self.hook_state & HOOK_FIRING)
143                         if (time > self.hook_refire)
144                         if (weapon_prepareattack(0, -1))
145                         {
146                                 W_DecreaseAmmo(ammo_fuel, autocvar_g_balance_hook_primary_fuel, FALSE);
147                                 self.hook_state |= HOOK_FIRING;
148                                 weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hook_primary_animtime, w_ready);                         
149                         }
150                 }
151
152                 if (self.BUTTON_ATCK2)
153                 {
154                         if (weapon_prepareattack(1, autocvar_g_balance_hook_secondary_refire))
155                         {
156                                 W_Hook_Attack2();
157                                 weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hook_secondary_animtime, w_ready);
158                         }
159                 }
160
161                 if(self.hook)
162                 {
163                         // if hooked, no bombs, and increase the timer
164                         self.hook_refire = max(self.hook_refire, time + autocvar_g_balance_hook_primary_refire * W_WeaponRateFactor());
165
166                         // hook also inhibits health regeneration, but only for 1 second
167                         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
168                                 self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
169                 }
170
171                 if(self.hook && self.hook.state == 1)
172                 {
173                         hooked_time_max = autocvar_g_balance_hook_primary_hooked_time_max;                      
174                         if (hooked_time_max > 0)
175                         {
176                                 if ( time > self.hook_time_hooked + hooked_time_max )
177                                         self.hook_state |= HOOK_REMOVING;
178                         }
179                         
180                         hooked_fuel = autocvar_g_balance_hook_primary_hooked_fuel;
181                         if (hooked_fuel > 0)
182                         {
183                                 if ( time > self.hook_time_fueldecrease )
184                                 {
185                                         if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
186                                         {
187                                                 if ( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel )
188                                                 {
189                                                         W_DecreaseAmmo(ammo_fuel, (time - self.hook_time_fueldecrease) * hooked_fuel, FALSE);
190                                                         self.hook_time_fueldecrease = time;
191                                                         // decrease next frame again
192                                                 }
193                                                 else
194                                                 {
195                                                         self.ammo_fuel = 0;
196                                                         self.hook_state |= HOOK_REMOVING;
197                                                         W_SwitchWeapon_Force(self, w_getbestweapon(self));
198                                                 }
199                                         }
200                                 }
201                         }
202                 }
203                 else
204                 {
205                         self.hook_time_hooked = time;                           
206                         self.hook_time_fueldecrease = time + autocvar_g_balance_hook_primary_hooked_time_free;
207                 }
208
209                 if (self.BUTTON_CROUCH)
210                 {
211                         self.hook_state &~= HOOK_PULLING;
212                         if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
213                                 self.hook_state &~= HOOK_RELEASING;
214                         else
215                                 self.hook_state |= HOOK_RELEASING;
216                 }
217                 else
218                 {
219                         self.hook_state |= HOOK_PULLING;
220                         self.hook_state &~= HOOK_RELEASING;
221
222                         if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
223                         {
224                                 // already fired
225                                 if(self.hook)
226                                         self.hook_state |= HOOK_WAITING_FOR_RELEASE;
227                         }
228                         else
229                         {
230                                 self.hook_state |= HOOK_REMOVING;
231                                 self.hook_state &~= HOOK_WAITING_FOR_RELEASE;
232                         }
233                 }
234         }
235         else if (req == WR_PRECACHE)
236         {
237                 precache_model ("models/weapons/g_hookgun.md3");
238                 precache_model ("models/weapons/v_hookgun.md3");
239                 precache_model ("models/weapons/h_hookgun.iqm");
240                 precache_sound ("weapons/hook_impact.wav"); // done by g_hook.qc
241                 precache_sound ("weapons/hook_fire.wav");
242                 precache_sound ("weapons/hookbomb_fire.wav");
243         }
244         else if (req == WR_SETUP)
245         {
246                 weapon_setup(WEP_HOOK);
247                 self.current_ammo = ammo_fuel;
248                 self.hook_state &~= HOOK_WAITING_FOR_RELEASE;
249         }
250         else if (req == WR_CHECKAMMO1)
251         {
252                 if(self.hook)
253                         return self.ammo_fuel > 0;
254                 else
255                         return self.ammo_fuel >= autocvar_g_balance_hook_primary_fuel;
256         }
257         else if (req == WR_CHECKAMMO2)
258         {
259                 return self.ammo_cells >= autocvar_g_balance_hook_secondary_ammo;
260         }
261         else if (req == WR_RESETPLAYER)
262         {
263                 self.hook_refire = time;
264         }
265         return TRUE;
266 };
267 #endif
268 #ifdef CSQC
269 float w_hook(float req)
270 {
271         if(req == WR_IMPACTEFFECT)
272         {
273                 vector org2;
274                 org2 = w_org + w_backoff * 2;
275                 pointparticles(particleeffectnum("hookbomb_explode"), org2, '0 0 0', 1);
276                 if(!w_issilent)
277                         sound(self, CH_SHOTS, "weapons/hookbomb_impact.wav", VOL_BASE, ATTN_NORM);
278         }
279         else if(req == WR_PRECACHE)
280         {
281                 precache_sound("weapons/hookbomb_impact.wav");
282         }
283         else if (req == WR_SUICIDEMESSAGE)
284                 w_deathtypestring = _("%s did the impossible");
285         else if (req == WR_KILLMESSAGE)
286                 w_deathtypestring = _("%s has run into %s's gravity bomb");
287         return TRUE;
288 }
289 #endif
290 #endif