Add a force_zscale setting to weapons that use the blaster secondary
[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
11         RadiusDamageForSource(
12                 this,
13                 (this.origin + (this.mins + this.maxs) * 0.5),
14                 this.velocity,
15                 this.realowner,
16                 this.blaster_damage,
17                 this.blaster_edgedamage,
18                 this.blaster_radius,
19                 NULL,
20                 NULL,
21                 false,
22                 this.blaster_force,
23                 this.blaster_force_zscale,
24                 this.projectiledeathtype,
25                 this.weaponentity_fld,
26                 toucher
27         );
28
29         delete(this);
30 }
31
32 void W_Blaster_Think(entity this)
33 {
34         set_movetype(this, MOVETYPE_FLY);
35         setthink(this, SUB_Remove);
36         this.nextthink = time + this.blaster_lifetime;
37         CSQCProjectile(this, true, PROJECTILE_BLASTER, true);
38 }
39
40 void W_Blaster_Attack(
41         entity actor,
42         .entity weaponentity,
43         float atk_deathtype,
44         float atk_shotangle,
45         float atk_damage,
46         float atk_edgedamage,
47         float atk_radius,
48         float atk_force,
49         float atk_force_zscale,
50         float atk_speed,
51         float atk_spread,
52         float atk_delay,
53         float atk_lifetime)
54 {
55         vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD);
56
57         W_SetupShot_Dir(actor, weaponentity, s_forward, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, atk_damage, atk_deathtype);
58         Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
59
60         entity missile = new(blasterbolt);
61         missile.owner = missile.realowner = actor;
62         missile.bot_dodge = true;
63         missile.bot_dodgerating = atk_damage;
64         PROJECTILE_MAKETRIGGER(missile);
65
66         missile.blaster_damage = atk_damage;
67         missile.blaster_edgedamage = atk_edgedamage;
68         missile.blaster_radius = atk_radius;
69         missile.blaster_force = atk_force;
70         missile.blaster_force_zscale = atk_force_zscale;
71         missile.blaster_lifetime = atk_lifetime;
72
73         setorigin(missile, w_shotorg);
74         setsize(missile, '0 0 0', '0 0 0');
75
76         W_SetupProjVelocity_Explicit(
77                 missile,
78                 w_shotdir,
79                 v_up,
80                 atk_speed,
81                 0,
82                 0,
83                 atk_spread,
84                 false
85         );
86
87         missile.angles = vectoangles(missile.velocity);
88
89         //missile.glow_color = 250; // 244, 250
90         //missile.glow_size = 120;
91
92         settouch(missile, W_Blaster_Touch);
93         missile.flags = FL_PROJECTILE;
94         IL_PUSH(g_projectiles, missile);
95         IL_PUSH(g_bot_dodge, missile);
96         missile.missile_flags = MIF_SPLASH;
97         missile.projectiledeathtype = atk_deathtype;
98         missile.weaponentity_fld = weaponentity;
99         setthink(missile, W_Blaster_Think);
100         missile.nextthink = time + atk_delay;
101
102         MUTATOR_CALLHOOK(EditProjectile, actor, missile);
103
104         if (time >= missile.nextthink)
105         {
106                 getthink(missile)(missile);
107         }
108 }
109
110 METHOD(Blaster, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
111 {
112     if(WEP_CVAR(blaster, secondary))
113     {
114         if((random() * (WEP_CVAR_PRI(blaster, damage) + WEP_CVAR_SEC(blaster, damage))) > WEP_CVAR_PRI(blaster, damage))
115             { PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR_SEC(blaster, speed), 0, WEP_CVAR_SEC(blaster, lifetime), false); }
116         else
117             { PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), false); }
118     }
119     else
120         { PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), false); }
121 }
122
123 METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, .entity weaponentity, int fire))
124 {
125     if(fire & 1)
126     {
127         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(blaster, refire)))
128         {
129             W_Blaster_Attack(
130                 actor,
131                 weaponentity,
132                 WEP_BLASTER.m_id,
133                 WEP_CVAR_PRI(blaster, shotangle),
134                 WEP_CVAR_PRI(blaster, damage),
135                 WEP_CVAR_PRI(blaster, edgedamage),
136                 WEP_CVAR_PRI(blaster, radius),
137                 WEP_CVAR_PRI(blaster, force),
138                 WEP_CVAR_PRI(blaster, force_zscale),
139                 WEP_CVAR_PRI(blaster, speed),
140                 WEP_CVAR_PRI(blaster, spread),
141                 WEP_CVAR_PRI(blaster, delay),
142                 WEP_CVAR_PRI(blaster, lifetime)
143             );
144             weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(blaster, animtime), w_ready);
145         }
146     }
147     else if(fire & 2)
148     {
149         switch(WEP_CVAR(blaster, secondary))
150         {
151             case 0: // switch to last used weapon
152             {
153                 if(actor.(weaponentity).m_switchweapon == WEP_BLASTER) // don't do this if already switching
154                     W_LastWeapon(actor, weaponentity);
155                 break;
156             }
157
158             case 1: // normal projectile secondary
159             {
160                 if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(blaster, refire)))
161                 {
162                     W_Blaster_Attack(
163                         actor,
164                         weaponentity,
165                         WEP_BLASTER.m_id | HITTYPE_SECONDARY,
166                         WEP_CVAR_SEC(blaster, shotangle),
167                         WEP_CVAR_SEC(blaster, damage),
168                         WEP_CVAR_SEC(blaster, edgedamage),
169                         WEP_CVAR_SEC(blaster, radius),
170                         WEP_CVAR_SEC(blaster, force),
171                         WEP_CVAR_SEC(blaster, force_zscale),
172                         WEP_CVAR_SEC(blaster, speed),
173                         WEP_CVAR_SEC(blaster, spread),
174                         WEP_CVAR_SEC(blaster, delay),
175                         WEP_CVAR_SEC(blaster, lifetime)
176                     );
177                     weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready);
178                 }
179
180                 break;
181             }
182         }
183     }
184 }
185
186 METHOD(Blaster, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
187 {
188     return true; // infinite ammo
189 }
190
191 METHOD(Blaster, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
192 {
193     return true; // blaster has infinite ammo
194 }
195
196 METHOD(Blaster, wr_suicidemessage, Notification(entity thiswep))
197 {
198     return WEAPON_BLASTER_SUICIDE;
199 }
200
201 METHOD(Blaster, wr_killmessage, Notification(entity thiswep))
202 {
203     return WEAPON_BLASTER_MURDER;
204 }
205
206 METHOD(OffhandBlaster, offhand_think, void(OffhandBlaster this, entity actor, bool key_pressed))
207 {
208         if (!key_pressed || (time < actor.jump_interval))
209         {
210                 return;
211         }
212         actor.jump_interval = time + WEP_CVAR_SEC(blaster, refire) * W_WeaponRateFactor(actor);
213         .entity weaponentity = weaponentities[1];
214         BLASTER_SECONDARY_ATTACK(blaster, actor, weaponentity);
215 }
216
217 #endif
218 #ifdef CSQC
219
220 METHOD(Blaster, wr_impacteffect, void(entity thiswep, entity actor))
221 {
222     vector org2;
223     org2 = w_org + w_backoff * 6;
224     pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1);
225     if(!w_issilent) { sound(actor, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); }
226 }
227
228 #endif