df54c6b921360c7ad2bcd5045c11cd67bbf8004a
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / weapon / blaster.qc
1 #ifndef IMPLEMENTATION
2 CLASS(Blaster, Weapon)
3 /* ammotype  */ //ATTRIB(Blaster, ammo_field, .int, ammo_none)
4 /* impulse   */ ATTRIB(Blaster, impulse, int, 1)
5 /* flags     */ ATTRIB(Blaster, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
6 /* rating    */ ATTRIB(Blaster, bot_pickupbasevalue, float, 0);
7 /* color     */ ATTRIB(Blaster, wpcolor, vector, '1 0.5 0.5');
8 /* modelname */ ATTRIB(Blaster, mdl, string, "laser");
9 #ifndef MENUQC
10 /* model     */ ATTRIB(Blaster, m_model, Model, MDL_BLASTER_ITEM);
11 #endif
12 /* crosshair */ ATTRIB(Blaster, w_crosshair, string, "gfx/crosshairlaser");
13 /* crosshair */ ATTRIB(Blaster, w_crosshair_size, float, 0.5);
14 /* wepimg    */ ATTRIB(Blaster, model2, string, "weaponlaser");
15 /* refname   */ ATTRIB(Blaster, netname, string, "blaster");
16 /* wepname   */ ATTRIB(Blaster, message, string, _("Blaster"));
17 ENDCLASS(Blaster)
18 REGISTER_WEAPON(BLASTER, NEW(Blaster));
19
20 #define BLASTER_SETTINGS(w_cvar,w_prop) BLASTER_SETTINGS_LIST(w_cvar, w_prop, BLASTER, blaster)
21 #define BLASTER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
22         w_cvar(id, sn, BOTH, animtime) \
23         w_cvar(id, sn, BOTH, damage) \
24         w_cvar(id, sn, BOTH, delay) \
25         w_cvar(id, sn, BOTH, edgedamage) \
26         w_cvar(id, sn, BOTH, force) \
27         w_cvar(id, sn, BOTH, force_zscale) \
28         w_cvar(id, sn, BOTH, lifetime) \
29         w_cvar(id, sn, BOTH, radius) \
30         w_cvar(id, sn, BOTH, refire) \
31         w_cvar(id, sn, BOTH, shotangle) \
32         w_cvar(id, sn, BOTH, speed) \
33         w_cvar(id, sn, BOTH, spread) \
34         w_cvar(id, sn, NONE, secondary) \
35         w_prop(id, sn, float,  switchdelay_raise, switchdelay_raise) \
36         w_prop(id, sn, float,  switchdelay_drop, switchdelay_drop) \
37         w_prop(id, sn, string, weaponreplace, weaponreplace) \
38         w_prop(id, sn, float,  weaponstart, weaponstart) \
39         w_prop(id, sn, float,  weaponstartoverride, weaponstartoverride) \
40         w_prop(id, sn, float,  weaponthrowable, weaponthrowable)
41
42 #ifdef SVQC
43 BLASTER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
44 .float blaster_damage;
45 .float blaster_edgedamage;
46 .float blaster_radius;
47 .float blaster_force;
48 .float blaster_lifetime;
49 #endif
50 #endif
51 #ifdef IMPLEMENTATION
52 #ifdef SVQC
53 void spawnfunc_weapon_blaster(void) { weapon_defaultspawnfunc(WEP_BLASTER.m_id); }
54 void spawnfunc_weapon_laser(void) { spawnfunc_weapon_blaster(); }
55
56 void W_Blaster_Touch(void)
57 {SELFPARAM();
58         PROJECTILE_TOUCH;
59
60         self.event_damage = func_null;
61
62         RadiusDamage(
63                 self,
64                 self.realowner,
65                 self.blaster_damage,
66                 self.blaster_edgedamage,
67                 self.blaster_radius,
68                 world,
69                 world,
70                 self.blaster_force,
71                 self.projectiledeathtype,
72                 other
73         );
74
75         remove(self);
76 }
77
78 void W_Blaster_Think(void)
79 {SELFPARAM();
80         self.movetype = MOVETYPE_FLY;
81         self.think = SUB_Remove;
82         self.nextthink = time + self.blaster_lifetime;
83         CSQCProjectile(self, true, PROJECTILE_BLASTER, true);
84 }
85
86 void W_Blaster_Attack(
87         float atk_deathtype,
88         float atk_shotangle,
89         float atk_damage,
90         float atk_edgedamage,
91         float atk_radius,
92         float atk_force,
93         float atk_speed,
94         float atk_spread,
95         float atk_delay,
96         float atk_lifetime)
97 {SELFPARAM();
98         vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD);
99
100         W_SetupShot_Dir(self, s_forward, false, 3, SND(LASERGUN_FIRE), CH_WEAPON_B, atk_damage);
101         Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
102
103         entity missile = spawn();
104         missile.owner = missile.realowner = self;
105         missile.classname = "blasterbolt";
106         missile.bot_dodge = true;
107         missile.bot_dodgerating = atk_damage;
108         PROJECTILE_MAKETRIGGER(missile);
109
110         missile.blaster_damage = atk_damage;
111         missile.blaster_edgedamage = atk_edgedamage;
112         missile.blaster_radius = atk_radius;
113         missile.blaster_force = atk_force;
114         missile.blaster_lifetime = atk_lifetime;
115
116         setorigin(missile, w_shotorg);
117         setsize(missile, '0 0 0', '0 0 0');
118
119         W_SetupProjVelocity_Explicit(
120                 missile,
121                 w_shotdir,
122                 v_up,
123                 atk_speed,
124                 0,
125                 0,
126                 atk_spread,
127                 false
128         );
129
130         missile.angles = vectoangles(missile.velocity);
131
132         //missile.glow_color = 250; // 244, 250
133         //missile.glow_size = 120;
134
135         missile.touch = W_Blaster_Touch;
136         missile.flags = FL_PROJECTILE;
137         missile.missile_flags = MIF_SPLASH;
138         missile.projectiledeathtype = atk_deathtype;
139         missile.think = W_Blaster_Think;
140         missile.nextthink = time + atk_delay;
141
142         MUTATOR_CALLHOOK(EditProjectile, self, missile);
143
144         if(time >= missile.nextthink)
145         {
146                 WITH(entity, self, missile, missile.think());
147         }
148 }
149
150                 METHOD(Blaster, wr_aim, bool(entity thiswep))
151                 {
152                         if(WEP_CVAR(blaster, secondary))
153                         {
154                                 if((random() * (WEP_CVAR_PRI(blaster, damage) + WEP_CVAR_SEC(blaster, damage))) > WEP_CVAR_PRI(blaster, damage))
155                                         { self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(blaster, speed), 0, WEP_CVAR_SEC(blaster, lifetime), false); }
156                                 else
157                                         { self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), false); }
158                         }
159                         else
160                                 { self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), false); }
161
162                         return true;
163                 }
164
165                 METHOD(Blaster, wr_think, bool(entity thiswep, bool fire1, bool fire2))
166                 {
167                         if(fire1)
168                         {
169                                 if(weapon_prepareattack(false, WEP_CVAR_PRI(blaster, refire)))
170                                 {
171                                         W_Blaster_Attack(
172                                                 WEP_BLASTER.m_id,
173                                                 WEP_CVAR_PRI(blaster, shotangle),
174                                                 WEP_CVAR_PRI(blaster, damage),
175                                                 WEP_CVAR_PRI(blaster, edgedamage),
176                                                 WEP_CVAR_PRI(blaster, radius),
177                                                 WEP_CVAR_PRI(blaster, force),
178                                                 WEP_CVAR_PRI(blaster, speed),
179                                                 WEP_CVAR_PRI(blaster, spread),
180                                                 WEP_CVAR_PRI(blaster, delay),
181                                                 WEP_CVAR_PRI(blaster, lifetime)
182                                         );
183                                         weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(blaster, animtime), w_ready);
184                                 }
185                         }
186                         else if(fire2)
187                         {
188                                 switch(WEP_CVAR(blaster, secondary))
189                                 {
190                                         case 0: // switch to last used weapon
191                                         {
192                                                 if(self.switchweapon == WEP_BLASTER.m_id) // don't do this if already switching
193                                                         W_LastWeapon();
194                                                 break;
195                                         }
196
197                                         case 1: // normal projectile secondary
198                                         {
199                                                 if(weapon_prepareattack(true, WEP_CVAR_SEC(blaster, refire)))
200                                                 {
201                                                         W_Blaster_Attack(
202                                                                 WEP_BLASTER.m_id | HITTYPE_SECONDARY,
203                                                                 WEP_CVAR_SEC(blaster, shotangle),
204                                                                 WEP_CVAR_SEC(blaster, damage),
205                                                                 WEP_CVAR_SEC(blaster, edgedamage),
206                                                                 WEP_CVAR_SEC(blaster, radius),
207                                                                 WEP_CVAR_SEC(blaster, force),
208                                                                 WEP_CVAR_SEC(blaster, speed),
209                                                                 WEP_CVAR_SEC(blaster, spread),
210                                                                 WEP_CVAR_SEC(blaster, delay),
211                                                                 WEP_CVAR_SEC(blaster, lifetime)
212                                                         );
213                                                         weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready);
214                                                 }
215
216                                                 break;
217                                         }
218                                 }
219                         }
220                         return true;
221                 }
222
223                 METHOD(Blaster, wr_init, bool(entity thiswep))
224                 {
225                         BLASTER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
226                         return true;
227                 }
228
229                 METHOD(Blaster, wr_setup, bool(entity thiswep))
230                 {
231                         self.ammo_field = ammo_none;
232                         return true;
233                 }
234
235                 METHOD(Blaster, wr_checkammo1, bool(entity thiswep))
236                 {
237                         return true; // infinite ammo
238                 }
239
240                 METHOD(Blaster, wr_checkammo2, bool(entity thiswep))
241                 {
242                         return true; // blaster has infinite ammo
243                 }
244
245                 METHOD(Blaster, wr_config, bool(entity thiswep))
246                 {
247                         BLASTER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
248                         return true;
249                 }
250
251                 METHOD(Blaster, wr_suicidemessage, bool(entity thiswep))
252                 {
253                         return WEAPON_BLASTER_SUICIDE;
254                 }
255
256                 METHOD(Blaster, wr_killmessage, bool(entity thiswep))
257                 {
258                         return WEAPON_BLASTER_MURDER;
259                 }
260
261 #endif
262 #ifdef CSQC
263
264                 METHOD(Blaster, wr_impacteffect, bool(entity thiswep))
265                 {
266                         vector org2;
267                         org2 = w_org + w_backoff * 6;
268                         pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1);
269                         if(!w_issilent) { sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); }
270                         return true;
271                 }
272
273                 METHOD(Blaster, wr_init, bool(entity thiswep))
274                 {
275                         return true;
276                 }
277
278                 METHOD(Blaster, wr_zoomreticle, bool(entity thiswep))
279                 {
280                         // no weapon specific image for this weapon
281                         return false;
282                 }
283
284 #endif
285 #endif