]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_tuba.qc
First step to implementing removal of spawned objects. Currently in debugging state...
[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                 self.realowner.tuba_note = world;
115                 remove(self);
116                 return;
117         }
118         self.nextthink = time;
119         dist_mult = autocvar_g_balance_tuba_attenuation / autocvar_snd_soundradius;
120         FOR_EACH_REALCLIENT(e)
121         if(e != self.realowner)
122         {
123                 v = self.origin - (e.origin + e.view_ofs);
124                 vol0 = max(0, 1 - vlen(v) * dist_mult);
125                 dir0 = normalize(v);
126                 v = self.realowner.origin - (e.origin + e.view_ofs);
127                 vol1 = max(0, 1 - vlen(v) * dist_mult);
128                 dir1 = normalize(v);
129                 if(fabs(vol0 - vol1) > 0.005) // 0.5 percent change in volume
130                 {
131                         setorigin(self, self.realowner.origin);
132                         self.SendFlags |= 2;
133                         break;
134                 }
135                 if(dir0 * dir1 < 0.9994) // 2 degrees change in angle
136                 {
137                         setorigin(self, self.realowner.origin);
138                         self.SendFlags |= 2;
139                         break;
140                 }
141         }
142 }
143
144 void W_Tuba_Attack(float hittype)
145 {
146         vector o;
147         float n;
148
149         W_SetupShot(self, FALSE, 2, "", 0, autocvar_g_balance_tuba_damage);
150
151         n = Tuba_GetNote(self, hittype);
152
153         hittype = 0;
154         if(self.tuba_instrument & 1)
155                 hittype |= HITTYPE_SECONDARY;
156         if(self.tuba_instrument & 2)
157                 hittype |= HITTYPE_BOUNCE;
158         if(self.tuba_instrument & 4)
159                 hittype |= HITTYPE_HEADSHOT;
160
161         if(self.tuba_note)
162         {
163                 if(self.tuba_note.cnt != n || self.tuba_note.tuba_instrument != self.tuba_instrument)
164                 {
165                         remove(self.tuba_note);
166                         self.tuba_note = world;
167                 }
168         }
169
170         if not(self.tuba_note)
171         {
172                 self.tuba_note = spawn();
173                 self.tuba_note.owner = self.tuba_note.realowner = self;
174                 self.tuba_note.cnt = n;
175                 self.tuba_note.tuba_instrument = self.tuba_instrument;
176                 self.tuba_note.think = W_Tuba_NoteThink;
177                 self.tuba_note.nextthink = time;
178                 Net_LinkEntity(self.tuba_note, FALSE, 0, W_Tuba_NoteSendEntity);
179         }
180
181         self.tuba_note.teleport_time = time + autocvar_g_balance_tuba_refire * 2 * W_WeaponRateFactor(); // so it can get prolonged safely
182
183         //sound(self, c, TUBA_NOTE(n), bound(0, VOL_BASE * cvar("g_balance_tuba_volume"), 1), autocvar_g_balance_tuba_attenuation);
184         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);
185
186         o = gettaginfo(self.exteriorweaponentity, 0);
187         if(time > self.tuba_smoketime)
188         {
189                 pointparticles(particleeffectnum("smoke_ring"), o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1);
190                 self.tuba_smoketime = time + 0.25;
191         }
192 }
193
194 void spawnfunc_weapon_tuba (void)
195 {
196         weapon_defaultspawnfunc(WEP_TUBA);
197 }
198
199 float w_tuba(float req)
200 {
201         if (req == WR_AIM)
202         {
203                 // bots cannot play the Tuba well yet
204                 // I think they should start with the recorder first
205                 if(vlen(self.origin - self.enemy.origin) < autocvar_g_balance_tuba_radius)
206                 {
207                         if(random() > 0.5)
208                                 self.BUTTON_ATCK = 1;
209                         else
210                                 self.BUTTON_ATCK2 = 1;
211                 }
212         }
213         else if (req == WR_THINK)
214         {
215                 if (self.BUTTON_ATCK)
216                 if (weapon_prepareattack(0, autocvar_g_balance_tuba_refire))
217                 {
218                         W_Tuba_Attack(0);
219                         //weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready);
220                         weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready);
221                 }
222                 if (self.BUTTON_ATCK2)
223                 if (weapon_prepareattack(1, autocvar_g_balance_tuba_refire))
224                 {
225                         W_Tuba_Attack(HITTYPE_SECONDARY);
226                         //weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready);
227                         weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready);
228                 }
229                 if(self.tuba_note)
230                 {
231                         if(!self.BUTTON_ATCK && !self.BUTTON_ATCK2)
232                         {
233                                 remove(self.tuba_note);
234                                 self.tuba_note = world;
235                         }
236                 }
237         }
238         else if (req == WR_PRECACHE)
239         {
240                 precache_model ("models/weapons/g_tuba.md3");
241                 precache_model ("models/weapons/v_tuba.md3");
242                 precache_model ("models/weapons/h_tuba.iqm");
243
244                 //float i;
245                 //for(i = -18; i <= +27; ++i)
246                 //      precache_sound(TUBA_NOTE(i));
247         }
248         else if (req == WR_SETUP)
249         {
250                 weapon_setup(WEP_TUBA);
251                 self.current_ammo = ammo_none;
252                 self.tuba_instrument = 0;
253         }
254         else if (req == WR_RELOAD)
255         {
256                 // switch to alternate instruments :)
257                 if(self.weaponentity.state == WS_READY)
258                 {
259                         switch(self.tuba_instrument)
260                         {
261                                 case 0:
262                                         self.tuba_instrument = 1;
263                                         self.weaponname = "akordeon";
264                                         break;
265                                 case 1:
266                                         self.tuba_instrument = 0;
267                                         self.weaponname = "tuba";
268                                         break;
269                         }
270                         W_SetupShot(self, FALSE, 0, "", 0, 0);
271                         pointparticles(particleeffectnum("teleport"), w_shotorg, '0 0 0', 1);
272                         self.weaponentity.state = WS_INUSE;
273                         weapon_thinkf(WFRAME_RELOAD, 0.5, w_ready);
274                 }
275         }
276         else if (req == WR_CHECKAMMO1)
277                 return TRUE; // TODO use fuel?
278         else if (req == WR_CHECKAMMO2)
279                 return TRUE; // TODO use fuel?
280         return TRUE;
281 }
282 #endif
283 #ifdef CSQC
284 float w_tuba(float req)
285 {
286         if(req == WR_IMPACTEFFECT)
287         {
288                 // nothing to do here; particles of tuba are handled differently
289         }
290         else if(req == WR_PRECACHE)
291         {
292                 // nothing to do
293         }
294         else if (req == WR_SUICIDEMESSAGE)
295         {
296                 float instr;
297                 instr = 0;
298                 if(w_deathtype & HITTYPE_SECONDARY)
299                         instr |= 1;
300                 if(w_deathtype & HITTYPE_BOUNCE)
301                         instr |= 2;
302                 if(w_deathtype & HITTYPE_HEADSHOT)
303                         instr |= 4;
304                 switch(instr)
305                 {
306                         default:
307                         case 0: // Tuba
308                                 w_deathtypestring = _("%s hurt his own ears with the @!#%%'n Tuba");
309                                 break;
310                         case 1: // Accordeon
311                                 w_deathtypestring = _("%s hurt his own ears with the @!#%%'n Accordeon");
312                                 break;
313                 }
314         }
315         else if (req == WR_KILLMESSAGE)
316         {
317                 float instr;
318                 instr = 0;
319                 if(w_deathtype & HITTYPE_SECONDARY)
320                         instr |= 1;
321                 if(w_deathtype & HITTYPE_BOUNCE)
322                         instr |= 2;
323                 if(w_deathtype & HITTYPE_HEADSHOT)
324                         instr |= 4;
325                 switch(instr)
326                 {
327                         default:
328                         case 0: // Tuba
329                                 w_deathtypestring = _("%s died of %s's great playing on the @!#%%'n Tuba");
330                                 break;
331                         case 1: // Accordeon
332                                 w_deathtypestring = _("%s died of %s's great playing on the @!#%%'n Accordeon");
333                                 break;
334                 }
335         }
336         return TRUE;
337 }
338 #endif
339 #endif