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 &= 0x7F;
86 sf |= 0x80;
88 if (sf & 1)
89 {
92 }
93 if (sf & 2)
94 WriteVector(MSG_ENTITY, this.hook_start);
95 if (sf & 4)
96 WriteVector(MSG_ENTITY, this.hook_end);
97
98 return true;
99}
100
102
104{
105 .entity weaponentity = this.weaponentity_fld;
106 if (this.realowner.(weaponentity).hook != this) // how did that happen?
107 {
108 error("Owner lost the hook!\n");
109 return;
110 }
113 || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade"))
114 {
115 RemoveHook(this);
116 return;
117 }
118 if (this.aiment)
120
121 this.nextthink = time;
122
123 int s = W_GunAlign(this.realowner.(weaponentity), STAT(GUNALIGN, this.realowner)) - 1;
124
125 makevectors(this.realowner.v_angle);
126 vector vs = hook_shotorigin[s];
127 vs = v_forward * vs.x + v_right * -vs.y + v_up * vs.z;
128 vector org = this.realowner.origin + this.realowner.view_ofs + vs;
130
131 if (this.hook_length < 0)
132 this.hook_length = vlen(myorg - this.origin);
133
134 entity pull_entity = this.realowner;
136 float velocity_multiplier = 1;
137 MUTATOR_CALLHOOK(GrappleHookThink, this, tarzan, pull_entity, velocity_multiplier);
138 tarzan = M_ARGV(1, int);
139 pull_entity = M_ARGV(2, entity);
140 velocity_multiplier = M_ARGV(3, float);
141
142 if (this.state == 1)
143 {
145 // speed the rope is pulled with
146
148 // force the rope will use if it is stretched
149
150 float rubberforce_overstretch = autocvar_g_balance_grapplehook_force_rubber_overstretch;
151 // force the rope will use if it is stretched
152
154 // minimal rope length
155 // if the rope goes below this length, it isn't pulled any more
156
157 float ropestretch = autocvar_g_balance_grapplehook_stretch;
158 // if the rope is stretched by more than this amount, more rope is
159 // given to you again
160
161 float ropeairfriction = autocvar_g_balance_grapplehook_airfriction;
162 // while hanging on the rope, this friction component will help you a
163 // bit to control the rope
164
166 bool target_isfrozen = (STAT(FROZEN, this.aiment) || StatusEffects_active(STATUSEFFECT_Frozen, this.aiment));
167
168 vector dir = this.origin - myorg;
169 float dist = vlen(dir);
170
171 // TODO: Consider changing this back to `dir / dist` after https://github.com/graphitemaster/gmqcc/issues/210.
172 dir = (dist ? dir * (1 / dist) : '0 0 0'); // same as dir = normalize(dir); but cheaper
173
174 float spd;
175 if (tarzan)
176 {
177 vector v, v0;
178 v = v0 = WarpZone_RefSys_TransformVelocity(pull_entity, this, pull_entity.velocity);
179
180 // first pull the rope...
181 if (this.realowner.(weaponentity).hook_state & HOOK_PULLING)
182 {
183 float newlength = max(this.hook_length - pullspeed * frametime, minlength);
184
185 if (newlength < dist - ropestretch) // overstretched?
186 {
187 newlength = dist - ropestretch;
188 if (v * dir < 0) // only if not already moving in hook direction
189 v += frametime * dir * rubberforce_overstretch;
190 }
191
192 this.hook_length = newlength;
193 }
194
195 if (pull_entity.move_movetype == MOVETYPE_FLY)
196 set_movetype(pull_entity, MOVETYPE_WALK);
197
198 if (this.realowner.(weaponentity).hook_state & HOOK_RELEASING)
199 this.hook_length = dist;
200 else
201 {
202 // then pull the player
203 spd = bound(0, (dist - this.hook_length) / ropestretch, 1);
204 v *= 1 - frametime * ropeairfriction;
205 v += frametime * dir * spd * rubberforce;
206
207 vector dv = ((v - v0) * dir) * dir;
208 if (tarzan >= 2)
209 {
210 if (this.aiment.move_movetype == MOVETYPE_WALK || this.aiment.classname == "nade")
211 {
212 entity aim_ent = ((IS_VEHICLE(this.aiment) && this.aiment.owner) ? this.aiment.owner : this.aiment);
213 v -= dv * 0.5;
214 if ((frozen_pulling && target_isfrozen) || !frozen_pulling)
215 {
216 this.aiment.velocity -= dv * 0.5;
218 if (this.aiment.flags & FL_PROJECTILE)
220 }
221 if (this.aiment.classname == "nade")
222 this.aiment.nextthink = time + autocvar_g_balance_grapplehook_nade_time; // set time after letting go?
223 aim_ent.pusher = this.realowner;
224 aim_ent.pushltime = time + autocvar_g_maxpushtime;
225 aim_ent.istypefrag = PHYS_INPUT_BUTTON_CHAT(aim_ent);
226 }
227 }
228
229 UNSET_ONGROUND(pull_entity);
230 }
231
232 if (!frozen_pulling && !(this.aiment.flags & FL_PROJECTILE))
233 pull_entity.velocity = WarpZone_RefSys_TransformVelocity(this, pull_entity, v * velocity_multiplier);
234
235 if (frozen_pulling && autocvar_g_balance_grapplehook_pull_frozen == 2 && !target_isfrozen)
236 {
237 RemoveHook(this);
238 return;
239 }
240 }
241 else
242 {
243 vector end = this.origin - dir * 50;
244 dist = vlen(end - myorg);
245 if (dist < 200)
246 spd = dist * (pullspeed / 200);
247 else
248 spd = pullspeed;
249 if (spd < 50)
250 spd = 0;
251 this.realowner.velocity = dir * spd;
253
255 }
256 }
257
258 makevectors(this.angles.x * '-1 0 0' + this.angles.y * '0 1 0');
259 myorg = WarpZone_RefSys_TransformOrigin(this, this.realowner, this.origin); // + v_forward * (-9);
260
261 if (myorg != this.hook_start)
262 {
263 this.SendFlags |= 2;
264 this.hook_start = myorg;
265 }
266 if (org != this.hook_end)
267 {
268 this.SendFlags |= 4;
269 this.hook_end = org;
270 }
271}
272
274{
275 if (toucher.move_movetype == MOVETYPE_FOLLOW)
276 return;
278
279 GrapplingHook_Stop(this);
280
281 if (toucher)
282 //if (toucher.move_movetype != MOVETYPE_NONE)
283 {
286 }
287
288 //this.realowner.disableclientprediction = true;
289}
290
291void GrapplingHook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
292{
293 if (GetResource(this, RES_HEALTH) <= 0)
294 return;
295 if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
296 return; // g_balance_projectiledamage says to halt
297
298 TakeResource(this, RES_HEALTH, damage);
299 if (GetResource(this, RES_HEALTH) <= 0)
300 {
301 if (attacker != this.realowner)
302 {
303 this.realowner.pusher = attacker;
304 this.realowner.pushltime = time + autocvar_g_maxpushtime;
305 this.realowner.istypefrag = PHYS_INPUT_BUTTON_CHAT(this.realowner);
306 }
307 RemoveHook(this);
308 }
309}
310
311void FireGrapplingHook(entity actor, .entity weaponentity)
312{
313 if (weaponLocked(actor) || actor.vehicle)
314 return;
315
316 int s = W_GunAlign(actor.(weaponentity), STAT(GUNALIGN, actor)) - 1;
317 vector oldmovedir = actor.(weaponentity).movedir;
318 actor.(weaponentity).movedir = hook_shotorigin[s];
319 W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', true, 0, SND_HOOK_FIRE, CH_WEAPON_B, 0, WEP_HOOK.m_id);
320 W_MuzzleFlash(WEP_HOOK, actor, weaponentity, w_shotorg, '0 0 0');
321 actor.(weaponentity).movedir = oldmovedir;
322
324 missile.owner = missile.realowner = actor;
325 actor.(weaponentity).hook = missile;
326 missile.weaponentity_fld = weaponentity;
327 missile.reset = GrapplingHookReset;
328 missile.classname = "grapplinghook";
329 missile.flags = FL_PROJECTILE;
330 IL_PUSH(g_projectiles, missile);
331 IL_PUSH(g_bot_dodge, missile);
332
334 PROJECTILE_MAKETRIGGER(missile);
335
336 //setmodel(missile, MDL_HOOK); // precision set below
337 setsize(missile, '-3 -3 -3', '3 3 3');
338 setorigin(missile, w_shotorg);
339
340 missile.state = 0; // not latched onto anything
341
343
344 missile.angles = vectoangles(missile.velocity);
345 //missile.glow_color = 250; // 244, 250
346 //missile.glow_size = 120;
349 missile.nextthink = time;
350
351 missile.effects = /*EF_FULLBRIGHT | EF_ADDITIVE |*/ EF_LOWPRECISION;
352
354 missile.event_damage = GrapplingHook_Damage;
355 missile.takedamage = DAMAGE_AIM;
356 missile.damageforcescale = 0;
357 missile.damagedbycontents = (autocvar_g_balance_grapplehook_damagedbycontents);
358 if (missile.damagedbycontents)
360
361 missile.hook_start = missile.hook_end = missile.origin;
362
363 Net_LinkEntity(missile, false, 0, GrapplingHookSend);
364
365 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
366}
367
368// NOTE: using PRECACHE here to make sure it's called after everything else
369PRECACHE(GrappleHookInit)
370{
372 {
373 hook_shotorigin[0] = '8 8 -12';
374 hook_shotorigin[1] = '8 8 -12';
375 hook_shotorigin[2] = '8 8 -12';
376 hook_shotorigin[3] = '8 8 -12';
377 }
378 else
379 {
380 Weapon w = WEP_HOOK;
381 w.wr_init(w);
382 vector vecs = CL_Weapon_GetShotOrg(WEP_HOOK.m_id);
383 hook_shotorigin[0] = shotorg_adjust(vecs, false, false, 1);
384 hook_shotorigin[1] = shotorg_adjust(vecs, false, false, 2);
385 hook_shotorigin[2] = shotorg_adjust(vecs, false, false, 3);
386 hook_shotorigin[3] = shotorg_adjust(vecs, false, false, 4);
387 }
388}
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:42
virtual void wr_init()
(BOTH) precaches models/sounds used by this weapon, also sets up weapon properties
Definition weapon.qh:114
vector hook_shotorigin[4]
Definition main.qh:205
#define M_ARGV(x, type)
Definition events.qh:17
entity hook
Definition player.qh:238
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition player.qh:161
float game_stopped
Definition stats.qh:81
void SetMovetypeFollow(entity ent, entity e)
Definition util.qc:2129
int LostMovetypeFollow(entity ent)
Definition util.qc:2159
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:143
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:120
ent angles
Definition ent_cs.qc:121
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
int SendFlags
Definition net.qh:159
const int MSG_ENTITY
Definition net.qh:156
#define WriteHeader(to, id)
Definition net.qh:265
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:167
#define STAT(...)
Definition stats.qh:82
vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel)
Definition common.qc:771
void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref)
Definition common.qc:758
void WarpZone_RefSys_AddIncrementally(entity me, entity ref)
Definition common.qc:747
entity WarpZone_RefSys_SpawnSameRefSys(entity me)
Definition common.qc:806
vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org)
Definition common.qc:763
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:291
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:273
int autocvar_g_grappling_hook_tarzan
Definition hook.qc:101
vector hook_end
Definition hook.qc:80
void GrapplingHookThink(entity this)
Definition hook.qc:103
void FireGrapplingHook(entity actor,.entity weaponentity)
Definition hook.qc:311
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:185
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:24
void W_MuzzleFlash(Weapon thiswep, entity actor,.entity weaponentity, vector shotorg, vector shotdir)
Definition all.qc:715
vector shotorg_adjust(vector vecs, bool y_is_right, bool visual, int algn)
Definition all.qc:260
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