Xonotic
hook.qc
Go to the documentation of this file.
1 #include "hook.qh"
2 
3 #include <common/constants.qh>
4 #include <common/effects/all.qh>
5 #include <common/net_linked.qh>
7 #include <common/state.qh>
8 #include <common/stats.qh>
9 #include <common/util.qh>
10 #include <common/vehicles/all.qh>
11 #include <common/weapons/_all.qh>
12 #include <common/weapons/_all.qh>
13 #include <lib/warpzone/common.qh>
14 #include <lib/warpzone/server.qh>
15 #include <server/bot/api.qh>
16 #include <server/command/common.qh>
17 #include <server/command/vote.qh>
18 #include <server/damage.qh>
19 #include <server/mutators/_mod.qh>
20 #include <server/player.qh>
21 #include <server/round_handler.qh>
22 #include <server/weapons/common.qh>
27 #include <server/world.qh>
28 
29 /*============================================
30 
31  Wazat's Xonotic Grappling Hook
32 
33  Contact: Wazat1@gmail.com
34 
35 
36 Installation instructions:
37 --------------------------
38 
39 1. Place hook.c in your gamec source directory with the other source files.
40 
41 2. Add this line to the bottom of progs.src:
42 
43 gamec/hook.c
44 
45 3. Open defs.h and add these lines to the very bottom:
46 
47 // Wazat's grappling hook
48 .entity hook;
49 void GrapplingHookFrame();
50 void RemoveGrapplingHook(entity pl);
51 void SetGrappleHookBindings();
52 // hook impulses
53 const float GRAPHOOK_FIRE = 20;
54 const float GRAPHOOK_RELEASE = 21;
55 // (note: you can change the hook impulse #'s to whatever you please)
56 
57 4. Open client.c and add this to the top of PutClientInServer():
58 
59  RemoveGrapplingHook(this); // Wazat's Grappling Hook
60 
61 5. Find ClientConnect() (in client.c) and add these lines to the bottom:
62 
63  // Wazat's grappling hook
64  SetGrappleHookBindings();
65 
66 6. Still in client.c, find PlayerPreThink and add this line just above the call to W_WeaponFrame:
67 
68  GrapplingHookFrame();
69 
70 7. Build and test the mod. You'll want to bind a key to "+hook" like this:
71 bind ctrl "+hook"
72 
73 And you should be done!
74 
75 
76 ============================================*/
77 
79 {
80  if(pl.move_movetype == MOVETYPE_FLY)
82 
83  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
84  {
85  .entity weaponentity = weaponentities[slot];
86  if(!pl.(weaponentity))
87  continue; // continue incase other slots exist?
88  if(pl.(weaponentity).hook)
89  delete(pl.(weaponentity).hook);
90  pl.(weaponentity).hook = NULL;
91  }
92 
93  //pl.disableclientprediction = false;
94 }
95 
96 void RemoveHook(entity this)
97 {
98  entity player = this.realowner;
99  .entity weaponentity = this.weaponentity_fld;
100 
101  if(player.(weaponentity).hook == this)
102  player.(weaponentity).hook = NULL;
103 
104  if(player.move_movetype == MOVETYPE_FLY)
105  set_movetype(player, MOVETYPE_WALK);
106  delete(this);
107 }
108 
110 {
111  RemoveHook(this);
112 }
113 
115 {
116  Send_Effect(EFFECT_HOOK_IMPACT, this.origin, '0 0 0', 1);
117  sound (this, CH_SHOTS, SND_HOOK_IMPACT, VOL_BASE, ATTEN_NORM);
118 
119  this.state = 1;
121  this.nextthink = time;
122  settouch(this, func_null);
123  this.velocity = '0 0 0';
125  this.hook_length = -1;
126 }
127 
129 bool GrapplingHookSend(entity this, entity to, int sf)
130 {
131  WriteHeader(MSG_ENTITY, ENT_CLIENT_HOOK);
132  sf = sf & 0x7F;
133  if(sound_allowed(MSG_BROADCAST, this.realowner))
134  sf |= 0x80;
135  WriteByte(MSG_ENTITY, sf);
136  if(sf & 1)
137  {
138  WriteByte(MSG_ENTITY, etof(this.realowner));
139  WriteByte(MSG_ENTITY, weaponslot(this.weaponentity_fld));
140  }
141  if(sf & 2)
142  {
143  WriteVector(MSG_ENTITY, this.hook_start);
144  }
145  if(sf & 4)
146  {
147  WriteVector(MSG_ENTITY, this.hook_end);
148  }
149  return true;
150 }
151 
153 
155 {
156  float spd, dist, minlength, pullspeed, ropestretch, ropeairfriction, rubberforce, newlength, rubberforce_overstretch;
157  vector dir, org, end, v0, dv, v, myorg, vs;
158  .entity weaponentity = this.weaponentity_fld;
159  if(this.realowner.(weaponentity).hook != this) // how did that happen?
160  {
161  error("Owner lost the hook!\n");
162  return;
163  }
164  if((this.move_movetype == MOVETYPE_FOLLOW && LostMovetypeFollow(this))
165  || game_stopped || (round_handler_IsActive() && !round_handler_IsRoundStarted())
166  || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade"))
167  {
168  RemoveHook(this);
169  return;
170  }
171  if(this.aiment)
173 
174  this.nextthink = time;
175 
176  int s = W_GunAlign(this.realowner.(weaponentity), STAT(GUNALIGN, this.realowner)) - 1;
177  vs = hook_shotorigin[s];
178 
179  makevectors(this.realowner.v_angle);
180  org = this.realowner.origin + this.realowner.view_ofs + v_forward * vs.x + v_right * -vs.y + v_up * vs.z;
181  myorg = WarpZone_RefSys_TransformOrigin(this.realowner, this, org);
182 
183  if(this.hook_length < 0)
184  this.hook_length = vlen(myorg - this.origin);
185 
187  entity pull_entity = this.realowner;
188  float velocity_multiplier = 1;
189  MUTATOR_CALLHOOK(GrappleHookThink, this, tarzan, pull_entity, velocity_multiplier);
190  tarzan = M_ARGV(1, int);
191  pull_entity = M_ARGV(2, entity);
192  velocity_multiplier = M_ARGV(3, float);
193 
194  if(this.state == 1)
195  {
197  // speed the rope is pulled with
198 
199  rubberforce = autocvar_g_balance_grapplehook_force_rubber;//2000;
200  // force the rope will use if it is stretched
201 
202  rubberforce_overstretch = autocvar_g_balance_grapplehook_force_rubber_overstretch;//1000;
203  // force the rope will use if it is stretched
204 
206  // minimal rope length
207  // if the rope goes below this length, it isn't pulled any more
208 
209  ropestretch = autocvar_g_balance_grapplehook_stretch;//400;
210  // if the rope is stretched by more than this amount, more rope is
211  // given to you again
212 
213  ropeairfriction = autocvar_g_balance_grapplehook_airfriction;//0.2
214  // while hanging on the rope, this friction component will help you a
215  // bit to control the rope
216 
218 
219  dir = this.origin - myorg;
220  dist = vlen(dir);
221  dir = normalize(dir);
222 
223  if(tarzan)
224  {
225  v = v0 = WarpZone_RefSys_TransformVelocity(pull_entity, this, pull_entity.velocity);
226 
227  // first pull the rope...
228  if(this.realowner.(weaponentity).hook_state & HOOK_PULLING)
229  {
230  newlength = this.hook_length;
231  newlength = max(newlength - pullspeed * frametime, minlength);
232 
233  if(newlength < dist - ropestretch) // overstretched?
234  {
235  newlength = dist - ropestretch;
236  if(v * dir < 0) // only if not already moving in hook direction
237  v = v + frametime * dir * rubberforce_overstretch;
238  }
239 
240  this.hook_length = newlength;
241  }
242 
243  if(pull_entity.move_movetype == MOVETYPE_FLY)
244  set_movetype(pull_entity, MOVETYPE_WALK);
245 
246  if(this.realowner.(weaponentity).hook_state & HOOK_RELEASING)
247  {
248  newlength = dist;
249  this.hook_length = newlength;
250  }
251  else
252  {
253  // then pull the player
254  spd = bound(0, (dist - this.hook_length) / ropestretch, 1);
255  v = v * (1 - frametime * ropeairfriction);
256  v = v + frametime * dir * spd * rubberforce;
257 
258  dv = ((v - v0) * dir) * dir;
259  if(tarzan >= 2)
260  {
261  if(this.aiment.move_movetype == MOVETYPE_WALK || this.aiment.classname == "nade")
262  {
263  entity aim_ent = ((IS_VEHICLE(this.aiment) && this.aiment.owner) ? this.aiment.owner : this.aiment);
264  v = v - dv * 0.5;
265  if((frozen_pulling && STAT(FROZEN, this.aiment)) || !frozen_pulling)
266  {
267  this.aiment.velocity = this.aiment.velocity - dv * 0.5;
268  UNSET_ONGROUND(this.aiment);
269  if(this.aiment.flags & FL_PROJECTILE)
271  }
272  if(this.aiment.classname == "nade")
273  this.aiment.nextthink = time + autocvar_g_balance_grapplehook_nade_time; // set time after letting go?
274  aim_ent.pusher = this.realowner;
275  aim_ent.pushltime = time + autocvar_g_maxpushtime;
276  aim_ent.istypefrag = PHYS_INPUT_BUTTON_CHAT(aim_ent);
277  }
278  }
279 
280  UNSET_ONGROUND(pull_entity);
281  }
282 
283  if(!frozen_pulling && !(this.aiment.flags & FL_PROJECTILE))
284  pull_entity.velocity = WarpZone_RefSys_TransformVelocity(this, pull_entity, v * velocity_multiplier);
285 
286  if(frozen_pulling && autocvar_g_balance_grapplehook_pull_frozen == 2 && !STAT(FROZEN, this.aiment))
287  {
288  RemoveHook(this);
289  return;
290  }
291  }
292  else
293  {
294  end = this.origin - dir*50;
295  dist = vlen(end - myorg);
296  if(dist < 200)
297  spd = dist * (pullspeed / 200);
298  else
299  spd = pullspeed;
300  if(spd < 50)
301  spd = 0;
302  this.realowner.velocity = dir*spd;
304 
306  }
307  }
308 
309  makevectors(this.angles.x * '-1 0 0' + this.angles.y * '0 1 0');
310  myorg = WarpZone_RefSys_TransformOrigin(this, this.realowner, this.origin); // + v_forward * (-9);
311 
312  if(myorg != this.hook_start)
313  {
314  this.SendFlags |= 2;
315  this.hook_start = myorg;
316  }
317  if(org != this.hook_end)
318  {
319  this.SendFlags |= 4;
320  this.hook_end = org;
321  }
322 }
323 
324 void GrapplingHookTouch(entity this, entity toucher)
325 {
326  if(toucher.move_movetype == MOVETYPE_FOLLOW)
327  return;
328  PROJECTILE_TOUCH(this, toucher);
329 
330  GrapplingHook_Stop(this);
331 
332  if(toucher)
333  //if(toucher.move_movetype != MOVETYPE_NONE)
334  {
335  SetMovetypeFollow(this, toucher);
337  }
338 
339  //this.realowner.disableclientprediction = true;
340 }
341 
342 void GrapplingHook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
343 {
344  if(GetResource(this, RES_HEALTH) <= 0)
345  return;
346 
347  if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
348  return; // g_balance_projectiledamage says to halt
349 
350  TakeResource(this, RES_HEALTH, damage);
351 
352  if (GetResource(this, RES_HEALTH) <= 0)
353  {
354  if(attacker != this.realowner)
355  {
356  this.realowner.pusher = attacker;
357  this.realowner.pushltime = time + autocvar_g_maxpushtime;
358  this.realowner.istypefrag = PHYS_INPUT_BUTTON_CHAT(this.realowner);
359  }
360  RemoveHook(this);
361  }
362 }
363 
364 void FireGrapplingHook(entity actor, .entity weaponentity)
365 {
366  if(weaponLocked(actor)) return;
367  if(actor.vehicle) return;
368 
369  int s = W_GunAlign(actor.(weaponentity), STAT(GUNALIGN, actor)) - 1;
370  vector vs = hook_shotorigin[s];
371  vector oldmovedir = actor.(weaponentity).movedir;
372  actor.(weaponentity).movedir = vs;
373  W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', true, 0, SND_HOOK_FIRE, CH_WEAPON_B, 0, WEP_HOOK.m_id);
374  W_MuzzleFlash(WEP_HOOK, actor, weaponentity, w_shotorg, '0 0 0');
375  actor.(weaponentity).movedir = oldmovedir;
376 
377  entity missile = WarpZone_RefSys_SpawnSameRefSys(actor);
378  missile.owner = missile.realowner = actor;
379  actor.(weaponentity).hook = missile;
380  missile.weaponentity_fld = weaponentity;
381  missile.reset = GrapplingHookReset;
382  missile.classname = "grapplinghook";
383  missile.flags = FL_PROJECTILE;
384  IL_PUSH(g_projectiles, missile);
385  IL_PUSH(g_bot_dodge, missile);
386 
388  PROJECTILE_MAKETRIGGER(missile);
389 
390  //setmodel (missile, MDL_HOOK); // precision set below
391  setsize (missile, '-3 -3 -3', '3 3 3');
392  setorigin(missile, w_shotorg);
393 
394  missile.state = 0; // not latched onto anything
395 
397 
398  missile.angles = vectoangles (missile.velocity);
399  //missile.glow_color = 250; // 244, 250
400  //missile.glow_size = 120;
401  settouch(missile, GrapplingHookTouch);
402  setthink(missile, GrapplingHookThink);
403  missile.nextthink = time;
404 
405  missile.effects = /*EF_FULLBRIGHT | EF_ADDITIVE |*/ EF_LOWPRECISION;
406 
408  missile.event_damage = GrapplingHook_Damage;
409  missile.takedamage = DAMAGE_AIM;
410  missile.damageforcescale = 0;
411  missile.damagedbycontents = (autocvar_g_balance_grapplehook_damagedbycontents);
412  if(missile.damagedbycontents)
413  IL_PUSH(g_damagedbycontents, missile);
414 
415  missile.hook_start = missile.hook_end = missile.origin;
416 
417  Net_LinkEntity(missile, false, 0, GrapplingHookSend);
418 }
419 
420 // NOTE: using PRECACHE here to make sure it's called after everything else
421 PRECACHE(GrappleHookInit)
422 {
423  if(g_grappling_hook)
424  {
425  hook_shotorigin[0] = '8 8 -12';
426  hook_shotorigin[1] = '8 8 -12';
427  hook_shotorigin[2] = '8 8 -12';
428  hook_shotorigin[3] = '8 8 -12';
429  }
430  else
431  {
432  Weapon w = WEP_HOOK;
433  w.wr_init(w);
434  hook_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 1);
435  hook_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 2);
436  hook_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 3);
437  hook_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK.m_id), false, false, 4);
438  }
439 }
float state
Definition: subs.qh:32
#define round_handler_IsActive()
float MOVETYPE_NONE
Definition: progsdefs.qc:246
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition: player.qh:155
float MOVETYPE_WALK
Definition: progsdefs.qc:249
float autocvar_g_balance_grapplehook_airfriction
Definition: hook.qh:3
void GrapplingHookTouch(entity this, entity toucher)
Definition: hook.qc:324
entity hook
Definition: hook.qh:19
vector w_shotorg
Definition: tracing.qh:18
#define PROJECTILE_MAKETRIGGER(e)
Definition: common.qh:29
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
Definition: cl_resources.qc:15
float autocvar_g_balance_grapplehook_damagedbycontents
Definition: hook.qh:11
float autocvar_g_balance_grapplehook_speed_pull
Definition: hook.qh:9
float MOVETYPE_TOSS
Definition: progsdefs.qc:252
#define W_SetupShot_ProjectileSize(ent, wepent, mi, ma, antilag, recoil, snd, chan, maxdamage, deathtype)
Definition: tracing.qh:29
float W_CheckProjectileDamage(entity inflictor, entity projowner, int deathtype, float exception)
Definition: common.qc:49
float autocvar_g_balance_grapplehook_health
Definition: hook.qh:6
IntrusiveList g_damagedbycontents
Definition: damage.qh:155
entity() spawn
float DAMAGE_AIM
Definition: progsdefs.qc:284
const float HOOK_RELEASING
Definition: hook.qh:31
vector hook_shotorigin[4]
Definition: main.qh:177
vector w_shotdir
Definition: tracing.qh:19
float hook_length
Definition: hook.qh:26
entity weaponentity_fld
Definition: weaponsystem.qh:27
float autocvar_g_balance_grapplehook_stretch
Definition: hook.qh:10
entity to
Definition: self.qh:96
origin
Definition: ent_cs.qc:114
void GrapplingHook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition: hook.qc:342
int W_GunAlign(entity this, int preferred_align)
#define round_handler_IsRoundStarted()
void WarpZone_RefSys_AddIncrementally(entity me, entity ref)
Definition: common.qc:732
void GrapplingHook_Stop(entity this)
Definition: hook.qc:114
#define UNSET_ONGROUND(s)
Definition: movetypes.qh:18
vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel)
Definition: common.qc:756
float autocvar_g_maxpushtime
Definition: damage.qh:17
vector hook_end
Definition: hook.qc:128
float move_movetype
Definition: movetypes.qh:76
IntrusiveList g_bot_dodge
Definition: api.qh:150
vector hook_start
Definition: hook.qc:128
int autocvar_g_grappling_hook_tarzan
Definition: hook.qc:152
bool GrapplingHookSend(entity this, entity to, int sf)
Definition: hook.qc:129
void TakeResource(entity receiver, Resource res_type, float amount)
Takes an entity some resource.
Definition: cl_resources.qc:31
int weaponslot(.entity weaponentity)
Definition: weapon.qh:16
#define PROJECTILE_TOUCH(e, t)
Definition: common.qh:27
void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref)
Definition: common.qc:743
RES_HEALTH
Definition: ent_cs.qc:126
float autocvar_g_balance_grapplehook_force_rubber_overstretch
Definition: hook.qh:5
bool weaponLocked(entity player)
vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org)
Definition: common.qc:748
PRECACHE(GrappleHookInit)
Definition: hook.qc:421
void W_SetupProjVelocity_Explicit(entity proj, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute)
Definition: tracing.qc:185
vector movedir
Definition: progsdefs.qc:203
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
void UpdateCSQCProjectile(entity e)
vector v_up
Definition: csprogsdefs.qc:31
const int MAX_WEAPONSLOTS
Definition: weapon.qh:13
float autocvar_g_balance_grapplehook_length_min
Definition: hook.qh:7
void GrapplingHookThink(entity this)
Definition: hook.qc:154
entity WarpZone_RefSys_SpawnSameRefSys(entity me)
Definition: common.qc:791
const float HOOK_PULLING
Definition: hook.qh:30
#define NULL
Definition: post.qh:17
float frametime
Definition: csprogsdefs.qc:17
const float VOL_BASE
Definition: sound.qh:36
#define M_ARGV(x, type)
Definition: events.qh:17
const float ATTEN_NORM
Definition: sound.qh:30
float nextthink
Definition: csprogsdefs.qc:121
const int CH_SHOTS
Definition: sound.qh:14
void RemoveGrapplingHooks(entity pl)
Definition: hook.qc:78
vector CL_Weapon_GetShotOrg(int wpn)
Definition: weaponsystem.qc:63
vector(float skel, float bonenum) _skel_get_boneabs_hidden
#define IS_VEHICLE(v)
Definition: utils.qh:22
float autocvar_g_balance_grapplehook_force_rubber
Definition: hook.qh:4
void FireGrapplingHook(entity actor,.entity weaponentity)
Definition: hook.qc:364
float autocvar_g_balance_grapplehook_speed_fly
Definition: hook.qh:8
IntrusiveList g_projectiles
Definition: common.qh:46
vector v
Definition: ent_cs.qc:116
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
Definition: cl_resources.qc:10
float g_grappling_hook
Definition: world.qh:116
float MOVETYPE_FOLLOW
bool autocvar_g_balance_grapplehook_gravity
Definition: hook.qh:15
vector v_right
Definition: csprogsdefs.qc:31
entity realowner
Definition: common.qh:25
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
entity aiment
Definition: movetypes.qh:90
entity weaponentities[MAX_WEAPONSLOTS]
Definition: weapon.qh:14
void GrapplingHookReset(entity this)
Definition: hook.qc:109
setorigin(ent, v)
#define setthink(e, f)
vector angles
Definition: csprogsdefs.qc:104
int autocvar_g_balance_grapplehook_pull_frozen
Definition: hook.qh:13
#define sound(e, c, s, v, a)
Definition: sound.qh:52
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition: weapon.qh:41
float time
Definition: csprogsdefs.qc:16
vector velocity
Definition: csprogsdefs.qc:103
const int CH_WEAPON_B
Definition: sound.qh:8
int dir
Definition: impulse.qc:89
#define makevectors
Definition: post.qh:21
float autocvar_g_balance_grapplehook_nade_time
Definition: hook.qh:14
void set_movetype(entity this, int mt)
float MOVETYPE_FLY
Definition: progsdefs.qc:251
float EF_LOWPRECISION
var void func_null()
void RemoveHook(entity this)
Definition: hook.qc:96
vector v_forward
Definition: csprogsdefs.qc:31