]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/weapon/blaster.qc
Switch to using the WEP_CVAR* macros I added in the previous commit as they are more...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / weapon / blaster.qc
1 #include "blaster.qh"
2
3 #ifdef SVQC
4
5 void W_Blaster_Touch(entity this, entity toucher)
6 {
7         PROJECTILE_TOUCH(this, toucher);
8
9         this.event_damage = func_null;
10         bool isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY);
11
12         RadiusDamageForSource(
13                 this,
14                 (this.origin + (this.mins + this.maxs) * 0.5),
15                 this.velocity,
16                 this.realowner,
17                 WEP_CVAR_BOTH(this.real_weapon, isprimary, damage),
18                 WEP_CVAR_BOTH(this.real_weapon, isprimary, edgedamage),
19                 WEP_CVAR_BOTH(this.real_weapon, isprimary, radius),
20                 NULL,
21                 NULL,
22                 false,
23                 WEP_CVAR_BOTH(this.real_weapon, isprimary, force),
24                 WEP_CVAR_BOTH(this.real_weapon, isprimary, force_zscale),
25                 this.projectiledeathtype,
26                 this.weaponentity_fld,
27                 toucher
28         );
29
30         delete(this);
31 }
32
33 void W_Blaster_Think(entity this)
34 {
35         set_movetype(this, MOVETYPE_FLY);
36         setthink(this, SUB_Remove);
37         bool isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY);
38         this.nextthink = time + WEP_CVAR_BOTH(WEP_BLASTER, isprimary, lifetime);
39         CSQCProjectile(this, true, PROJECTILE_BLASTER, true);
40 }
41
42 void W_Blaster_Attack(
43         entity actor,
44         entity real_wpn,
45         .entity weaponentity,
46         float atk_deathtype)
47 {
48         bool isprimary = !(atk_deathtype & HITTYPE_SECONDARY);
49         float atk_shotangle = WEP_CVAR_BOTH(WEP_BLASTER, isprimary, shotangle);
50         float atk_damage = WEP_CVAR_BOTH(WEP_BLASTER, isprimary, damage);
51         vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD);
52
53         W_SetupShot_Dir(actor, weaponentity, s_forward, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, atk_damage, atk_deathtype);
54         W_MuzzleFlash(WEP_BLASTER, actor, weaponentity, w_shotorg, w_shotdir);
55
56         entity missile = new(blasterbolt);
57         missile.real_weapon = real_wpn;
58         missile.owner = missile.realowner = actor;
59         missile.bot_dodge = true;
60         missile.bot_dodgerating = atk_damage;
61         PROJECTILE_MAKETRIGGER(missile);
62
63         setorigin(missile, w_shotorg);
64         setsize(missile, '0 0 0', '0 0 0');
65
66         float atk_speed = WEP_CVAR_BOTH(WEP_BLASTER, isprimary, speed);
67         float atk_spread = WEP_CVAR_BOTH(WEP_BLASTER, isprimary, spread);
68         W_SetupProjVelocity_Explicit(missile, w_shotdir, v_up, atk_speed, 0, 0, atk_spread, false);
69
70         missile.angles = vectoangles(missile.velocity);
71
72         //missile.glow_color = 250; // 244, 250
73         //missile.glow_size = 120;
74
75         settouch(missile, W_Blaster_Touch);
76         missile.flags = FL_PROJECTILE;
77         IL_PUSH(g_projectiles, missile);
78         IL_PUSH(g_bot_dodge, missile);
79         missile.missile_flags = MIF_SPLASH;
80         missile.projectiledeathtype = atk_deathtype;
81         missile.weaponentity_fld = weaponentity;
82         setthink(missile, W_Blaster_Think);
83         missile.nextthink = time + WEP_CVAR_BOTH(WEP_BLASTER, isprimary, delay);
84
85         MUTATOR_CALLHOOK(EditProjectile, actor, missile);
86
87         if (time >= missile.nextthink)
88         {
89                 getthink(missile)(missile);
90         }
91 }
92
93 METHOD(Blaster, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
94 {
95     if(WEP_CVAR(WEP_BLASTER, secondary))
96     {
97         if((random() * (WEP_CVAR_PRI(WEP_BLASTER, damage) + WEP_CVAR_SEC(WEP_BLASTER, damage))) > WEP_CVAR_PRI(WEP_BLASTER, damage))
98             { PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR_SEC(WEP_BLASTER, speed), 0, WEP_CVAR_SEC(WEP_BLASTER, lifetime), false, true); }
99         else
100             { PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(WEP_BLASTER, speed), 0, WEP_CVAR_PRI(WEP_BLASTER, lifetime), false, true); }
101     }
102     else
103         { PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(WEP_BLASTER, speed), 0, WEP_CVAR_PRI(WEP_BLASTER, lifetime), false, true); }
104 }
105
106 METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, .entity weaponentity, int fire))
107 {
108     if(fire & 1)
109     {
110         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(WEP_BLASTER, refire)))
111         {
112             W_Blaster_Attack(actor, thiswep, weaponentity, WEP_BLASTER.m_id);
113             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(WEP_BLASTER, animtime), w_ready);
114         }
115     }
116     else if(fire & 2)
117     {
118         switch(WEP_CVAR(WEP_BLASTER, secondary))
119         {
120             case 0: // switch to last used weapon
121             {
122                 if(actor.(weaponentity).m_switchweapon == WEP_BLASTER) // don't do this if already switching
123                     W_LastWeapon(actor, weaponentity);
124                 break;
125             }
126
127             case 1: // normal projectile secondary
128             {
129                 if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(WEP_BLASTER, refire)))
130                 {
131                     W_Blaster_Attack(actor, thiswep, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
132                     weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(WEP_BLASTER, animtime), w_ready);
133                 }
134
135                 break;
136             }
137         }
138     }
139 }
140
141 METHOD(Blaster, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
142 {
143     return true; // infinite ammo
144 }
145
146 METHOD(Blaster, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
147 {
148     return true; // blaster has infinite ammo
149 }
150
151 METHOD(Blaster, wr_suicidemessage, Notification(entity thiswep))
152 {
153     return WEAPON_BLASTER_SUICIDE;
154 }
155
156 METHOD(Blaster, wr_killmessage, Notification(entity thiswep))
157 {
158     return WEAPON_BLASTER_MURDER;
159 }
160
161 METHOD(OffhandBlaster, offhand_think, void(OffhandBlaster this, entity actor, bool key_pressed))
162 {
163         if (!key_pressed || (time < actor.jump_interval))
164         {
165                 return;
166         }
167         actor.jump_interval = time + WEP_CVAR_SEC(WEP_BLASTER, refire) * W_WeaponRateFactor(actor);
168         .entity weaponentity = weaponentities[1];
169         makevectors(actor.v_angle);
170         W_Blaster_Attack(actor, this, weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY);
171 }
172
173 #endif
174 #ifdef CSQC
175
176 METHOD(Blaster, wr_impacteffect, void(entity thiswep, entity actor))
177 {
178     vector org2 = w_org + w_backoff * 2;
179     pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1);
180     if(!w_issilent) { sound(actor, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); }
181 }
182
183 #endif