]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_tuba.qc
Merge remote branch 'refs/remotes/origin/terencehill/numbered_bots'
[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 //#define TUBA_NOTE(n) strcat("weapons/tuba_note", ftos(n), ".wav")
5 .float tuba_notecount;
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(teams_matter)
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         }
86         return TRUE;
87 }
88
89 void W_Tuba_NoteThink()
90 {
91         float needchange, dist_mult;
92         float vol0, vol1;
93         vector dir0, dir1;
94         vector v;
95         entity e;
96         if(time > self.teleport_time)
97         {
98                 self.owner.tuba_note = world;
99                 remove(self);
100                 return;
101         }
102         self.nextthink = time;
103         dist_mult = cvar("g_balance_tuba_attenuation") / cvar("snd_soundradius");
104         needchange = 0;
105         FOR_EACH_REALCLIENT(e)
106         if(e != self.owner)
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.owner.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.owner.origin);
117                         self.SendFlags |= 1;
118                         break;
119                 }
120                 if(dir0 * dir1 < 0.9994) // 2 degrees change in angle
121                 {
122                         setorigin(self, self.owner.origin);
123                         self.SendFlags |= 1;
124                         break;
125                 }
126         }
127 }
128
129 void W_Tuba_Attack(float hittype)
130 {
131         vector o;
132         float c, n;
133         W_SetupShot(self, FALSE, 2, "", cvar("g_balance_tuba_damage"));
134         if(self.tuba_notecount)
135         {
136                 self.tuba_notecount = FALSE;
137                 c = CHAN_WEAPON;
138         }
139         else
140         {
141                 self.tuba_notecount = TRUE;
142                 c = CHAN_WEAPON2;
143         }
144
145         n = Tuba_GetNote(self, hittype);
146
147         if(self.tuba_note)
148         {
149                 if(self.tuba_note.cnt != n)
150                 {
151                         /*
152                         self.tuba_note.cnt = n;
153                         self.tuba_note.SendFlags |= 2;
154                         */
155                         remove(self.tuba_note);
156                         self.tuba_note = world;
157                 }
158         }
159
160         if not(self.tuba_note)
161         {
162                 self.tuba_note = spawn();
163                 self.tuba_note.owner = self;
164                 self.tuba_note.cnt = n;
165                 self.tuba_note.think = W_Tuba_NoteThink;
166                 self.tuba_note.nextthink = time;
167                 Net_LinkEntity(self.tuba_note, FALSE, 0, W_Tuba_NoteSendEntity);
168         }
169
170         self.tuba_note.teleport_time = time + cvar("g_balance_tuba_refire") * 2; // so it can get prolonged safely
171
172         //sound(self, c, TUBA_NOTE(n), bound(0, VOL_BASE * cvar("g_balance_tuba_volume"), 1), cvar("g_balance_tuba_attenuation"));
173         RadiusDamage(self, self, cvar("g_balance_tuba_damage"), cvar("g_balance_tuba_edgedamage"), cvar("g_balance_tuba_radius"), world, cvar("g_balance_tuba_force"), hittype | WEP_TUBA, world);
174
175         o = gettaginfo(self.exteriorweaponentity, 0);
176         if(time > self.tuba_smoketime)
177         {
178                 pointparticles(particleeffectnum("smoke_ring"), o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1);
179                 self.tuba_smoketime = time + 0.25;
180         }
181 }
182
183 void spawnfunc_weapon_tuba (void)
184 {
185         weapon_defaultspawnfunc(WEP_TUBA);
186 }
187
188 float w_tuba(float req)
189 {
190         if (req == WR_AIM)
191         {
192                 // bots cannot play the Tuba well yet
193                 // I think they should start with the recorder first
194                 if(vlen(self.origin - self.enemy.origin) < cvar("g_balance_tuba_radius"))
195                 {
196                         if(random() > 0.5)
197                                 self.BUTTON_ATCK = 1;
198                         else
199                                 self.BUTTON_ATCK2 = 1;
200                 }
201         }
202         else if (req == WR_THINK)
203         {
204                 if (self.BUTTON_ATCK)
205                 if (weapon_prepareattack(0, cvar("g_balance_tuba_refire")))
206                 {
207                         W_Tuba_Attack(0);
208                         //weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_tuba_animtime"), w_ready);
209                         weapon_thinkf(WFRAME_IDLE, cvar("g_balance_tuba_animtime"), w_ready);
210                 }
211                 if (self.BUTTON_ATCK2)
212                 if (weapon_prepareattack(1, cvar("g_balance_tuba_refire")))
213                 {
214                         W_Tuba_Attack(HITTYPE_SECONDARY);
215                         //weapon_thinkf(WFRAME_FIRE2, cvar("g_balance_tuba_animtime"), w_ready);
216                         weapon_thinkf(WFRAME_IDLE, cvar("g_balance_tuba_animtime"), w_ready);
217                 }
218                 if(self.tuba_note)
219                 {
220                         if(!self.BUTTON_ATCK && !self.BUTTON_ATCK2)
221                         {
222                                 remove(self.tuba_note);
223                                 self.tuba_note = world;
224                         }
225                 }
226         }
227         else if (req == WR_PRECACHE)
228         {
229                 precache_model ("models/weapons/g_tuba.md3");
230                 precache_model ("models/weapons/v_tuba.md3");
231                 precache_model ("models/weapons/h_tuba.iqm");
232
233                 //float i;
234                 //for(i = -18; i <= +27; ++i)
235                 //      precache_sound(TUBA_NOTE(i));
236         }
237         else if (req == WR_SETUP)
238                 weapon_setup(WEP_TUBA);
239         else if (req == WR_CHECKAMMO1)
240                 return TRUE; // TODO use fuel?
241         else if (req == WR_CHECKAMMO2)
242                 return TRUE; // TODO use fuel?
243         else if (req == WR_SUICIDEMESSAGE)
244         {
245                 w_deathtypestring = "hurt his own ears with the @!#%'n Tuba";
246         }
247         else if (req == WR_KILLMESSAGE)
248         {
249                 w_deathtypestring = "died of #'s great playing on the @!#%'n Tuba";
250         }
251         return TRUE;
252 };
253 #endif