32310d033c1a48d9737913e6d7709b8c94dcb054
[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         entity actor,
88         float atk_deathtype,
89         float atk_shotangle,
90         float atk_damage,
91         float atk_edgedamage,
92         float atk_radius,
93         float atk_force,
94         float atk_speed,
95         float atk_spread,
96         float atk_delay,
97         float atk_lifetime)
98 {SELFPARAM();
99         vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD);
100
101         W_SetupShot_Dir(actor, s_forward, false, 3, SND(LASERGUN_FIRE), CH_WEAPON_B, atk_damage);
102         Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
103
104         entity missile = spawn();
105         missile.owner = missile.realowner = actor;
106         missile.classname = "blasterbolt";
107         missile.bot_dodge = true;
108         missile.bot_dodgerating = atk_damage;
109         PROJECTILE_MAKETRIGGER(missile);
110
111         missile.blaster_damage = atk_damage;
112         missile.blaster_edgedamage = atk_edgedamage;
113         missile.blaster_radius = atk_radius;
114         missile.blaster_force = atk_force;
115         missile.blaster_lifetime = atk_lifetime;
116
117         setorigin(missile, w_shotorg);
118         setsize(missile, '0 0 0', '0 0 0');
119
120         W_SetupProjVelocity_Explicit(
121                 missile,
122                 w_shotdir,
123                 v_up,
124                 atk_speed,
125                 0,
126                 0,
127                 atk_spread,
128                 false
129         );
130
131         missile.angles = vectoangles(missile.velocity);
132
133         //missile.glow_color = 250; // 244, 250
134         //missile.glow_size = 120;
135
136         missile.touch = W_Blaster_Touch;
137         missile.flags = FL_PROJECTILE;
138         missile.missile_flags = MIF_SPLASH;
139         missile.projectiledeathtype = atk_deathtype;
140         missile.think = W_Blaster_Think;
141         missile.nextthink = time + atk_delay;
142
143         MUTATOR_CALLHOOK(EditProjectile, actor, missile);
144
145         if (time >= missile.nextthink)
146         {
147                 WITH(entity, self, missile, missile.think());
148         }
149 }
150
151                 METHOD(Blaster, wr_aim, void(entity thiswep))
152                 {
153                         if(WEP_CVAR(blaster, secondary))
154                         {
155                                 if((random() * (WEP_CVAR_PRI(blaster, damage) + WEP_CVAR_SEC(blaster, damage))) > WEP_CVAR_PRI(blaster, damage))
156                                         { self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(blaster, speed), 0, WEP_CVAR_SEC(blaster, lifetime), false); }
157                                 else
158                                         { self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), false); }
159                         }
160                         else
161                                 { self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), false); }
162                 }
163
164                 METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, bool fire1, bool fire2))
165                 {
166                         if(fire1)
167                         {
168                                 if(weapon_prepareattack(actor, false, WEP_CVAR_PRI(blaster, refire)))
169                                 {
170                                         W_Blaster_Attack(
171                                                 actor,
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(actor, 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(actor.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(actor, true, WEP_CVAR_SEC(blaster, refire)))
200                                                 {
201                                                         W_Blaster_Attack(
202                                                                 actor,
203                                                                 WEP_BLASTER.m_id | HITTYPE_SECONDARY,
204                                                                 WEP_CVAR_SEC(blaster, shotangle),
205                                                                 WEP_CVAR_SEC(blaster, damage),
206                                                                 WEP_CVAR_SEC(blaster, edgedamage),
207                                                                 WEP_CVAR_SEC(blaster, radius),
208                                                                 WEP_CVAR_SEC(blaster, force),
209                                                                 WEP_CVAR_SEC(blaster, speed),
210                                                                 WEP_CVAR_SEC(blaster, spread),
211                                                                 WEP_CVAR_SEC(blaster, delay),
212                                                                 WEP_CVAR_SEC(blaster, lifetime)
213                                                         );
214                                                         weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready);
215                                                 }
216
217                                                 break;
218                                         }
219                                 }
220                         }
221                 }
222
223                 METHOD(Blaster, wr_init, void(entity thiswep))
224                 {
225                         BLASTER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
226                 }
227
228                 METHOD(Blaster, wr_setup, void(entity thiswep))
229                 {
230                         self.ammo_field = ammo_none;
231                 }
232
233                 METHOD(Blaster, wr_checkammo1, bool(entity thiswep))
234                 {
235                         return true; // infinite ammo
236                 }
237
238                 METHOD(Blaster, wr_checkammo2, bool(entity thiswep))
239                 {
240                         return true; // blaster has infinite ammo
241                 }
242
243                 METHOD(Blaster, wr_config, void(entity thiswep))
244                 {
245                         BLASTER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
246                 }
247
248                 METHOD(Blaster, wr_suicidemessage, int(entity thiswep))
249                 {
250                         return WEAPON_BLASTER_SUICIDE;
251                 }
252
253                 METHOD(Blaster, wr_killmessage, int(entity thiswep))
254                 {
255                         return WEAPON_BLASTER_MURDER;
256                 }
257
258 #endif
259 #ifdef CSQC
260
261                 METHOD(Blaster, wr_impacteffect, void(entity thiswep))
262                 {
263                         vector org2;
264                         org2 = w_org + w_backoff * 6;
265                         pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1);
266                         if(!w_issilent) { sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); }
267                 }
268
269 #endif
270 #endif