2 REGISTER_WEAPON(TUBA, w_tuba, 0, 1, WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_MID, "tuba", "tuba", _("@!#%'n Tuba"))
5 //#define TUBA_NOTE(n) strcat("weapons/tuba_note", ftos(n), ".wav")
8 .float tuba_instrument;
10 #define MAX_TUBANOTES 32
11 .float tuba_lastnotes_pos;
12 .vector tuba_lastnotes[MAX_TUBANOTES];
17 // on: self.spawnshieldtime
20 if(self.owner.tuba_note == self)
22 self.owner.(tuba_lastnotes[self.owner.tuba_lastnotes_pos]) = eX * self.spawnshieldtime + eY * time + eZ * self.cnt;
23 self.owner.tuba_lastnotes_pos = mod(self.owner.tuba_lastnotes_pos + 1, MAX_TUBANOTES);
24 self.owner.tuba_note = world;
29 float Tuba_GetNote(entity pl, float hittype)
34 if(pl.movement_x < 0) movestate -= 3;
35 if(pl.movement_x > 0) movestate += 3;
36 if(pl.movement_y < 0) movestate -= 1;
37 if(pl.movement_y > 0) movestate += 1;
40 // layout: originally I wanted
44 // but then you only use forward and right key. So to make things more
45 // interesting, I swapped B with e#. Har har har...
49 case 1: note = -6; break; // Gb
50 case 2: note = -5; break; // G
51 case 3: note = -4; break; // G#
52 case 4: note = +5; break; // e#
53 case 5: note = 0; break; // c
54 case 6: note = +2; break; // d
55 case 7: note = +3; break; // eb
56 case 8: note = +4; break; // e
57 case 9: note = -1; break; // B
63 if(hittype & HITTYPE_SECONDARY)
66 // we support two kinds of tubas, those tuned in Eb and those tuned in C
67 // kind of tuba currently is player slot number, or team number if in
69 // that way, holes in the range of notes are "plugged"
72 if(pl.team == COLOR_TEAM2 || pl.team == COLOR_TEAM4)
77 if(pl.clientcolors & 1)
81 // total range of notes:
87 // *** ********************* ****
88 // -18.........................+12
89 // *** ********************* ****
90 // -18............................+15
96 float W_Tuba_NoteSendEntity(entity to, float sf)
101 if(!sound_allowed(MSG_ONE, self.realowner))
104 WriteByte(MSG_ENTITY, ENT_CLIENT_TUBANOTE);
105 WriteByte(MSG_ENTITY, sf);
108 WriteChar(MSG_ENTITY, self.cnt);
110 if(self.realowner != to)
112 f |= 2 * self.tuba_instrument;
113 WriteByte(MSG_ENTITY, f);
117 WriteCoord(MSG_ENTITY, self.origin_x);
118 WriteCoord(MSG_ENTITY, self.origin_y);
119 WriteCoord(MSG_ENTITY, self.origin_z);
124 void W_Tuba_NoteThink()
131 if(time > self.teleport_time)
136 self.nextthink = time;
137 dist_mult = autocvar_g_balance_tuba_attenuation / autocvar_snd_soundradius;
138 FOR_EACH_REALCLIENT(e)
139 if(e != self.realowner)
141 v = self.origin - (e.origin + e.view_ofs);
142 vol0 = max(0, 1 - vlen(v) * dist_mult);
144 v = self.realowner.origin - (e.origin + e.view_ofs);
145 vol1 = max(0, 1 - vlen(v) * dist_mult);
147 if(fabs(vol0 - vol1) > 0.005) // 0.5 percent change in volume
149 setorigin(self, self.realowner.origin);
153 if(dir0 * dir1 < 0.9994) // 2 degrees change in angle
155 setorigin(self, self.realowner.origin);
162 void W_Tuba_NoteOn(float hittype)
167 W_SetupShot(self, FALSE, 2, "", 0, autocvar_g_balance_tuba_damage);
169 n = Tuba_GetNote(self, hittype);
172 if(self.tuba_instrument & 1)
173 hittype |= HITTYPE_SECONDARY;
174 if(self.tuba_instrument & 2)
175 hittype |= HITTYPE_BOUNCE;
176 if(self.tuba_instrument & 4)
177 hittype |= HITTYPE_HEADSHOT;
181 if(self.tuba_note.cnt != n || self.tuba_note.tuba_instrument != self.tuba_instrument)
183 entity oldself = self;
184 self = self.tuba_note;
190 if not(self.tuba_note)
192 self.tuba_note = spawn();
193 self.tuba_note.owner = self.tuba_note.realowner = self;
194 self.tuba_note.cnt = n;
195 self.tuba_note.tuba_instrument = self.tuba_instrument;
196 self.tuba_note.think = W_Tuba_NoteThink;
197 self.tuba_note.nextthink = time;
198 self.tuba_note.spawnshieldtime = time;
199 Net_LinkEntity(self.tuba_note, FALSE, 0, W_Tuba_NoteSendEntity);
202 self.tuba_note.teleport_time = time + autocvar_g_balance_tuba_refire * 2 * W_WeaponRateFactor(); // so it can get prolonged safely
204 //sound(self, c, TUBA_NOTE(n), bound(0, VOL_BASE * cvar("g_balance_tuba_volume"), 1), autocvar_g_balance_tuba_attenuation);
205 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);
207 o = gettaginfo(self.exteriorweaponentity, 0);
208 if(time > self.tuba_smoketime)
210 pointparticles(particleeffectnum("smoke_ring"), o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1);
211 self.tuba_smoketime = time + 0.25;
215 void spawnfunc_weapon_tuba (void)
217 weapon_defaultspawnfunc(WEP_TUBA);
220 float w_tuba(float req)
224 // bots cannot play the Tuba well yet
225 // I think they should start with the recorder first
226 if(vlen(self.origin - self.enemy.origin) < autocvar_g_balance_tuba_radius)
229 self.BUTTON_ATCK = 1;
231 self.BUTTON_ATCK2 = 1;
234 else if (req == WR_THINK)
236 if (self.BUTTON_ATCK)
237 if (weapon_prepareattack(0, autocvar_g_balance_tuba_refire))
240 //weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready);
241 weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready);
243 if (self.BUTTON_ATCK2)
244 if (weapon_prepareattack(1, autocvar_g_balance_tuba_refire))
246 W_Tuba_NoteOn(HITTYPE_SECONDARY);
247 //weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready);
248 weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready);
252 if(!self.BUTTON_ATCK && !self.BUTTON_ATCK2)
254 entity oldself = self;
255 self = self.tuba_note;
261 else if (req == WR_PRECACHE)
263 precache_model ("models/weapons/g_tuba.md3");
264 precache_model ("models/weapons/v_tuba.md3");
265 precache_model ("models/weapons/h_tuba.iqm");
266 precache_model ("models/weapons/g_akordeon.md3");
267 precache_model ("models/weapons/v_akordeon.md3");
268 precache_model ("models/weapons/h_akordeon.iqm");
271 //for(i = -18; i <= +27; ++i)
272 // precache_sound(TUBA_NOTE(i));
274 else if (req == WR_SETUP)
276 weapon_setup(WEP_TUBA);
277 self.current_ammo = ammo_none;
278 self.tuba_instrument = 0;
280 else if (req == WR_RELOAD)
282 // switch to alternate instruments :)
283 if(self.weaponentity.state == WS_READY)
285 switch(self.tuba_instrument)
288 self.tuba_instrument = 1;
289 self.weaponname = "akordeon";
292 self.tuba_instrument = 0;
293 self.weaponname = "tuba";
296 W_SetupShot(self, FALSE, 0, "", 0, 0);
297 pointparticles(particleeffectnum("teleport"), w_shotorg, '0 0 0', 1);
298 self.weaponentity.state = WS_INUSE;
299 weapon_thinkf(WFRAME_RELOAD, 0.5, w_ready);
302 else if (req == WR_CHECKAMMO1)
303 return TRUE; // TODO use fuel?
304 else if (req == WR_CHECKAMMO2)
305 return TRUE; // TODO use fuel?
310 float w_tuba(float req)
312 if(req == WR_IMPACTEFFECT)
314 // nothing to do here; particles of tuba are handled differently
316 else if(req == WR_PRECACHE)
320 else if (req == WR_SUICIDEMESSAGE)
324 if(w_deathtype & HITTYPE_SECONDARY)
326 if(w_deathtype & HITTYPE_BOUNCE)
328 if(w_deathtype & HITTYPE_HEADSHOT)
334 w_deathtypestring = _("%s hurt his own ears with the @!#%%'n Tuba");
337 w_deathtypestring = _("%s hurt his own ears with the @!#%%'n Accordeon");
341 else if (req == WR_KILLMESSAGE)
345 if(w_deathtype & HITTYPE_SECONDARY)
347 if(w_deathtype & HITTYPE_BOUNCE)
349 if(w_deathtype & HITTYPE_HEADSHOT)
355 w_deathtypestring = _("%s died of %s's great playing on the @!#%%'n Tuba");
358 w_deathtypestring = _("%s died of %s's great playing on the @!#%%'n Accordeon");