]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_tuba.qc
some refactor
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_tuba.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(TUBA, w_tuba, 0, 1, WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_MID, "tuba", "tuba", _("@!#%'n Tuba"))
3 #else
4 #ifdef SVQC
5 //#define TUBA_NOTE(n) strcat("weapons/tuba_note", ftos(n), ".wav")
6 .entity tuba_note;
7 .float tuba_smoketime;
8 .float tuba_instrument;
9
10 float Tuba_GetNote(entity pl, float hittype)
11 {
12         float note;
13         float movestate;
14         movestate = 5;
15         if(pl.movement_x < 0) movestate -= 3;
16         if(pl.movement_x > 0) movestate += 3;
17         if(pl.movement_y < 0) movestate -= 1;
18         if(pl.movement_y > 0) movestate += 1;
19         switch(movestate)
20         {
21         // layout: originally I wanted
22         //   eb e  e#=f
23         //   B  c  d
24         //   Gb G  G#
25         // but then you only use forward and right key. So to make things more
26         // interesting, I swapped B with e#. Har har har...
27         //   eb e  B
28         // f=e# c  d
29         //   Gb G  G#
30                 case 1: note = -6; break; // Gb
31                 case 2: note = -5; break; // G
32                 case 3: note = -4; break; // G#
33                 case 4: note = +5; break; // e#
34                 case 5: note =  0; break; // c
35                 case 6: note = +2; break; // d
36                 case 7: note = +3; break; // eb
37                 case 8: note = +4; break; // e
38                 case 9: note = -1; break; // B
39         }
40         if(pl.BUTTON_CROUCH)
41                 note -= 12;
42         if(pl.BUTTON_JUMP)
43                 note += 12;
44         if(hittype & HITTYPE_SECONDARY)
45                 note += 7;
46         
47         // we support two kinds of tubas, those tuned in Eb and those tuned in C
48         // kind of tuba currently is player slot number, or team number if in
49         // teamplay
50         // that way, holes in the range of notes are "plugged"
51         if(teamplay)
52         {
53                 if(pl.team == COLOR_TEAM2 || pl.team == COLOR_TEAM4)
54                         note += 3;
55         }
56         else
57         {
58                 if(pl.clientcolors & 1)
59                         note += 3;
60         }
61         
62         // total range of notes:
63         //                       0
64         //                 ***  ** ****
65         //                        ***  ** ****
66         //     ***  ** ****
67         //            ***  ** ****
68         //     ***  ********************* ****
69         //     -18.........................+12
70         //        ***  ********************* ****
71         //     -18............................+15
72         //     with jump: ... +24
73         //     ... +27
74         return note;
75 }
76
77 float W_Tuba_NoteSendEntity(entity to, float sf)
78 {
79         float f;
80
81         msg_entity = to;
82         if(!sound_allowed(MSG_ONE, self.realowner))
83                 return FALSE;
84
85         WriteByte(MSG_ENTITY, ENT_CLIENT_TUBANOTE);
86         WriteByte(MSG_ENTITY, sf);
87         if(sf & 1)
88         {
89                 WriteChar(MSG_ENTITY, self.cnt);
90                 f = 0;
91                 if(self.realowner != to)
92                         f |= 1;
93                 f |= 2 * self.tuba_instrument;
94                 WriteByte(MSG_ENTITY, f);
95         }
96         if(sf & 2)
97         {
98                 WriteCoord(MSG_ENTITY, self.origin_x);
99                 WriteCoord(MSG_ENTITY, self.origin_y);
100                 WriteCoord(MSG_ENTITY, self.origin_z);
101         }
102         return TRUE;
103 }
104
105 void W_Tuba_NoteThink()
106 {
107         float dist_mult;
108         float vol0, vol1;
109         vector dir0, dir1;
110         vector v;
111         entity e;
112         if(time > self.teleport_time)
113         {
114                 W_Tuba_NoteOff();
115                 return;
116         }
117         self.nextthink = time;
118         dist_mult = autocvar_g_balance_tuba_attenuation / autocvar_snd_soundradius;
119         FOR_EACH_REALCLIENT(e)
120         if(e != self.realowner)
121         {
122                 v = self.origin - (e.origin + e.view_ofs);
123                 vol0 = max(0, 1 - vlen(v) * dist_mult);
124                 dir0 = normalize(v);
125                 v = self.realowner.origin - (e.origin + e.view_ofs);
126                 vol1 = max(0, 1 - vlen(v) * dist_mult);
127                 dir1 = normalize(v);
128                 if(fabs(vol0 - vol1) > 0.005) // 0.5 percent change in volume
129                 {
130                         setorigin(self, self.realowner.origin);
131                         self.SendFlags |= 2;
132                         break;
133                 }
134                 if(dir0 * dir1 < 0.9994) // 2 degrees change in angle
135                 {
136                         setorigin(self, self.realowner.origin);
137                         self.SendFlags |= 2;
138                         break;
139                 }
140         }
141 }
142
143 void W_Tuba_NoteOff()
144 {
145         remove(self.tuba_note);
146         self.tuba_note = world;
147 }
148
149 void W_Tuba_NoteOn(float hittype)
150 {
151         vector o;
152         float n;
153
154         W_SetupShot(self, FALSE, 2, "", 0, autocvar_g_balance_tuba_damage);
155
156         n = Tuba_GetNote(self, hittype);
157
158         hittype = 0;
159         if(self.tuba_instrument & 1)
160                 hittype |= HITTYPE_SECONDARY;
161         if(self.tuba_instrument & 2)
162                 hittype |= HITTYPE_BOUNCE;
163         if(self.tuba_instrument & 4)
164                 hittype |= HITTYPE_HEADSHOT;
165
166         if(self.tuba_note)
167         {
168                 if(self.tuba_note.cnt != n || self.tuba_note.tuba_instrument != self.tuba_instrument)
169                         W_Tuba_NoteOff();
170         }
171
172         if not(self.tuba_note)
173         {
174                 self.tuba_note = spawn();
175                 self.tuba_note.owner = self.tuba_note.realowner = self;
176                 self.tuba_note.cnt = n;
177                 self.tuba_note.tuba_instrument = self.tuba_instrument;
178                 self.tuba_note.think = W_Tuba_NoteThink;
179                 self.tuba_note.nextthink = time;
180                 Net_LinkEntity(self.tuba_note, FALSE, 0, W_Tuba_NoteSendEntity);
181         }
182
183         self.tuba_note.teleport_time = time + autocvar_g_balance_tuba_refire * 2 * W_WeaponRateFactor(); // so it can get prolonged safely
184
185         //sound(self, c, TUBA_NOTE(n), bound(0, VOL_BASE * cvar("g_balance_tuba_volume"), 1), autocvar_g_balance_tuba_attenuation);
186         RadiusDamage(self, self, autocvar_g_balance_tuba_damage, autocvar_g_balance_tuba_edgedamage, autocvar_g_balance_tuba_radius, world, autocvar_g_balance_tuba_force, hittype | WEP_TUBA, world);
187
188         o = gettaginfo(self.exteriorweaponentity, 0);
189         if(time > self.tuba_smoketime)
190         {
191                 pointparticles(particleeffectnum("smoke_ring"), o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1);
192                 self.tuba_smoketime = time + 0.25;
193         }
194 }
195
196 void spawnfunc_weapon_tuba (void)
197 {
198         weapon_defaultspawnfunc(WEP_TUBA);
199 }
200
201 float w_tuba(float req)
202 {
203         if (req == WR_AIM)
204         {
205                 // bots cannot play the Tuba well yet
206                 // I think they should start with the recorder first
207                 if(vlen(self.origin - self.enemy.origin) < autocvar_g_balance_tuba_radius)
208                 {
209                         if(random() > 0.5)
210                                 self.BUTTON_ATCK = 1;
211                         else
212                                 self.BUTTON_ATCK2 = 1;
213                 }
214         }
215         else if (req == WR_THINK)
216         {
217                 if (self.BUTTON_ATCK)
218                 if (weapon_prepareattack(0, autocvar_g_balance_tuba_refire))
219                 {
220                         W_Tuba_NoteOn(0);
221                         //weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready);
222                         weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready);
223                 }
224                 if (self.BUTTON_ATCK2)
225                 if (weapon_prepareattack(1, autocvar_g_balance_tuba_refire))
226                 {
227                         W_Tuba_NoteOn(HITTYPE_SECONDARY);
228                         //weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready);
229                         weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready);
230                 }
231                 if(self.tuba_note)
232                 {
233                         if(!self.BUTTON_ATCK && !self.BUTTON_ATCK2)
234                                 W_Tuba_NoteOff();
235                 }
236         }
237         else if (req == WR_PRECACHE)
238         {
239                 precache_model ("models/weapons/g_tuba.md3");
240                 precache_model ("models/weapons/v_tuba.md3");
241                 precache_model ("models/weapons/h_tuba.iqm");
242                 precache_model ("models/weapons/g_akordeon.md3");
243                 precache_model ("models/weapons/v_akordeon.md3");
244                 precache_model ("models/weapons/h_akordeon.iqm");
245
246                 //float i;
247                 //for(i = -18; i <= +27; ++i)
248                 //      precache_sound(TUBA_NOTE(i));
249         }
250         else if (req == WR_SETUP)
251         {
252                 weapon_setup(WEP_TUBA);
253                 self.current_ammo = ammo_none;
254                 self.tuba_instrument = 0;
255         }
256         else if (req == WR_RELOAD)
257         {
258                 // switch to alternate instruments :)
259                 if(self.weaponentity.state == WS_READY)
260                 {
261                         switch(self.tuba_instrument)
262                         {
263                                 case 0:
264                                         self.tuba_instrument = 1;
265                                         self.weaponname = "akordeon";
266                                         break;
267                                 case 1:
268                                         self.tuba_instrument = 0;
269                                         self.weaponname = "tuba";
270                                         break;
271                         }
272                         W_SetupShot(self, FALSE, 0, "", 0, 0);
273                         pointparticles(particleeffectnum("teleport"), w_shotorg, '0 0 0', 1);
274                         self.weaponentity.state = WS_INUSE;
275                         weapon_thinkf(WFRAME_RELOAD, 0.5, w_ready);
276                 }
277         }
278         else if (req == WR_CHECKAMMO1)
279                 return TRUE; // TODO use fuel?
280         else if (req == WR_CHECKAMMO2)
281                 return TRUE; // TODO use fuel?
282         return TRUE;
283 }
284 #endif
285 #ifdef CSQC
286 float w_tuba(float req)
287 {
288         if(req == WR_IMPACTEFFECT)
289         {
290                 // nothing to do here; particles of tuba are handled differently
291         }
292         else if(req == WR_PRECACHE)
293         {
294                 // nothing to do
295         }
296         else if (req == WR_SUICIDEMESSAGE)
297         {
298                 float instr;
299                 instr = 0;
300                 if(w_deathtype & HITTYPE_SECONDARY)
301                         instr |= 1;
302                 if(w_deathtype & HITTYPE_BOUNCE)
303                         instr |= 2;
304                 if(w_deathtype & HITTYPE_HEADSHOT)
305                         instr |= 4;
306                 switch(instr)
307                 {
308                         default:
309                         case 0: // Tuba
310                                 w_deathtypestring = _("%s hurt his own ears with the @!#%%'n Tuba");
311                                 break;
312                         case 1: // Accordeon
313                                 w_deathtypestring = _("%s hurt his own ears with the @!#%%'n Accordeon");
314                                 break;
315                 }
316         }
317         else if (req == WR_KILLMESSAGE)
318         {
319                 float instr;
320                 instr = 0;
321                 if(w_deathtype & HITTYPE_SECONDARY)
322                         instr |= 1;
323                 if(w_deathtype & HITTYPE_BOUNCE)
324                         instr |= 2;
325                 if(w_deathtype & HITTYPE_HEADSHOT)
326                         instr |= 4;
327                 switch(instr)
328                 {
329                         default:
330                         case 0: // Tuba
331                                 w_deathtypestring = _("%s died of %s's great playing on the @!#%%'n Tuba");
332                                 break;
333                         case 1: // Accordeon
334                                 w_deathtypestring = _("%s died of %s's great playing on the @!#%%'n Accordeon");
335                                 break;
336                 }
337         }
338         return TRUE;
339 }
340 #endif
341 #endif