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