Merge remote branch 'origin/master' into samual/updatecommands
[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
9 float Tuba_GetNote(entity pl, float hittype)
10 {
11         float note;
12         float movestate;
13         movestate = 5;
14         if(pl.movement_x < 0) movestate -= 3;
15         if(pl.movement_x > 0) movestate += 3;
16         if(pl.movement_y < 0) movestate -= 1;
17         if(pl.movement_y > 0) movestate += 1;
18         switch(movestate)
19         {
20         // layout: originally I wanted
21         //   eb e  e#=f
22         //   B  c  d
23         //   Gb G  G#
24         // but then you only use forward and right key. So to make things more
25         // interesting, I swapped B with e#. Har har har...
26         //   eb e  B
27         // f=e# c  d
28         //   Gb G  G#
29                 case 1: note = -6; break; // Gb
30                 case 2: note = -5; break; // G
31                 case 3: note = -4; break; // G#
32                 case 4: note = +5; break; // e#
33                 case 5: note =  0; break; // c
34                 case 6: note = +2; break; // d
35                 case 7: note = +3; break; // eb
36                 case 8: note = +4; break; // e
37                 case 9: note = -1; break; // B
38         }
39         if(pl.BUTTON_CROUCH)
40                 note -= 12;
41         if(pl.BUTTON_JUMP)
42                 note += 12;
43         if(hittype & HITTYPE_SECONDARY)
44                 note += 7;
45         
46         // we support two kinds of tubas, those tuned in Eb and those tuned in C
47         // kind of tuba currently is player slot number, or team number if in
48         // teamplay
49         // that way, holes in the range of notes are "plugged"
50         if(teamplay)
51         {
52                 if(pl.team == COLOR_TEAM2 || pl.team == COLOR_TEAM4)
53                         note += 3;
54         }
55         else
56         {
57                 if(pl.clientcolors & 1)
58                         note += 3;
59         }
60         
61         // total range of notes:
62         //                       0
63         //                 ***  ** ****
64         //                        ***  ** ****
65         //     ***  ** ****
66         //            ***  ** ****
67         //     ***  ********************* ****
68         //     -18.........................+12
69         //        ***  ********************* ****
70         //     -18............................+15
71         //     with jump: ... +24
72         //     ... +27
73         return note;
74 }
75
76 float W_Tuba_NoteSendEntity(entity to, float sf)
77 {
78         WriteByte(MSG_ENTITY, ENT_CLIENT_TUBANOTE);
79         WriteByte(MSG_ENTITY, (sf & 1) | ((self.cnt + 42) * 2));
80         if(sf & 1)
81         {
82                 WriteCoord(MSG_ENTITY, self.origin_x);
83                 WriteCoord(MSG_ENTITY, self.origin_y);
84                 WriteCoord(MSG_ENTITY, self.origin_z);
85                 WriteByte(MSG_ENTITY, self.realowner != to);
86         }
87         return TRUE;
88 }
89
90 void W_Tuba_NoteThink()
91 {
92         float dist_mult;
93         float vol0, vol1;
94         vector dir0, dir1;
95         vector v;
96         entity e;
97         if(time > self.teleport_time)
98         {
99                 self.realowner.tuba_note = world;
100                 remove(self);
101                 return;
102         }
103         self.nextthink = time;
104         dist_mult = autocvar_g_balance_tuba_attenuation / autocvar_snd_soundradius;
105         FOR_EACH_REALCLIENT(e)
106         if(e != self.realowner)
107         {
108                 v = self.origin - (e.origin + e.view_ofs);
109                 vol0 = max(0, 1 - vlen(v) * dist_mult);
110                 dir0 = normalize(v);
111                 v = self.realowner.origin - (e.origin + e.view_ofs);
112                 vol1 = max(0, 1 - vlen(v) * dist_mult);
113                 dir1 = normalize(v);
114                 if(fabs(vol0 - vol1) > 0.005) // 0.5 percent change in volume
115                 {
116                         setorigin(self, self.realowner.origin);
117                         self.SendFlags |= 1;
118                         break;
119                 }
120                 if(dir0 * dir1 < 0.9994) // 2 degrees change in angle
121                 {
122                         setorigin(self, self.realowner.origin);
123                         self.SendFlags |= 1;
124                         break;
125                 }
126         }
127 }
128
129 void W_Tuba_Attack(float hittype)
130 {
131         vector o;
132         float n;
133         W_SetupShot(self, FALSE, 2, "", 0, autocvar_g_balance_tuba_damage);
134
135         n = Tuba_GetNote(self, hittype);
136
137         if(self.tuba_note)
138         {
139                 if(self.tuba_note.cnt != n)
140                 {
141                         /*
142                         self.tuba_note.cnt = n;
143                         self.tuba_note.SendFlags |= 2;
144                         */
145                         remove(self.tuba_note);
146                         self.tuba_note = world;
147                 }
148         }
149
150         if not(self.tuba_note)
151         {
152                 self.tuba_note = spawn();
153                 self.tuba_note.owner = self.tuba_note.realowner = self;
154                 self.tuba_note.cnt = n;
155                 self.tuba_note.think = W_Tuba_NoteThink;
156                 self.tuba_note.nextthink = time;
157                 Net_LinkEntity(self.tuba_note, FALSE, 0, W_Tuba_NoteSendEntity);
158         }
159
160         self.tuba_note.teleport_time = time + autocvar_g_balance_tuba_refire * 2; // so it can get prolonged safely
161
162         //sound(self, c, TUBA_NOTE(n), bound(0, VOL_BASE * cvar("g_balance_tuba_volume"), 1), autocvar_g_balance_tuba_attenuation);
163         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);
164
165         o = gettaginfo(self.exteriorweaponentity, 0);
166         if(time > self.tuba_smoketime)
167         {
168                 pointparticles(particleeffectnum("smoke_ring"), o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1);
169                 self.tuba_smoketime = time + 0.25;
170         }
171 }
172
173 void spawnfunc_weapon_tuba (void)
174 {
175         weapon_defaultspawnfunc(WEP_TUBA);
176 }
177
178 float w_tuba(float req)
179 {
180         if (req == WR_AIM)
181         {
182                 // bots cannot play the Tuba well yet
183                 // I think they should start with the recorder first
184                 if(vlen(self.origin - self.enemy.origin) < autocvar_g_balance_tuba_radius)
185                 {
186                         if(random() > 0.5)
187                                 self.BUTTON_ATCK = 1;
188                         else
189                                 self.BUTTON_ATCK2 = 1;
190                 }
191         }
192         else if (req == WR_THINK)
193         {
194                 if (self.BUTTON_ATCK)
195                 if (weapon_prepareattack(0, autocvar_g_balance_tuba_refire))
196                 {
197                         W_Tuba_Attack(0);
198                         //weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready);
199                         weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready);
200                 }
201                 if (self.BUTTON_ATCK2)
202                 if (weapon_prepareattack(1, autocvar_g_balance_tuba_refire))
203                 {
204                         W_Tuba_Attack(HITTYPE_SECONDARY);
205                         //weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready);
206                         weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready);
207                 }
208                 if(self.tuba_note)
209                 {
210                         if(!self.BUTTON_ATCK && !self.BUTTON_ATCK2)
211                         {
212                                 remove(self.tuba_note);
213                                 self.tuba_note = world;
214                         }
215                 }
216         }
217         else if (req == WR_PRECACHE)
218         {
219                 precache_model ("models/weapons/g_tuba.md3");
220                 precache_model ("models/weapons/v_tuba.md3");
221                 precache_model ("models/weapons/h_tuba.iqm");
222
223                 //float i;
224                 //for(i = -18; i <= +27; ++i)
225                 //      precache_sound(TUBA_NOTE(i));
226         }
227         else if (req == WR_SETUP)
228         {
229                 weapon_setup(WEP_TUBA);
230                 self.current_ammo = ammo_none;
231         }
232         else if (req == WR_CHECKAMMO1)
233                 return TRUE; // TODO use fuel?
234         else if (req == WR_CHECKAMMO2)
235                 return TRUE; // TODO use fuel?
236         return TRUE;
237 };
238 #endif
239 #ifdef CSQC
240 float w_tuba(float req)
241 {
242         if(req == WR_IMPACTEFFECT)
243         {
244                 // nothing to do here; particles of tuba are handled differently
245         }
246         else if(req == WR_PRECACHE)
247         {
248                 // nothing to do
249         }
250         else if (req == WR_SUICIDEMESSAGE)
251         {
252                 w_deathtypestring = _("%s hurt his own ears with the @!#%%'n Tuba");
253         }
254         else if (req == WR_KILLMESSAGE)
255         {
256                 w_deathtypestring = _("%s died of %s's great playing on the @!#%%'n Tuba");
257         }
258         return TRUE;
259 }
260 #endif
261 #endif