Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
hlac.qc
Go to the documentation of this file.
1#include "hlac.qh"
2
3#ifdef SVQC
4
6{
8
9 this.event_damage = func_null;
10
11 bool is_primary = !(this.projectiledeathtype & HITTYPE_SECONDARY);
12 RadiusDamage(this, this.realowner,
13 WEP_CVAR_BOTH(WEP_HLAC, is_primary, damage),
14 WEP_CVAR_BOTH(WEP_HLAC, is_primary, edgedamage),
15 WEP_CVAR_BOTH(WEP_HLAC, is_primary, radius),
16 NULL,
17 NULL,
18 WEP_CVAR_BOTH(WEP_HLAC, is_primary, force),
22 );
23
24 delete(this);
25}
26
27void W_HLAC_Attack(Weapon thiswep, entity actor, .entity weaponentity)
28{
29 W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(WEP_HLAC, ammo), weaponentity);
30
31 float spread = WEP_CVAR_PRI(WEP_HLAC, spread_min) + (WEP_CVAR_PRI(WEP_HLAC, spread_add) * actor.(weaponentity).misc_bulletcounter);
32 spread = min(spread, WEP_CVAR_PRI(WEP_HLAC, spread_max));
33 if (IS_DUCKED(actor) && IS_ONGROUND(actor))
34 spread *= WEP_CVAR_PRI(WEP_HLAC, spread_crouchmod);
35
36 W_SetupShot(actor, weaponentity, false, 3, SND_HLAC_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(WEP_HLAC, damage), thiswep.m_id);
37 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
39 {
40 actor.punchangle.x = random() - 0.5;
41 actor.punchangle.y = random() - 0.5;
42 }
43
44 entity missile = new(hlacbolt);
45 missile.owner = missile.realowner = actor;
46 missile.bot_dodge = true;
47
48 missile.bot_dodgerating = WEP_CVAR_PRI(WEP_HLAC, damage);
49
50 set_movetype(missile, MOVETYPE_FLY);
52
53 setorigin(missile, w_shotorg);
54 setsize(missile, '0 0 0', '0 0 0');
55
56 W_SetupProjVelocity_Basic(missile, WEP_CVAR_PRI(WEP_HLAC, speed), spread);
57 //missile.angles = vectoangles(missile.velocity); // csqc
58
59 settouch(missile, W_HLAC_Touch);
60 setthink(missile, SUB_Remove);
61
62 missile.nextthink = time + WEP_CVAR_PRI(WEP_HLAC, lifetime);
63
64 missile.flags = FL_PROJECTILE;
65 IL_PUSH(g_projectiles, missile);
66 IL_PUSH(g_bot_dodge, missile);
67 missile.projectiledeathtype = thiswep.m_id;
68 missile.weaponentity_fld = weaponentity;
69
70 CSQCProjectile(missile, true, PROJECTILE_HLAC, true);
71
72 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
73}
74
75void W_HLAC_Attack2(Weapon thiswep, entity actor, .entity weaponentity)
76{
77 float spread = WEP_CVAR_SEC(WEP_HLAC, spread);
78
79 if (IS_DUCKED(actor) && IS_ONGROUND(actor))
80 spread *= WEP_CVAR_SEC(WEP_HLAC, spread_crouchmod);
81
82 W_SetupShot(actor, weaponentity, false, 3, SND_HLAC_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(WEP_HLAC, damage) * WEP_CVAR_SEC(WEP_HLAC, shots), thiswep.m_id | HITTYPE_SECONDARY);
83 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
84 W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(WEP_HLAC, ammo), weaponentity);
85
86 entity missile;
87 for (int j = WEP_CVAR_SEC(WEP_HLAC, shots); j > 0; --j)
88 {
89 missile = new(hlacbolt);
90 missile.owner = missile.realowner = actor;
91 missile.bot_dodge = true;
92
93 missile.bot_dodgerating = WEP_CVAR_SEC(WEP_HLAC, damage);
94
95 set_movetype(missile, MOVETYPE_FLY);
97
98 setorigin(missile, w_shotorg);
99 setsize(missile, '0 0 0', '0 0 0');
100
101 W_SetupProjVelocity_Basic(missile, WEP_CVAR_SEC(WEP_HLAC, speed), spread);
102 //missile.angles = vectoangles(missile.velocity); // csqc
103
104 settouch(missile, W_HLAC_Touch);
105 setthink(missile, SUB_Remove);
106
107 missile.nextthink = time + WEP_CVAR_SEC(WEP_HLAC, lifetime);
108
109 missile.flags = FL_PROJECTILE;
110 IL_PUSH(g_projectiles, missile);
111 IL_PUSH(g_bot_dodge, missile);
112 missile.missile_flags = MIF_SPLASH;
113 missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
114 missile.weaponentity_fld = weaponentity;
115
116 CSQCProjectile(missile, true, PROJECTILE_HLAC, true);
117
118 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
119 }
120
122 {
123 actor.punchangle.x = random() - 0.5;
124 actor.punchangle.y = random() - 0.5;
125 }
126}
127
128// weapon frames
129void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire)
130{
131 if (actor.(weaponentity).m_weapon != actor.(weaponentity).m_switchweapon) // abort immediately if switching
132 {
133 w_ready(thiswep, actor, weaponentity, fire);
134 return;
135 }
136
137 if (PHYS_INPUT_BUTTON_ATCK(actor))
138 {
139 if (!thiswep.wr_checkammo1(thiswep, actor, weaponentity)
140 && !(actor.items & IT_UNLIMITED_AMMO))
141 {
142 W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
143 w_ready(thiswep, actor, weaponentity, fire);
144 return;
145 }
146
147 ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(WEP_HLAC, refire) * W_WeaponRateFactor(actor);
148 W_HLAC_Attack(thiswep, actor, weaponentity);
149 ++actor.(weaponentity).misc_bulletcounter;
150 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(WEP_HLAC, refire), W_HLAC_Attack_Frame);
151 }
152 else
153 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(WEP_HLAC, animtime), w_ready);
154}
155
156METHOD(HLAC, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
157{
158 PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(WEP_HLAC, speed), 0, WEP_CVAR_PRI(WEP_HLAC, lifetime), false, true);
159}
160
161METHOD(HLAC, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
162{
163 if (autocvar_g_balance_hlac_reload_ammo // forced reload
164 && actor.(weaponentity).clip_load < WEP_CVAR_PRI(WEP_HLAC, ammo)
165 && actor.(weaponentity).clip_load < WEP_CVAR_SEC(WEP_HLAC, ammo))
166 {
167 thiswep.wr_reload(thiswep, actor, weaponentity);
168 return;
169 }
170
171 if (fire & 1)
172 {
173 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(WEP_HLAC, refire)))
174 {
175 actor.(weaponentity).misc_bulletcounter = 0;
176 W_HLAC_Attack(thiswep, actor, weaponentity);
177 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(WEP_HLAC, refire), W_HLAC_Attack_Frame);
178 }
179 }
180 else if ((fire & 2) && WEP_CVAR(WEP_HLAC, secondary))
181 {
182 if (weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(WEP_HLAC, refire)))
183 {
184 W_HLAC_Attack2(thiswep, actor, weaponentity);
185 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(WEP_HLAC, animtime), w_ready);
186 }
187 }
188}
189
190METHOD(HLAC, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
191{
192 float ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(WEP_HLAC, ammo);
193 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(WEP_HLAC, ammo);
194 return ammo_amount;
195}
196
197METHOD(HLAC, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
198{
199 float ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(WEP_HLAC, ammo);
200 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(WEP_HLAC, ammo);
201 return ammo_amount;
202}
203
204METHOD(HLAC, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
205{
206 W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(WEP_HLAC, ammo), WEP_CVAR_SEC(WEP_HLAC, ammo)), SND_RELOAD);
207}
208
209METHOD(HLAC, wr_suicidemessage, Notification(entity thiswep))
210{
211 return WEAPON_HLAC_SUICIDE;
212}
213
214METHOD(HLAC, wr_killmessage, Notification(entity thiswep))
215{
216 return WEAPON_HLAC_MURDER;
217}
218
219#endif // SVQC
220#ifdef CSQC
221
222METHOD(HLAC, wr_impacteffect, void(entity thiswep, entity actor))
223{
224 // XONRELEASE TODO: xonotic-v0.9.0 post-release remove this caching & fallback, use GREEN_HLAC_IMPACT
225 // Do the same for .m_muzzleeffect in hlac.qh (use GREEN_HLAC_MUZZLEFLASH).
226 // See also common/effects/all.inc.
227 static entity eff;
228 if (!eff)
229 eff = (particleeffectnum(EFFECT_GREEN_HLAC_IMPACT) < 0)
230 ? EFFECT_BLASTER_IMPACT // compatible with Xonotic v0.8.6 or lower
231 : EFFECT_GREEN_HLAC_IMPACT;
232
233 vector org2 = w_org + w_backoff * 2;
234 pointparticles(eff, org2, w_backoff * 1000, 1);
235 if (!w_issilent)
236 sound(actor, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM);
237}
238
239#endif // CSQC
240#ifdef MENUQC
242#include "blaster.qh"
243
244METHOD(HLAC, describe, string(HLAC this))
245{
246 TC(HLAC, this);
248 PAR(_("The %s (or HLAC for short) fires lasers in quick succession. "
249 "The projectiles it fires are similar to those of the %s."), COLORED_NAME(this), COLORED_NAME(WEP_BLASTER));
250 PAR(_("The secondary fire shoots a burst of multiple randomly-scattered lasers at once."));
251 PAR(_("Unlike the %s, it consumes %s ammo for each laser shot, meaning that it cannot be used infinitely."), COLORED_NAME(WEP_BLASTER), COLORED_NAME(ITEM_Cells));
252 PAR(_("The %s works best in close ranges, but the primary fire is also useful in medium ranges."), COLORED_NAME(this));
253 PAR(_("A unique aspect of the %s is that the longer the primary fire is held, the more that the lasers will start to spread out. "
254 "This means releasing primary fire every now and then is important to restore accuracy. "
255 "Also, the %s has less spread when used while crouching."), COLORED_NAME(this), COLORED_NAME(this));
256 PAR(W_Guide_Keybinds(this));
257 PAR(W_Guide_DPS_secondaryMultishot(this.netname, "primary", "secondary", "secondary_shots", "", false));
258 return PAGE_TEXT;
259}
260
261#endif // MENUQC
bool bot_aim(entity this,.entity weaponentity, float shotspeed, float shotspeedupward, float maxshottime, float applygravity, bool shot_accurate)
IntrusiveList g_bot_dodge
Definition api.qh:150
#define MUTATOR_CALLHOOK(name,...)
Definition base.qh:186
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 hlac.qh:11
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:37
int m_id
Definition weapon.qh:38
virtual void wr_checkammo1()
(SERVER) checks ammo for weapon primary
Definition weapon.qh:95
string netname
Definition powerups.qc:20
float lifetime
Definition powerups.qc:23
#define COLORED_NAME(this)
Definition color.qh:189
const int IT_UNLIMITED_AMMO
Definition item.qh:23
float radius
Definition impulse.qh:11
#define IS_DUCKED(s)
Definition player.qh:212
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition player.qh:152
float W_WeaponRateFactor(entity this)
const int FL_PROJECTILE
Definition constants.qh:77
float time
const float ATTN_NORM
void CSQCProjectile(entity e, float clientanimate, int type, float docull)
float RadiusDamage(entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype,.entity weaponentity, entity directhitentity)
Definition damage.qc:937
void W_SwitchWeapon_Force(Player this, Weapon w,.entity weaponentity)
Definition selection.qc:237
vector w_org
vector w_backoff
float w_issilent
const int HITTYPE_SECONDARY
Definition all.qh:31
void SUB_Remove(entity this)
Remove entity.
Definition defer.qh:12
float speed
Definition dynlight.qc:9
#define pointparticles(effect, org, vel, howmany)
Definition effect.qh:6
#define particleeffectnum(e)
Definition effect.qh:3
void W_HLAC_Attack(Weapon thiswep, entity actor,.entity weaponentity)
Definition hlac.qc:27
void W_HLAC_Attack_Frame(Weapon thiswep, entity actor,.entity weaponentity, int fire)
Definition hlac.qc:129
void W_HLAC_Attack2(Weapon thiswep, entity actor,.entity weaponentity)
Definition hlac.qc:75
void W_HLAC_Touch(entity this, entity toucher)
Definition hlac.qc:5
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define TC(T, sym)
Definition _all.inc:82
float random(void)
float min(float f,...)
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
const int MOVETYPE_FLY
Definition movetypes.qh:138
#define IS_ONGROUND(s)
Definition movetypes.qh:16
var void func_null()
entity Notification
always last
Definition all.qh:86
#define METHOD(cname, name, prototype)
Definition oo.qh:274
#define NULL
Definition post.qh:14
const int PROJECTILE_HLAC
#define w_getbestweapon(ent, wepent)
Definition selection.qh:23
#define setthink(e, f)
vector
Definition self.qh:96
entity entity toucher
Definition self.qh:76
#define settouch(e, f)
Definition self.qh:77
const int MIF_SPLASH
Definition common.qh:58
int projectiledeathtype
Definition common.qh:33
#define PROJECTILE_TOUCH(e, t)
Definition common.qh:40
IntrusiveList g_projectiles
Definition common.qh:70
#define PROJECTILE_MAKETRIGGER(e)
Definition common.qh:46
float misc_bulletcounter
Definition common.qh:31
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
#define PAGE_TEXT
Definition string.qh:652
#define PAR(...)
Adds an individually translatable paragraph to PAGE_TEXT without having to deal with strcat and sprin...
Definition string.qh:658
#define PAGE_TEXT_INIT()
Definition string.qh:651
float ammo
Definition sv_turrets.qh:43
entity realowner
bool autocvar_g_norecoil
Definition tracing.qh:16
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 W_SetupProjVelocity_Basic(ent, pspeed, pspread)
Definition tracing.qh:51
string W_Guide_Keybinds(Weapon wep)
Definition all.qc:803
string W_Guide_DPS_secondaryMultishot(string name, string pri, string sec, string shots, string refire2, bool sec_variable)
Definition all.qc:934
void W_MuzzleFlash(Weapon thiswep, entity actor,.entity weaponentity, vector shotorg, vector shotdir)
Definition all.qc:694
#define WEP_CVAR_PRI(wep, name)
Definition all.qh:340
#define WEP_CVAR_BOTH(wep, isprimary, name)
Definition all.qh:342
#define WEP_CVAR(wep, name)
Definition all.qh:339
#define WEP_CVAR_SEC(wep, name)
Definition all.qh:341
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)
#define ATTACK_FINISHED(ent, w)
entity weaponentity_fld
float weapon_load[REGISTRY_MAX(Weapons)]