Merge branch 'master' into terencehill/music_player
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_nex.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(
3 /* WEP_##id  */ NEX,
4 /* function  */ w_nex,
5 /* ammotype  */ IT_CELLS,
6 /* impulse   */ 7,
7 /* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
8 /* rating    */ BOT_PICKUP_RATING_HIGH,
9 /* model     */ "nex",
10 /* shortname */ "nex",
11 /* fullname  */ _("Nex")
12 );
13 #else
14 #ifdef SVQC
15
16 void SendCSQCNexBeamParticle(float charge) {
17         vector v;
18         v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
19         WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
20         WriteByte(MSG_BROADCAST, TE_CSQC_NEXGUNBEAMPARTICLE);
21         WriteCoord(MSG_BROADCAST, w_shotorg_x);
22         WriteCoord(MSG_BROADCAST, w_shotorg_y);
23         WriteCoord(MSG_BROADCAST, w_shotorg_z);
24         WriteCoord(MSG_BROADCAST, v_x);
25         WriteCoord(MSG_BROADCAST, v_y);
26         WriteCoord(MSG_BROADCAST, v_z);
27         WriteByte(MSG_BROADCAST, bound(0, 255 * charge, 255));
28 }
29
30 void W_Nex_Attack (float issecondary)
31 {
32         float mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, myammo, charge;
33         if(issecondary)
34         {
35                 mydmg = autocvar_g_balance_nex_secondary_damage;
36                 myforce = autocvar_g_balance_nex_secondary_force;
37                 mymindist = autocvar_g_balance_nex_secondary_damagefalloff_mindist;
38                 mymaxdist = autocvar_g_balance_nex_secondary_damagefalloff_maxdist;
39                 myhalflife = autocvar_g_balance_nex_secondary_damagefalloff_halflife;
40                 myforcehalflife = autocvar_g_balance_nex_secondary_damagefalloff_forcehalflife;
41                 myammo = autocvar_g_balance_nex_secondary_ammo;
42         }
43         else
44         {
45                 mydmg = autocvar_g_balance_nex_primary_damage;
46                 myforce = autocvar_g_balance_nex_primary_force;
47                 mymindist = autocvar_g_balance_nex_primary_damagefalloff_mindist;
48                 mymaxdist = autocvar_g_balance_nex_primary_damagefalloff_maxdist;
49                 myhalflife = autocvar_g_balance_nex_primary_damagefalloff_halflife;
50                 myforcehalflife = autocvar_g_balance_nex_primary_damagefalloff_forcehalflife;
51                 myammo = autocvar_g_balance_nex_primary_ammo;
52         }
53
54         float flying;
55         flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
56
57         if(autocvar_g_balance_nex_charge)
58         {
59                 charge = autocvar_g_balance_nex_charge_mindmg / mydmg + (1 - autocvar_g_balance_nex_charge_mindmg / mydmg) * self.nex_charge;
60                 self.nex_charge *= autocvar_g_balance_nex_charge_shot_multiplier; // do this AFTER setting mydmg/myforce
61                 // O RLY? -- divVerent
62                 // YA RLY -- FruitieX
63         }
64         else
65                 charge = 1;
66         mydmg *= charge;
67         myforce *= charge;
68
69         W_SetupShot (self, TRUE, 5, "weapons/nexfire.wav", CH_WEAPON_A, mydmg);
70         if(charge > autocvar_g_balance_nex_charge_animlimit && autocvar_g_balance_nex_charge_animlimit) // if the Nex is overcharged, we play an extra sound
71         {
72                 sound (self, CH_WEAPON_B, "weapons/nexcharge.wav", VOL_BASE * (charge - 0.5 * autocvar_g_balance_nex_charge_animlimit) / (1 - 0.5 * autocvar_g_balance_nex_charge_animlimit), ATTEN_NORM);
73         }
74
75         yoda = 0;
76         FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, WEP_NEX);
77
78         if(yoda && flying)
79                 Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA);
80
81         //beam and muzzle flash done on client
82         SendCSQCNexBeamParticle(charge);
83
84         W_DecreaseAmmo(ammo_cells, myammo, autocvar_g_balance_nex_reload_ammo);
85 }
86
87 void spawnfunc_weapon_nex (void); // defined in t_items.qc
88
89 .float nex_chargepool_pauseregen_finished;
90 float w_nex(float req)
91 {
92         float dt;
93         float ammo_amount;
94         if (req == WR_AIM)
95         {
96                 if(bot_aim(1000000, 0, 1, FALSE))
97                         self.BUTTON_ATCK = TRUE;
98                 else
99                 {
100                         if(autocvar_g_balance_nex_charge)
101                                 self.BUTTON_ATCK2 = TRUE;
102                 }
103         }
104         else if (req == WR_THINK)
105         {
106                 if(autocvar_g_balance_nex_charge && self.nex_charge < autocvar_g_balance_nex_charge_limit)
107                         self.nex_charge = min(1, self.nex_charge + autocvar_g_balance_nex_charge_rate * frametime / W_TICSPERFRAME);
108
109                 if(autocvar_g_balance_nex_secondary_chargepool)
110                         if(self.nex_chargepool_ammo < 1)
111                         {
112                                 if(self.nex_chargepool_pauseregen_finished < time)
113                                         self.nex_chargepool_ammo = min(1, self.nex_chargepool_ammo + autocvar_g_balance_nex_secondary_chargepool_regen * frametime / W_TICSPERFRAME);
114                                 self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_nex_secondary_chargepool_pause_health_regen);
115                         }
116
117                 if(autocvar_g_balance_nex_reload_ammo && self.clip_load < min(autocvar_g_balance_nex_primary_ammo, autocvar_g_balance_nex_secondary_ammo)) // forced reload
118                         weapon_action(self.weapon, WR_RELOAD);
119                 else
120                 {
121                         if (self.BUTTON_ATCK)
122                         {
123                                 if (weapon_prepareattack(0, autocvar_g_balance_nex_primary_refire))
124                                 {
125                                         W_Nex_Attack(0);
126                                         weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nex_primary_animtime, w_ready);
127                                 }
128                         }
129                         if ((autocvar_g_balance_nex_secondary_charge && !autocvar_g_balance_nex_secondary) ? (self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) : self.BUTTON_ATCK2)
130                         {
131                                 if(autocvar_g_balance_nex_secondary_charge)
132                                 {
133                                         self.nex_charge_rottime = time + autocvar_g_balance_nex_charge_rot_pause;
134                                         dt = frametime / W_TICSPERFRAME;
135
136                                         if(self.nex_charge < 1)
137                                         {
138                                                 if(autocvar_g_balance_nex_secondary_chargepool)
139                                                 {
140                                                         if(autocvar_g_balance_nex_secondary_ammo)
141                                                         {
142                                                                 // always deplete if secondary is held
143                                                                 self.nex_chargepool_ammo = max(0, self.nex_chargepool_ammo - autocvar_g_balance_nex_secondary_ammo * dt);
144
145                                                                 dt = min(dt, (1 - self.nex_charge) / autocvar_g_balance_nex_secondary_charge_rate);
146                                                                 self.nex_chargepool_pauseregen_finished = time + autocvar_g_balance_nex_secondary_chargepool_pause_regen;
147                                                                 dt = min(dt, self.nex_chargepool_ammo);
148                                                                 dt = max(0, dt);
149
150                                                                 self.nex_charge += dt * autocvar_g_balance_nex_secondary_charge_rate;
151                                                         }
152                                                 }
153
154                                                 else if(autocvar_g_balance_nex_secondary_ammo)
155                                                 {
156                                                         if(self.BUTTON_ATCK2) // only eat ammo when the button is pressed
157                                                         {
158                                                                 dt = min(dt, (1 - self.nex_charge) / autocvar_g_balance_nex_secondary_charge_rate);
159                                                                 if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
160                                                                 {
161                                                                         // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
162                                                                         if(autocvar_g_balance_nex_reload_ammo)
163                                                                         {
164                                                                                 dt = min(dt, (self.clip_load - autocvar_g_balance_nex_primary_ammo) / autocvar_g_balance_nex_secondary_ammo);
165                                                                                 dt = max(0, dt);
166                                                                                 if(dt > 0)
167                                                                                 {
168                                                                                         self.clip_load = max(autocvar_g_balance_nex_secondary_ammo, self.clip_load - autocvar_g_balance_nex_secondary_ammo * dt);
169                                                                                 }
170                                                                                 self.(weapon_load[WEP_NEX]) = self.clip_load;
171                                                                         }
172                                                                         else
173                                                                         {
174                                                                                 dt = min(dt, (self.ammo_cells - autocvar_g_balance_nex_primary_ammo) / autocvar_g_balance_nex_secondary_ammo);
175                                                                                 dt = max(0, dt);
176                                                                                 if(dt > 0)
177                                                                                 {
178                                                                                         self.ammo_cells = max(autocvar_g_balance_nex_secondary_ammo, self.ammo_cells - autocvar_g_balance_nex_secondary_ammo * dt);
179                                                                                 }
180                                                                         }
181                                                                 }
182                                                                 self.nex_charge += dt * autocvar_g_balance_nex_secondary_charge_rate;
183                                                         }
184                                                 }
185
186                                                 else
187                                                 {
188                                                         dt = min(dt, (1 - self.nex_charge) / autocvar_g_balance_nex_secondary_charge_rate);
189                                                         self.nex_charge += dt * autocvar_g_balance_nex_secondary_charge_rate;
190                                                 }
191                                         }
192                                 }
193                                 else if(autocvar_g_balance_nex_secondary)
194                                 {
195                                         if (weapon_prepareattack(0, autocvar_g_balance_nex_secondary_refire))
196                                         {
197                                                 W_Nex_Attack(1);
198                                                 weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nex_secondary_animtime, w_ready);
199                                         }
200                                 }
201                         }
202                 }
203         }
204         else if (req == WR_PRECACHE)
205         {
206                 precache_model ("models/nexflash.md3");
207                 precache_model ("models/weapons/g_nex.md3");
208                 precache_model ("models/weapons/v_nex.md3");
209                 precache_model ("models/weapons/h_nex.iqm");
210                 precache_sound ("weapons/nexfire.wav");
211                 precache_sound ("weapons/nexcharge.wav");
212                 precache_sound ("weapons/nexwhoosh1.wav");
213                 precache_sound ("weapons/nexwhoosh2.wav");
214                 precache_sound ("weapons/nexwhoosh3.wav");
215                 //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
216         }
217         else if (req == WR_SETUP)
218         {
219                 weapon_setup(WEP_NEX);
220                 self.current_ammo = ammo_cells;
221         }
222         else if (req == WR_CHECKAMMO1)
223         {
224                 ammo_amount = self.ammo_cells >= autocvar_g_balance_nex_primary_ammo;
225                 ammo_amount += (autocvar_g_balance_nex_reload_ammo && self.(weapon_load[WEP_NEX]) >= autocvar_g_balance_nex_primary_ammo);
226                 return ammo_amount;
227         }
228         else if (req == WR_CHECKAMMO2)
229         {
230                 if(autocvar_g_balance_nex_secondary)
231                 {
232                         // don't allow charging if we don't have enough ammo
233                         ammo_amount = self.ammo_cells >= autocvar_g_balance_nex_secondary_ammo;
234                         ammo_amount += self.(weapon_load[WEP_NEX]) >= autocvar_g_balance_nex_secondary_ammo;
235                         return ammo_amount;
236                 }
237                 else
238                 {
239                         return FALSE; // zoom is not a fire mode
240                 }
241         }
242         else if (req == WR_RELOAD)
243         {
244                 W_Reload(min(autocvar_g_balance_nex_primary_ammo, autocvar_g_balance_nex_secondary_ammo), autocvar_g_balance_nex_reload_ammo, autocvar_g_balance_nex_reload_time, "weapons/reload.wav");
245         }
246         else if (req == WR_SUICIDEMESSAGE)
247         {
248                 return WEAPON_THINKING_WITH_PORTALS;
249         }
250         else if (req == WR_KILLMESSAGE)
251         {
252                 return WEAPON_NEX_MURDER;
253         }
254         return TRUE;
255 }
256 #endif
257 #ifdef CSQC
258 float w_nex(float req)
259 {
260         if(req == WR_IMPACTEFFECT)
261         {
262                 vector org2;
263                 org2 = w_org + w_backoff * 6;
264                 pointparticles(particleeffectnum("nex_impact"), org2, '0 0 0', 1);
265                 if(!w_issilent)
266                         sound(self, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTEN_NORM);
267         }
268         else if(req == WR_PRECACHE)
269         {
270                 precache_sound("weapons/neximpact.wav");
271         }
272         return TRUE;
273 }
274 #endif
275 #endif