Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
rifle.qc
Go to the documentation of this file.
1#include "rifle.qh"
2
3#ifdef SVQC
4
5void W_Rifle_FireBullet(Weapon thiswep, .entity weaponentity, int deathtype, Sound pSound, entity actor, bool is_primary)
6{
7 float dmg = WEP_CVAR_BOTH(WEP_RIFLE, is_primary, damage);
8 float shots = WEP_CVAR_BOTH(WEP_RIFLE, is_primary, shots);
9
10 W_DecreaseAmmo(thiswep, actor, WEP_CVAR_BOTH(WEP_RIFLE, is_primary, ammo), weaponentity);
11
12 W_SetupShot(actor, weaponentity, true, 2, pSound, CH_WEAPON_A, dmg * shots, deathtype);
13
14 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir * 2);
15
16 if (PHYS_INPUT_BUTTON_ZOOM(actor) | PHYS_INPUT_BUTTON_ZOOMSCRIPT(actor)) // if zoomed, shoot from the eye
17 {
19 w_shotorg = actor.origin + actor.view_ofs + ((w_shotorg - actor.origin - actor.view_ofs) * v_forward) * v_forward;
20 }
21
22 for (int i = 0; i < shots; ++i)
23 fireBullet_falloff(actor, weaponentity, w_shotorg, w_shotdir,
24 WEP_CVAR_BOTH(WEP_RIFLE, is_primary, spread),
25 WEP_CVAR_BOTH(WEP_RIFLE, is_primary, solidpenetration),
26 dmg,
27 WEP_CVAR_BOTH(WEP_RIFLE, is_primary, damagefalloff_halflife),
28 WEP_CVAR_BOTH(WEP_RIFLE, is_primary, damagefalloff_mindist),
29 WEP_CVAR_BOTH(WEP_RIFLE, is_primary, damagefalloff_maxdist),
30 WEP_CVAR_BOTH(WEP_RIFLE, is_primary, headshot_multiplier),
31 WEP_CVAR_BOTH(WEP_RIFLE, is_primary, force),
32 WEP_CVAR_BOTH(WEP_RIFLE, is_primary, damagefalloff_forcehalflife),
33 deathtype,
34 (WEP_CVAR_BOTH(WEP_RIFLE, is_primary, tracer) ? EFFECT_RIFLE : EFFECT_RIFLE_WEAK),
35 true
36 );
37
38 if (autocvar_g_casings >= 2)
39 {
40 makevectors(actor.v_angle); // for some reason, this is lost
41 SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), vectoangles(v_forward), 3, actor, weaponentity);
42 }
43}
44
45void W_Rifle_Attack(Weapon thiswep, entity actor, .entity weaponentity)
46{
47 W_Rifle_FireBullet(thiswep, weaponentity, thiswep.m_id, SND_RIFLE_FIRE, actor, true);
48}
49
50void W_Rifle_Attack2(Weapon thiswep, entity actor, .entity weaponentity)
51{
52 W_Rifle_FireBullet(thiswep, weaponentity, thiswep.m_id | HITTYPE_SECONDARY, SND_RIFLE_FIRE2, actor, false);
53}
54
55.void(Weapon thiswep, entity actor, .entity weaponentity) rifle_bullethail_attackfunc;
59void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponentity, int fire)
60{
61 Weapon sw = actor.(weaponentity).m_switchweapon; // make it not detect weapon changes as reason to abort firing
62 float af = ATTACK_FINISHED(actor, weaponentity);
63 actor.(weaponentity).m_switchweapon = actor.(weaponentity).m_weapon;
64 ATTACK_FINISHED(actor, weaponentity) = time;
65 float r = weapon_prepareattack(thiswep, actor, weaponentity, actor.rifle_bullethail_frame == WFRAME_FIRE2, actor.rifle_bullethail_refire);
66 if (actor.(weaponentity).m_switchweapon == actor.(weaponentity).m_weapon)
67 actor.(weaponentity).m_switchweapon = sw;
68 if (r)
69 {
70 actor.rifle_bullethail_attackfunc(thiswep, actor, weaponentity);
71 weapon_thinkf(actor, weaponentity, actor.rifle_bullethail_frame, actor.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue);
72 }
73 else
74 ATTACK_FINISHED(actor, weaponentity) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
75}
76
77void W_Rifle_BulletHail(Weapon thiswep, entity actor, .entity weaponentity, float mode, void(Weapon thiswep, entity actor, .entity weaponentity) AttackFunc, WFRAME fr, float animtime, float refire)
78{
79 // if we get here, we have at least one bullet to fire
80 AttackFunc(thiswep, actor, weaponentity);
81 if (mode)
82 {
83 // continue hail
84 actor.rifle_bullethail_attackfunc = AttackFunc;
85 actor.rifle_bullethail_frame = fr;
86 actor.rifle_bullethail_animtime = animtime;
87 actor.rifle_bullethail_refire = refire;
88 weapon_thinkf(actor, weaponentity, fr, animtime, W_Rifle_BulletHail_Continue);
89 }
90 else // just one shot
91 weapon_thinkf(actor, weaponentity, fr, animtime, w_ready);
92}
93
94.bool bot_secondary_riflemooth; // whatever a mooth is
95
96METHOD(Rifle, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
97{
98 PHYS_INPUT_BUTTON_ATCK(actor) = false;
99 PHYS_INPUT_BUTTON_ATCK2(actor) = false;
100 if (vdist(actor.origin - actor.enemy.origin, >, 1000))
101 actor.bot_secondary_riflemooth = false;
102 if (!actor.bot_secondary_riflemooth)
103 {
104 if (bot_aim(actor, weaponentity, 1000000, 0, 0.001, false, true))
105 {
106 PHYS_INPUT_BUTTON_ATCK(actor) = true;
107 if (random() < 0.01)
108 actor.bot_secondary_riflemooth = true;
109 }
110 }
111 else
112 {
113 if (bot_aim(actor, weaponentity, 1000000, 0, 0.001, false, true))
114 {
115 PHYS_INPUT_BUTTON_ATCK2(actor) = true;
116 if (random() < 0.03)
117 actor.bot_secondary_riflemooth = false;
118 }
119 }
120}
121
122
123METHOD(Rifle, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
124{
125 if (autocvar_g_balance_rifle_reload_ammo && actor.(weaponentity).clip_load < min(WEP_CVAR_PRI(WEP_RIFLE, ammo), WEP_CVAR_SEC(WEP_RIFLE, ammo)))
126 { // forced reload
127 thiswep.wr_reload(thiswep, actor, weaponentity);
128 return;
129 }
130
131 actor.(weaponentity).rifle_accumulator = bound(time - WEP_CVAR(WEP_RIFLE, bursttime), actor.(weaponentity).rifle_accumulator, time);
132 if (fire & 1)
133 if (weapon_prepareattack_check(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(WEP_RIFLE, refire))
134 && time >= actor.(weaponentity).rifle_accumulator + WEP_CVAR_PRI(WEP_RIFLE, burstcost))
135 {
136 weapon_prepareattack_do(actor, weaponentity, false, WEP_CVAR_PRI(WEP_RIFLE, refire));
137 W_Rifle_BulletHail(thiswep, actor, weaponentity, WEP_CVAR_PRI(WEP_RIFLE, bullethail), W_Rifle_Attack, WFRAME_FIRE1, WEP_CVAR_PRI(WEP_RIFLE, animtime), WEP_CVAR_PRI(WEP_RIFLE, refire));
138 actor.(weaponentity).rifle_accumulator += WEP_CVAR_PRI(WEP_RIFLE, burstcost);
139 }
140 if ((fire & 2) && WEP_CVAR(WEP_RIFLE, secondary))
141 {
142 if (WEP_CVAR_SEC(WEP_RIFLE, reload))
143 thiswep.wr_reload(thiswep, actor, weaponentity);
144 else if (weapon_prepareattack_check(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(WEP_RIFLE, refire))
145 && time >= actor.(weaponentity).rifle_accumulator + WEP_CVAR_SEC(WEP_RIFLE, burstcost))
146 {
147 weapon_prepareattack_do(actor, weaponentity, true, WEP_CVAR_SEC(WEP_RIFLE, refire));
148 W_Rifle_BulletHail(thiswep, actor, weaponentity, WEP_CVAR_SEC(WEP_RIFLE, bullethail), W_Rifle_Attack2, WFRAME_FIRE2, WEP_CVAR_SEC(WEP_RIFLE, animtime), WEP_CVAR_PRI(WEP_RIFLE, refire));
149 actor.(weaponentity).rifle_accumulator += WEP_CVAR_SEC(WEP_RIFLE, burstcost);
150 }
151 }
152}
153
154METHOD(Rifle, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
155{
156 float ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(WEP_RIFLE, ammo);
157 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(WEP_RIFLE, ammo);
158 return ammo_amount;
159}
160
161METHOD(Rifle, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
162{
163 float ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(WEP_RIFLE, ammo);
164 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(WEP_RIFLE, ammo);
165 return ammo_amount;
166}
167
168METHOD(Rifle, wr_resetplayer, void(entity thiswep, entity actor))
169{
170 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
171 {
172 .entity weaponentity = weaponentities[slot];
173 actor.(weaponentity).rifle_accumulator = time - WEP_CVAR(WEP_RIFLE, bursttime);
174 }
175}
176
177METHOD(Rifle, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
178{
179 W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(WEP_RIFLE, ammo), WEP_CVAR_SEC(WEP_RIFLE, ammo)), SND_RELOAD);
180}
181
182METHOD(Rifle, wr_suicidemessage, Notification(entity thiswep))
183{
184 return WEAPON_THINKING_WITH_PORTALS;
185}
186
187METHOD(Rifle, wr_killmessage, Notification(entity thiswep))
188{
190 {
192 return WEAPON_RIFLE_MURDER_HAIL_PIERCING;
193 else
194 return WEAPON_RIFLE_MURDER_HAIL;
195 }
196 else
197 {
199 return WEAPON_RIFLE_MURDER_PIERCING;
200 else
201 return WEAPON_RIFLE_MURDER;
202 }
203}
204
205METHOD(Rifle, wr_zoom, bool(entity thiswep, entity actor))
206{
207 return PHYS_INPUT_BUTTON_ATCK2(actor) && WEP_CVAR(WEP_RIFLE, secondary) == 0;
208}
209
210#endif // SVQC
211#ifdef CSQC
212
213METHOD(Rifle, wr_impacteffect, void(entity thiswep, entity actor))
214{
215 vector org2 = w_org + w_backoff * 2;
216 pointparticles(EFFECT_RIFLE_IMPACT, org2, w_backoff * 1000, 1);
217 if (!w_issilent)
219}
220
221METHOD(Rifle, wr_init, void(entity thiswep))
222{
224 precache_pic(thiswep.w_reticle);
225}
226
227METHOD(Rifle, wr_zoom, bool(entity thiswep, entity actor))
228{
230 return true;
231 else // no weapon specific image for this weapon
232 return false;
233}
234
235METHOD(Rifle, wr_zoomdir, bool(entity thiswep))
236{
237 return button_attack2 && !WEP_CVAR(WEP_RIFLE, secondary);
238}
239
240#endif // CSQC
241#ifdef MENUQC
243#include "vortex.qh"
244
245METHOD(Rifle, describe, string(Rifle this))
246{
247 TC(Rifle, this);
249 PAR(_("The %s fires bullets that traverse the map instantaneously and deal a significant chunk of damage on impact."), COLORED_NAME(this));
250 PAR(_("The secondary fire shoots a few less powerful bullets at once with a bit of scatter."));
251 PAR(_("It consumes %s ammo for each bullet shot."), COLORED_NAME(ITEM_Bullets)); // shared string
252 PAR(_("Unlike the %s, the secondary fire doesn't zoom, so %s needs to be used manually with the %s. "
253 "Also, it needs to be reloaded after its magazine is emptied."), COLORED_NAME(WEP_VORTEX), strcat("^3", _("zoom"), "^7"), COLORED_NAME(this));
254 PAR(_("Similar to the %s, the %s can be used at any range, but it stands out at long ranges."), COLORED_NAME(WEP_VORTEX), COLORED_NAME(this));
255 PAR(W_Guide_Keybinds(this));
256 PAR(W_Guide_DPS_secondaryMultishot(this.netname, "primary", "secondary", "secondary_shots", "", false));
257 return PAGE_TEXT;
258}
259
260#endif // MENUQC
bool bot_aim(entity this,.entity weaponentity, float shotspeed, float shotspeedupward, float maxshottime, float applygravity, bool shot_accurate)
float dmg
Definition breakable.qc:12
void SpawnCasing(vector vel, vector ang, int casingtype, entity casingowner,.entity weaponentity)
Definition casings.qc:17
int autocvar_g_casings
Definition casings.qh:17
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.
Definition rifle.qh:12
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:42
int m_id
Definition weapon.qh:43
string netname
Definition powerups.qc:20
bool button_zoom
Definition main.qh:113
bool button_attack2
Definition main.qh:116
#define COLORED_NAME(this)
Definition color.qh:195
#define PHYS_INPUT_BUTTON_ZOOMSCRIPT(s)
Definition player.qh:163
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition player.qh:152
#define PHYS_INPUT_BUTTON_ZOOM(s)
Definition player.qh:155
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition player.qh:154
bool autocvar_cl_reticle_weapon
Definition crosshair.qh:5
bool autocvar_cl_reticle
Definition crosshair.qh:3
vector v_up
float time
vector v_right
vector v_forward
const float ATTN_NORM
vector w_org
int w_deathtype
vector w_backoff
float w_issilent
const int HITTYPE_BOUNCE
Definition all.qh:31
const int HITTYPE_SECONDARY
Definition all.qh:29
#define pointparticles(effect, org, vel, howmany)
Definition effect.qh:7
#define TC(T, sym)
Definition _all.inc:82
float bound(float min, float value, float max)
float random(void)
string precache_pic(string name,...)
vector vectoangles(vector v)
float min(float f,...)
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
entity Notification
always last
Definition all.qh:81
#define METHOD(cname, name, prototype)
Definition oo.qh:269
#define makevectors
Definition post.qh:21
WFRAME rifle_bullethail_frame
Definition rifle.qc:56
void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor,.entity weaponentity, int fire)
Definition rifle.qc:59
void W_Rifle_Attack2(Weapon thiswep, entity actor,.entity weaponentity)
Definition rifle.qc:50
float rifle_bullethail_refire
Definition rifle.qc:58
bool bot_secondary_riflemooth
Definition rifle.qc:94
float rifle_bullethail_animtime
Definition rifle.qc:57
void W_Rifle_BulletHail(Weapon thiswep, entity actor,.entity weaponentity, float mode, void(Weapon thiswep, entity actor,.entity weaponentity) AttackFunc, WFRAME fr, float animtime, float refire)
Definition rifle.qc:77
void W_Rifle_FireBullet(Weapon thiswep,.entity weaponentity, int deathtype, Sound pSound, entity actor, bool is_primary)
Definition rifle.qc:5
void W_Rifle_Attack(Weapon thiswep, entity actor,.entity weaponentity)
Definition rifle.qc:45
float rifle_accumulator
Definition rifle.qh:78
vector
Definition self.qh:92
const float VOL_BASE
Definition sound.qh:36
const int CH_SHOTS
Definition sound.qh:14
const int CH_WEAPON_A
Definition sound.qh:7
#define sound(e, c, s, v, a)
Definition sound.qh:52
Sound SND_RIC_RANDOM()
Definition all.inc:33
#define PAGE_TEXT
Definition string.qh:642
#define PAR(...)
Adds an individually translatable paragraph to PAGE_TEXT without having to deal with strcat and sprin...
Definition string.qh:648
#define PAGE_TEXT_INIT()
Definition string.qh:641
float ammo
Definition sv_turrets.qh:43
void fireBullet_falloff(entity this,.entity weaponentity, vector start, vector dir, float spread, float max_solid_penetration, float damage, float falloff_halflife, float falloff_mindist, float falloff_maxdist, float headshot_multiplier, float force, float falloff_forcehalflife, float dtype, entity tracer_effect, bool do_antilag)
Definition tracing.qc:356
vector w_shotdir
Definition tracing.qh:20
vector w_shotorg
Definition tracing.qh:19
#define W_SetupShot(ent, wepent, antilag, recoil, snd, chan, maxdamage, deathtype)
Definition tracing.qh:34
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8
float zoomscript_caught
Definition view.qh:121
string W_Guide_Keybinds(Weapon wep)
Definition all.qc:824
string W_Guide_DPS_secondaryMultishot(string name, string pri, string sec, string shots, string refire2, bool sec_variable)
Definition all.qc:955
void W_MuzzleFlash(Weapon thiswep, entity actor,.entity weaponentity, vector shotorg, vector shotdir)
Definition all.qc:715
#define WEP_CVAR_PRI(wep, name)
Definition all.qh:338
#define WEP_CVAR_BOTH(wep, isprimary, name)
Definition all.qh:340
#define WEP_CVAR(wep, name)
Definition all.qh:337
#define WEP_CVAR_SEC(wep, name)
Definition all.qh:339
const int MAX_WEAPONSLOTS
Definition weapon.qh:16
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use,.entity weaponentity)
void W_Reload(entity actor,.entity weaponentity, float sent_ammo_min, Sound sent_sound)
void weapon_thinkf(entity actor,.entity weaponentity, WFRAME fr, float t, void(Weapon thiswep, entity actor,.entity weaponentity, int fire) func)
bool weapon_prepareattack(Weapon thiswep, entity actor,.entity weaponentity, bool secondary, float attacktime)
void w_ready(Weapon thiswep, entity actor,.entity weaponentity, int fire)
void weapon_prepareattack_do(entity actor,.entity weaponentity, bool secondary, float attacktime)
bool weapon_prepareattack_check(Weapon thiswep, entity actor,.entity weaponentity, bool secondary, float attacktime)
#define ATTACK_FINISHED(ent, w)
float weapon_load[REGISTRY_MAX(Weapons)]
Weapon m_switchweapon
Definition wepent.qh:25