Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
hook.qc
Go to the documentation of this file.
1#include "hook.qh"
2
3#include <common/constants.qh>
8#include <common/state.qh>
9#include <common/stats.qh>
10#include <common/util.qh>
16#include <server/bot/api.qh>
19#include <server/damage.qh>
21#include <server/player.qh>
28#include <server/world.qh>
29
31{
32 if(pl.move_movetype == MOVETYPE_FLY)
34
35 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
36 {
37 .entity weaponentity = weaponentities[slot];
38 if(!pl.(weaponentity))
39 continue; // continue incase other slots exist?
40 if(pl.(weaponentity).hook)
41 delete(pl.(weaponentity).hook);
42 pl.(weaponentity).hook = NULL;
43 }
44
45 //pl.disableclientprediction = false;
46}
47
49{
50 entity player = this.realowner;
51 .entity weaponentity = this.weaponentity_fld;
52
53 if(player.(weaponentity).hook == this)
54 player.(weaponentity).hook = NULL;
55
56 if(player.move_movetype == MOVETYPE_FLY)
58 delete(this);
59}
60
62{
63 RemoveHook(this);
64}
65
67{
68 Send_Effect(EFFECT_HOOK_IMPACT, this.origin, '0 0 0', 1);
69 sound(this, CH_SHOTS, SND_HOOK_IMPACT, VOL_BASE, ATTEN_NORM);
70
71 this.state = 1;
73 this.nextthink = time;
74 settouch(this, func_null);
75 this.velocity = '0 0 0';
77 this.hook_length = -1;
78}
79
81bool GrapplingHookSend(entity this, entity to, int sf)
82{
83 WriteHeader(MSG_ENTITY, ENT_CLIENT_HOOK);
84 sf = sf & 0x7F;
86 sf |= 0x80;
88 if(sf & 1)
89 {
92 }
93 if(sf & 2)
94 {
95 WriteVector(MSG_ENTITY, this.hook_start);
96 }
97 if(sf & 4)
98 {
99 WriteVector(MSG_ENTITY, this.hook_end);
100 }
101 return true;
102}
103
105
107{
108 float spd, dist;
109
110 .entity weaponentity = this.weaponentity_fld;
111 if(this.realowner.(weaponentity).hook != this) // how did that happen?
112 {
113 error("Owner lost the hook!\n");
114 return;
115 }
118 || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade"))
119 {
120 RemoveHook(this);
121 return;
122 }
123 if(this.aiment)
125
126 this.nextthink = time;
127
128 int s = W_GunAlign(this.realowner.(weaponentity), STAT(GUNALIGN, this.realowner)) - 1;
129
130 makevectors(this.realowner.v_angle);
131 vector vs = hook_shotorigin[s];
132 vs = v_forward * vs.x + v_right * -vs.y + v_up * vs.z;
133 vector org = this.realowner.origin + this.realowner.view_ofs + vs;
135
136 if(this.hook_length < 0)
137 this.hook_length = vlen(myorg - this.origin);
138
140 entity pull_entity = this.realowner;
141 float velocity_multiplier = 1;
142 MUTATOR_CALLHOOK(GrappleHookThink, this, tarzan, pull_entity, velocity_multiplier);
143 tarzan = M_ARGV(1, int);
144 pull_entity = M_ARGV(2, entity);
145 velocity_multiplier = M_ARGV(3, float);
146
147 if(this.state == 1)
148 {
150 // speed the rope is pulled with
151
153 // force the rope will use if it is stretched
154
155 float rubberforce_overstretch = autocvar_g_balance_grapplehook_force_rubber_overstretch;
156 // force the rope will use if it is stretched
157
159 // minimal rope length
160 // if the rope goes below this length, it isn't pulled any more
161
162 float ropestretch = autocvar_g_balance_grapplehook_stretch;
163 // if the rope is stretched by more than this amount, more rope is
164 // given to you again
165
166 float ropeairfriction = autocvar_g_balance_grapplehook_airfriction;
167 // while hanging on the rope, this friction component will help you a
168 // bit to control the rope
169
171 bool target_isfrozen = (STAT(FROZEN, this.aiment) || StatusEffects_active(STATUSEFFECT_Frozen, this.aiment));
172
173 vector dir = this.origin - myorg;
174 dist = vlen(dir);
175
176 // TODO: Consider changing this back to `dir / dist` after https://github.com/graphitemaster/gmqcc/issues/210.
177 dir = (dist ? dir * (1 / dist) : '0 0 0'); // same as dir = normalize(dir); but cheaper
178
179 if(tarzan)
180 {
181 vector v, v0;
182 v = v0 = WarpZone_RefSys_TransformVelocity(pull_entity, this, pull_entity.velocity);
183
184 // first pull the rope...
185 if(this.realowner.(weaponentity).hook_state & HOOK_PULLING)
186 {
187 float newlength = max(this.hook_length - pullspeed * frametime, minlength);
188
189 if(newlength < dist - ropestretch) // overstretched?
190 {
191 newlength = dist - ropestretch;
192 if(v * dir < 0) // only if not already moving in hook direction
193 v = v + frametime * dir * rubberforce_overstretch;
194 }
195
196 this.hook_length = newlength;
197 }
198
199 if(pull_entity.move_movetype == MOVETYPE_FLY)
200 set_movetype(pull_entity, MOVETYPE_WALK);
201
202 if(this.realowner.(weaponentity).hook_state & HOOK_RELEASING)
203 {
204 this.hook_length = dist;
205 }
206 else
207 {
208 // then pull the player
209 spd = bound(0, (dist - this.hook_length) / ropestretch, 1);
210 v = v * (1 - frametime * ropeairfriction);
211 v = v + frametime * dir * spd * rubberforce;
212
213 vector dv = ((v - v0) * dir) * dir;
214 if(tarzan >= 2)
215 {
216 if(this.aiment.move_movetype == MOVETYPE_WALK || this.aiment.classname == "nade")
217 {
218 entity aim_ent = ((IS_VEHICLE(this.aiment) && this.aiment.owner) ? this.aiment.owner : this.aiment);
219 v = v - dv * 0.5;
220 if((frozen_pulling && target_isfrozen) || !frozen_pulling)
221 {
222 this.aiment.velocity = this.aiment.velocity - dv * 0.5;
224 if(this.aiment.flags & FL_PROJECTILE)
226 }
227 if(this.aiment.classname == "nade")
228 this.aiment.nextthink = time + autocvar_g_balance_grapplehook_nade_time; // set time after letting go?
229 aim_ent.pusher = this.realowner;
230 aim_ent.pushltime = time + autocvar_g_maxpushtime;
231 aim_ent.istypefrag = PHYS_INPUT_BUTTON_CHAT(aim_ent);
232 }
233 }
234
235 UNSET_ONGROUND(pull_entity);
236 }
237
238 if(!frozen_pulling && !(this.aiment.flags & FL_PROJECTILE))
239 pull_entity.velocity = WarpZone_RefSys_TransformVelocity(this, pull_entity, v * velocity_multiplier);
240
241 if(frozen_pulling && autocvar_g_balance_grapplehook_pull_frozen == 2 && !target_isfrozen)
242 {
243 RemoveHook(this);
244 return;
245 }
246 }
247 else
248 {
249 vector end = this.origin - dir * 50;
250 dist = vlen(end - myorg);
251 if(dist < 200)
252 spd = dist * (pullspeed / 200);
253 else
254 spd = pullspeed;
255 if(spd < 50)
256 spd = 0;
257 this.realowner.velocity = dir * spd;
259
261 }
262 }
263
264 makevectors(this.angles.x * '-1 0 0' + this.angles.y * '0 1 0');
265 myorg = WarpZone_RefSys_TransformOrigin(this, this.realowner, this.origin); // + v_forward * (-9);
266
267 if(myorg != this.hook_start)
268 {
269 this.SendFlags |= 2;
270 this.hook_start = myorg;
271 }
272 if(org != this.hook_end)
273 {
274 this.SendFlags |= 4;
275 this.hook_end = org;
276 }
277}
278
280{
281 if(toucher.move_movetype == MOVETYPE_FOLLOW)
282 return;
284
285 GrapplingHook_Stop(this);
286
287 if(toucher)
288 //if(toucher.move_movetype != MOVETYPE_NONE)
289 {
292 }
293
294 //this.realowner.disableclientprediction = true;
295}
296
297void GrapplingHook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
298{
299 if(GetResource(this, RES_HEALTH) <= 0)
300 return;
301
302 if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
303 return; // g_balance_projectiledamage says to halt
304
305 TakeResource(this, RES_HEALTH, damage);
306
307 if (GetResource(this, RES_HEALTH) <= 0)
308 {
309 if(attacker != this.realowner)
310 {
311 this.realowner.pusher = attacker;
312 this.realowner.pushltime = time + autocvar_g_maxpushtime;
313 this.realowner.istypefrag = PHYS_INPUT_BUTTON_CHAT(this.realowner);
314 }
315 RemoveHook(this);
316 }
317}
318
319void FireGrapplingHook(entity actor, .entity weaponentity)
320{
321 if(weaponLocked(actor)) return;
322 if(actor.vehicle) return;
323
324 int s = W_GunAlign(actor.(weaponentity), STAT(GUNALIGN, actor)) - 1;
325 vector oldmovedir = actor.(weaponentity).movedir;
326 actor.(weaponentity).movedir = hook_shotorigin[s];
327 W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', true, 0, SND_HOOK_FIRE, CH_WEAPON_B, 0, WEP_HOOK.m_id);
328 W_MuzzleFlash(WEP_HOOK, actor, weaponentity, w_shotorg, '0 0 0');
329 actor.(weaponentity).movedir = oldmovedir;
330
332 missile.owner = missile.realowner = actor;
333 actor.(weaponentity).hook = missile;
334 missile.weaponentity_fld = weaponentity;
335 missile.reset = GrapplingHookReset;
336 missile.classname = "grapplinghook";
337 missile.flags = FL_PROJECTILE;
338 IL_PUSH(g_projectiles, missile);
339 IL_PUSH(g_bot_dodge, missile);
340
342 PROJECTILE_MAKETRIGGER(missile);
343
344 //setmodel (missile, MDL_HOOK); // precision set below
345 setsize (missile, '-3 -3 -3', '3 3 3');
346 setorigin(missile, w_shotorg);
347
348 missile.state = 0; // not latched onto anything
349
351
352 missile.angles = vectoangles (missile.velocity);
353 //missile.glow_color = 250; // 244, 250
354 //missile.glow_size = 120;
357 missile.nextthink = time;
358
359 missile.effects = /*EF_FULLBRIGHT | EF_ADDITIVE |*/ EF_LOWPRECISION;
360
362 missile.event_damage = GrapplingHook_Damage;
363 missile.takedamage = DAMAGE_AIM;
364 missile.damageforcescale = 0;
365 missile.damagedbycontents = (autocvar_g_balance_grapplehook_damagedbycontents);
366 if(missile.damagedbycontents)
368
369 missile.hook_start = missile.hook_end = missile.origin;
370
371 Net_LinkEntity(missile, false, 0, GrapplingHookSend);
372
373 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
374}
375
376// NOTE: using PRECACHE here to make sure it's called after everything else
377PRECACHE(GrappleHookInit)
378{
380 {
381 hook_shotorigin[0] = '8 8 -12';
382 hook_shotorigin[1] = '8 8 -12';
383 hook_shotorigin[2] = '8 8 -12';
384 hook_shotorigin[3] = '8 8 -12';
385 }
386 else
387 {
388 Weapon w = WEP_HOOK;
389 w.wr_init(w);
390 vector vecs = CL_Weapon_GetShotOrg(WEP_HOOK.m_id);
391 hook_shotorigin[0] = shotorg_adjust(vecs, false, false, 1);
392 hook_shotorigin[1] = shotorg_adjust(vecs, false, false, 2);
393 hook_shotorigin[2] = shotorg_adjust(vecs, false, false, 3);
394 hook_shotorigin[3] = shotorg_adjust(vecs, false, false, 4);
395 }
396}
IntrusiveList g_bot_dodge
Definition api.qh:150
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
int W_GunAlign(entity this, int preferred_align)
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
void TakeResource(entity receiver, Resource res_type, float amount)
Takes an entity some resource.
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:44
virtual void wr_init()
(BOTH) precaches models/sounds used by this weapon, also sets up weapon properties
Definition weapon.qh:98
vector hook_shotorigin[4]
Definition main.qh:205
#define M_ARGV(x, type)
Definition events.qh:17
entity hook
Definition player.qh:239
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition player.qh:159
float game_stopped
Definition stats.qh:81
void SetMovetypeFollow(entity ent, entity e)
Definition util.qc:2033
int LostMovetypeFollow(entity ent)
Definition util.qc:2063
const int FL_PROJECTILE
Definition constants.qh:85
vector v_up
float frametime
vector velocity
float time
vector v_right
float nextthink
vector v_forward
vector origin
void UpdateCSQCProjectile(entity e)
IntrusiveList g_damagedbycontents
Definition damage.qh:135
float autocvar_g_maxpushtime
Definition damage.qh:17
int state
float EF_LOWPRECISION
void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt)
Definition all.qc:124
ent angles
Definition ent_cs.qc:121
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
int SendFlags
Definition net.qh:118
const int MSG_ENTITY
Definition net.qh:115
#define WriteHeader(to, id)
Definition net.qh:221
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
#define STAT(...)
Definition stats.qh:82
vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel)
Definition common.qc:773
void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref)
Definition common.qc:760
void WarpZone_RefSys_AddIncrementally(entity me, entity ref)
Definition common.qc:749
entity WarpZone_RefSys_SpawnSameRefSys(entity me)
Definition common.qc:808
vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org)
Definition common.qc:765
vector movedir
Definition viewloc.qh:18
float bound(float min, float value, float max)
float vlen(vector v)
vector vectoangles(vector v)
void WriteByte(float data, float dest, float desto)
float MSG_BROADCAST
Definition menudefs.qc:55
float max(float f,...)
#define etof(e)
Definition misc.qh:25
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
const int MOVETYPE_WALK
Definition movetypes.qh:132
const int MOVETYPE_NONE
Definition movetypes.qh:129
const int MOVETYPE_FOLLOW
Definition movetypes.qh:141
entity aiment
Definition movetypes.qh:90
float move_movetype
Definition movetypes.qh:76
#define UNSET_ONGROUND(s)
Definition movetypes.qh:18
const int MOVETYPE_FLY
Definition movetypes.qh:134
const int MOVETYPE_TOSS
Definition movetypes.qh:135
var void func_null()
#define NULL
Definition post.qh:14
#define makevectors
Definition post.qh:21
#define error
Definition pre.qh:6
#define round_handler_IsActive()
#define round_handler_IsRoundStarted()
#define setthink(e, f)
vector
Definition self.qh:92
vector org
Definition self.qh:92
entity entity toucher
Definition self.qh:72
#define settouch(e, f)
Definition self.qh:73
vector hook_start
Definition hook.qc:80
void GrapplingHook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition hook.qc:297
bool GrapplingHookSend(entity this, entity to, int sf)
Definition hook.qc:81
void RemoveHook(entity this)
Definition hook.qc:48
void GrapplingHook_Stop(entity this)
Definition hook.qc:66
void RemoveGrapplingHooks(entity pl)
Definition hook.qc:30
void GrapplingHookReset(entity this)
Definition hook.qc:61
void GrapplingHookTouch(entity this, entity toucher)
Definition hook.qc:279
int autocvar_g_grappling_hook_tarzan
Definition hook.qc:104
vector hook_end
Definition hook.qc:80
void GrapplingHookThink(entity this)
Definition hook.qc:106
void FireGrapplingHook(entity actor,.entity weaponentity)
Definition hook.qc:319
const float HOOK_RELEASING
Definition hook.qh:30
float hook_length
Definition hook.qh:25
const float HOOK_PULLING
Definition hook.qh:29
float autocvar_g_balance_grapplehook_speed_fly
Definition hook.qh:8
float autocvar_g_balance_grapplehook_force_rubber
Definition hook.qh:4
float autocvar_g_balance_grapplehook_health
Definition hook.qh:6
float autocvar_g_balance_grapplehook_damagedbycontents
Definition hook.qh:11
int autocvar_g_balance_grapplehook_pull_frozen
Definition hook.qh:13
float autocvar_g_balance_grapplehook_airfriction
Definition hook.qh:3
float autocvar_g_balance_grapplehook_force_rubber_overstretch
Definition hook.qh:5
float autocvar_g_balance_grapplehook_length_min
Definition hook.qh:7
float autocvar_g_balance_grapplehook_speed_pull
Definition hook.qh:9
float autocvar_g_balance_grapplehook_nade_time
Definition hook.qh:14
bool autocvar_g_balance_grapplehook_gravity
Definition hook.qh:15
float autocvar_g_balance_grapplehook_stretch
Definition hook.qh:10
int dir
Definition impulse.qc:89
float W_CheckProjectileDamage(entity inflictor, entity projowner, int deathtype, float exception)
Definition common.qc:45
#define PROJECTILE_TOUCH(e, t)
Definition common.qh:28
IntrusiveList g_projectiles
Definition common.qh:58
#define PROJECTILE_MAKETRIGGER(e)
Definition common.qh:34
const float VOL_BASE
Definition sound.qh:36
const int CH_SHOTS
Definition sound.qh:14
const float ATTEN_NORM
Definition sound.qh:30
#define sound(e, c, s, v, a)
Definition sound.qh:52
const int CH_WEAPON_B
Definition sound.qh:8
bool sound_allowed(int to, entity e)
Definition all.qc:9
#define PRECACHE(func)
directly after STATIC_INIT_LATE
Definition static.qh:42
bool StatusEffects_active(StatusEffect this, entity actor)
const int DAMAGE_AIM
Definition subs.qh:81
entity realowner
void W_SetupProjVelocity_Explicit(entity proj, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute)
Definition tracing.qc:192
vector w_shotdir
Definition tracing.qh:20
#define W_SetupShot_ProjectileSize(ent, wepent, mi, ma, antilag, recoil, snd, chan, maxdamage, deathtype)
Definition tracing.qh:30
vector w_shotorg
Definition tracing.qh:19
#define IS_VEHICLE(v)
Definition utils.qh:22
void W_MuzzleFlash(Weapon thiswep, entity actor,.entity weaponentity, vector shotorg, vector shotdir)
Definition all.qc:728
vector shotorg_adjust(vector vecs, bool y_is_right, bool visual, int algn)
Definition all.qc:262
const int MAX_WEAPONSLOTS
Definition weapon.qh:16
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
int weaponslot(.entity weaponentity)
Definition weapon.qh:19
vector CL_Weapon_GetShotOrg(int wpn)
bool weaponLocked(entity player)
entity weaponentity_fld
float g_grappling_hook
Definition world.qh:113