]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/w_mortar.qc
Begin moving per-weapon crosshair selection as weapon specific info
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / w_mortar.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(
3 /* WEP_##id */ MORTAR,
4 /* function */ W_Mortar,
5 /* ammotype */ ammo_rockets,
6 /* impulse  */ 4,
7 /* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
8 /* rating   */ BOT_PICKUP_RATING_MID,
9 /* color        */ '1 0 0',
10 /* model    */ "gl",
11 /* crosshair */ "gfx/crosshairgrenadelauncher",
12 /* netname  */ "grenadelauncher",
13 /* fullname */ _("Mortar")
14 );
15
16 #define MORTAR_SETTINGS(w_cvar,w_prop) MORTAR_SETTINGS_LIST(w_cvar, w_prop, MORTAR, mortar)
17 #define MORTAR_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
18         w_cvar(id, sn, BOTH, ammo) \
19         w_cvar(id, sn, BOTH, animtime) \
20         w_cvar(id, sn, NONE, bouncefactor) \
21         w_cvar(id, sn, NONE, bouncestop) \
22         w_cvar(id, sn, BOTH, damage) \
23         w_cvar(id, sn, BOTH, damageforcescale) \
24         w_cvar(id, sn, BOTH, edgedamage) \
25         w_cvar(id, sn, BOTH, force) \
26         w_cvar(id, sn, BOTH, health) \
27         w_cvar(id, sn, BOTH, lifetime) \
28         w_cvar(id, sn, SEC,  lifetime_bounce) \
29         w_cvar(id, sn, BOTH, lifetime_stick) \
30         w_cvar(id, sn, BOTH, radius) \
31         w_cvar(id, sn, BOTH, refire) \
32         w_cvar(id, sn, SEC,  remote_detonateprimary) \
33         w_cvar(id, sn, PRI,  remote_minbouncecnt) \
34         w_cvar(id, sn, BOTH, speed) \
35         w_cvar(id, sn, BOTH, speed_up) \
36         w_cvar(id, sn, BOTH, speed_z) \
37         w_cvar(id, sn, BOTH, spread) \
38         w_cvar(id, sn, BOTH, type) \
39         w_prop(id, sn, float,  reloading_ammo, reload_ammo) \
40         w_prop(id, sn, float,  reloading_time, reload_time) \
41         w_prop(id, sn, float,  switchdelay_raise, switchdelay_raise) \
42         w_prop(id, sn, float,  switchdelay_drop, switchdelay_drop) \
43         w_prop(id, sn, string, weaponreplace, weaponreplace) \
44         w_prop(id, sn, float,  weaponstart, weaponstart) \
45         w_prop(id, sn, float,  weaponstartoverride, weaponstartoverride)
46
47 #ifdef SVQC
48 MORTAR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
49 .float gl_detonate_later;
50 .float gl_bouncecnt;
51 #endif
52 #else
53 #ifdef SVQC
54
55 void spawnfunc_weapon_mortar() { weapon_defaultspawnfunc(WEP_MORTAR); }
56 void spawnfunc_weapon_grenadelauncher() { spawnfunc_weapon_mortar(); }
57
58 void W_Mortar_Grenade_Explode()
59 {
60         if(other.takedamage == DAMAGE_AIM)
61                 if(IS_PLAYER(other))
62                         if(DIFF_TEAM(self.realowner, other))
63                                 if(other.deadflag == DEAD_NO)
64                                         if(IsFlying(other))
65                                                 Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
66
67         self.event_damage = func_null;
68         self.takedamage = DAMAGE_NO;
69
70         if(self.movetype == MOVETYPE_NONE)
71                 self.velocity = self.oldvelocity;
72
73         RadiusDamage (self, self.realowner, WEP_CVAR_PRI(mortar, damage), WEP_CVAR_PRI(mortar, edgedamage), WEP_CVAR_PRI(mortar, radius), world, world, WEP_CVAR_PRI(mortar, force), self.projectiledeathtype, other);
74
75         remove (self);
76 }
77
78 void W_Mortar_Grenade_Explode2()
79 {
80         if(other.takedamage == DAMAGE_AIM)
81                 if(IS_PLAYER(other))
82                         if(DIFF_TEAM(self.realowner, other))
83                                 if(other.deadflag == DEAD_NO)
84                                         if(IsFlying(other))
85                                                 Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
86
87         self.event_damage = func_null;
88         self.takedamage = DAMAGE_NO;
89
90         if(self.movetype == MOVETYPE_NONE)
91                 self.velocity = self.oldvelocity;
92
93         RadiusDamage (self, self.realowner, WEP_CVAR_SEC(mortar, damage), WEP_CVAR_SEC(mortar, edgedamage), WEP_CVAR_SEC(mortar, radius), world, world, WEP_CVAR_SEC(mortar, force), self.projectiledeathtype, other);
94
95         remove (self);
96 }
97
98
99 void W_Mortar_Grenade_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
100 {
101         if (self.health <= 0)
102                 return;
103                 
104         if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
105                 return; // g_projectiles_damage says to halt
106                 
107         self.health = self.health - damage;
108         
109         if (self.health <= 0)
110                 W_PrepareExplosionByDamage(attacker, self.use);
111 }
112
113 void W_Mortar_Grenade_Think1()
114 {
115         self.nextthink = time;
116         if (time > self.cnt)
117         {
118                 other = world;
119                 self.projectiledeathtype |= HITTYPE_BOUNCE;
120                 W_Mortar_Grenade_Explode();
121                 return;
122         }
123         if(self.gl_detonate_later && self.gl_bouncecnt >= WEP_CVAR_PRI(mortar, remote_minbouncecnt))
124                 W_Mortar_Grenade_Explode();
125 }
126
127 void W_Mortar_Grenade_Touch1()
128 {
129         PROJECTILE_TOUCH;
130         if (other.takedamage == DAMAGE_AIM || WEP_CVAR_PRI(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
131         {
132                 self.use ();
133         }
134         else if (WEP_CVAR_PRI(mortar, type) == 1) // bounce
135         {
136                 float r;
137                 r = random() * 6;
138                 if(r < 1)
139                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM);
140                 else if(r < 2)
141                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM);
142                 else if(r < 3)
143                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM);
144                 else if(r < 4)
145                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM);
146                 else if(r < 5)
147                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM);
148                 else
149                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM);
150                 pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1);
151                 self.projectiledeathtype |= HITTYPE_BOUNCE;
152                 self.gl_bouncecnt += 1;
153         }
154         else if(WEP_CVAR_PRI(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
155         {
156                 spamsound (self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
157
158                 // let it stick whereever it is
159                 self.oldvelocity = self.velocity;
160                 self.velocity = '0 0 0';
161                 self.movetype = MOVETYPE_NONE; // also disables gravity
162                 self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
163                 UpdateCSQCProjectile(self);
164
165                 // do not respond to any more touches
166                 self.solid = SOLID_NOT;
167
168                 self.nextthink = min(self.nextthink, time + WEP_CVAR_PRI(mortar, lifetime_stick));
169         }
170 }
171
172 void W_Mortar_Grenade_Touch2()
173 {
174         PROJECTILE_TOUCH;
175         if (other.takedamage == DAMAGE_AIM || WEP_CVAR_SEC(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
176         {
177                 self.use ();
178         }
179         else if (WEP_CVAR_SEC(mortar, type) == 1) // bounce
180         {
181                 float r;
182                 r = random() * 6;
183                 if(r < 1)
184                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM);
185                 else if(r < 2)
186                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM);
187                 else if(r < 3)
188                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM);
189                 else if(r < 4)
190                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM);
191                 else if(r < 5)
192                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM);
193                 else
194                         spamsound (self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM);
195                 pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1);
196                 self.projectiledeathtype |= HITTYPE_BOUNCE;
197                 self.gl_bouncecnt += 1;
198                 
199                 if (WEP_CVAR_SEC(mortar, lifetime_bounce) && self.gl_bouncecnt == 1)
200                         self.nextthink = time + WEP_CVAR_SEC(mortar, lifetime_bounce);
201                         
202         }
203         else if(WEP_CVAR_SEC(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
204         {
205                 spamsound (self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
206
207                 // let it stick whereever it is
208                 self.oldvelocity = self.velocity;
209                 self.velocity = '0 0 0';
210                 self.movetype = MOVETYPE_NONE; // also disables gravity
211                 self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
212                 UpdateCSQCProjectile(self);
213
214                 // do not respond to any more touches
215                 self.solid = SOLID_NOT;
216
217                 self.nextthink = min(self.nextthink, time + WEP_CVAR_SEC(mortar, lifetime_stick));
218         }
219 }
220
221 void W_Mortar_Attack()
222 {
223         entity gren;
224
225         W_DecreaseAmmo(WEP_CVAR_PRI(mortar, ammo));
226
227         W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage));
228         w_shotdir = v_forward; // no TrueAim for grenades please
229
230         pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
231
232         gren = spawn ();
233         gren.owner = gren.realowner = self;
234         gren.classname = "grenade";
235         gren.bot_dodge = TRUE;
236         gren.bot_dodgerating = WEP_CVAR_PRI(mortar, damage);
237         gren.movetype = MOVETYPE_BOUNCE;
238         gren.bouncefactor = WEP_CVAR(mortar, bouncefactor);
239         gren.bouncestop = WEP_CVAR(mortar, bouncestop);
240         PROJECTILE_MAKETRIGGER(gren);
241         gren.projectiledeathtype = WEP_MORTAR;
242         setorigin(gren, w_shotorg);
243         setsize(gren, '-3 -3 -3', '3 3 3');
244
245         gren.cnt = time + WEP_CVAR_PRI(mortar, lifetime);
246         gren.nextthink = time;
247         gren.think = W_Mortar_Grenade_Think1;
248         gren.use = W_Mortar_Grenade_Explode;
249         gren.touch = W_Mortar_Grenade_Touch1;
250
251         gren.takedamage = DAMAGE_YES;
252         gren.health = WEP_CVAR_PRI(mortar, health);
253         gren.damageforcescale = WEP_CVAR_PRI(mortar, damageforcescale);
254         gren.event_damage = W_Mortar_Grenade_Damage;
255         gren.damagedbycontents = TRUE;
256         gren.missile_flags = MIF_SPLASH | MIF_ARC;
257         W_SetupProjVelocity_UP_PRI(gren, mortar);
258
259         gren.angles = vectoangles (gren.velocity);
260         gren.flags = FL_PROJECTILE;
261
262         if(WEP_CVAR_PRI(mortar, type) == 0 || WEP_CVAR_PRI(mortar, type) == 2)
263                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE);
264         else
265                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE);
266
267         other = gren; MUTATOR_CALLHOOK(EditProjectile);
268 }
269
270 void W_Mortar_Attack2()
271 {
272         entity gren;
273
274         W_DecreaseAmmo(WEP_CVAR_SEC(mortar, ammo));
275
276         W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage));
277         w_shotdir = v_forward; // no TrueAim for grenades please
278
279         pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
280
281         gren = spawn ();
282         gren.owner = gren.realowner = self;
283         gren.classname = "grenade";
284         gren.bot_dodge = TRUE;
285         gren.bot_dodgerating = WEP_CVAR_SEC(mortar, damage);
286         gren.movetype = MOVETYPE_BOUNCE;
287         gren.bouncefactor = WEP_CVAR(mortar, bouncefactor);
288         gren.bouncestop = WEP_CVAR(mortar, bouncestop);
289         PROJECTILE_MAKETRIGGER(gren);
290         gren.projectiledeathtype = WEP_MORTAR | HITTYPE_SECONDARY;
291         setorigin(gren, w_shotorg);
292         setsize(gren, '-3 -3 -3', '3 3 3');
293
294         gren.nextthink = time + WEP_CVAR_SEC(mortar, lifetime);
295         gren.think = adaptor_think2use_hittype_splash;
296         gren.use = W_Mortar_Grenade_Explode2;
297         gren.touch = W_Mortar_Grenade_Touch2;
298
299         gren.takedamage = DAMAGE_YES;
300         gren.health = WEP_CVAR_SEC(mortar, health);
301         gren.damageforcescale = WEP_CVAR_SEC(mortar, damageforcescale);
302         gren.event_damage = W_Mortar_Grenade_Damage;
303         gren.damagedbycontents = TRUE;
304         gren.missile_flags = MIF_SPLASH | MIF_ARC;
305         W_SetupProjVelocity_UP_SEC(gren, mortar);
306
307         gren.angles = vectoangles (gren.velocity);
308         gren.flags = FL_PROJECTILE;
309
310         if(WEP_CVAR_SEC(mortar, type) == 0 || WEP_CVAR_SEC(mortar, type) == 2)
311                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE);
312         else
313                 CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE);
314
315         other = gren; MUTATOR_CALLHOOK(EditProjectile);
316 }
317
318 .float bot_secondary_grenademooth;
319 float W_Mortar(float req)
320 {
321         entity nade;
322         float nadefound;
323         float ammo_amount;
324         switch(req)
325         {
326                 case WR_AIM:
327                 {
328                         self.BUTTON_ATCK = FALSE;
329                         self.BUTTON_ATCK2 = FALSE;
330                         if (self.bot_secondary_grenademooth == 0) // WEAPONTODO: merge this into using WEP_CVAR_BOTH
331                         {
332                                 if(bot_aim(WEP_CVAR_PRI(mortar, speed), WEP_CVAR_PRI(mortar, speed_up), WEP_CVAR_PRI(mortar, lifetime), TRUE))
333                                 {
334                                         self.BUTTON_ATCK = TRUE;
335                                         if(random() < 0.01) self.bot_secondary_grenademooth = 1;
336                                 }
337                         }
338                         else
339                         {
340                                 if(bot_aim(WEP_CVAR_SEC(mortar, speed), WEP_CVAR_SEC(mortar, speed_up), WEP_CVAR_SEC(mortar, lifetime), TRUE))
341                                 {
342                                         self.BUTTON_ATCK2 = TRUE;
343                                         if(random() < 0.02) self.bot_secondary_grenademooth = 0;
344                                 }
345                         }
346                         
347                         return TRUE;
348                 }
349                 /*case WR_CALCINFO:
350                 {
351                         wepinfo_pri_refire = max3(sys_frametime, WEP_CVAR_PRI(mortar, refire), WEP_CVAR_PRI(mortar, animtime));
352                         wepinfo_pri_dps = (WEP_CVAR_PRI(mortar, damage) * (1 / wepinfo_pri_refire));
353                         wepinfo_pri_speed = (1 / max(1, (10000 / max(1, WEP_CVAR_PRI(mortar, speed)))));
354
355                         // for the range calculation, closer to 1 is better
356                         wepinfo_pri_range_max = 2000 * wepinfo_pri_speed;
357                         wepinfo_pri_range = wepinfo_pri_speed * WEP_CVAR_PRI(mortar, 
358                         
359                         wepinfo_sec_refire = max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime));
360                         wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / wepinfo_sec_refire));
361                         
362                         wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime))));
363                         wepinfo_ter_dps = 0;
364                         */
365                 case WR_THINK:
366                 {
367                         if(autocvar_g_balance_mortar_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo))) // forced reload
368                                 WEP_ACTION(self.weapon, WR_RELOAD);
369                         else if (self.BUTTON_ATCK)
370                         {
371                                 if (weapon_prepareattack(0, WEP_CVAR_PRI(mortar, refire)))
372                                 {
373                                         W_Mortar_Attack();
374                                         weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready);
375                                 }
376                         }
377                         else if (self.BUTTON_ATCK2)
378                         {
379                                 if(WEP_CVAR_SEC(mortar, remote_detonateprimary))
380                                 {
381                                         nadefound = 0;
382                                         for(nade = world; (nade = find(nade, classname, "grenade")); ) if(nade.realowner == self)
383                                         {
384                                                 if(!nade.gl_detonate_later)
385                                                 {
386                                                         nade.gl_detonate_later = TRUE;
387                                                         nadefound = 1;
388                                                 }
389                                         }
390                                         if(nadefound)
391                                                 sound (self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTN_NORM);
392                                 }
393                                 else if (weapon_prepareattack(1, WEP_CVAR_SEC(mortar, refire)))
394                                 {
395                                         W_Mortar_Attack2();
396                                         weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready);
397                                 }
398                         }
399                         
400                         return TRUE;
401                 }
402                 case WR_INIT:
403                 {
404                         precache_model ("models/weapons/g_gl.md3");
405                         precache_model ("models/weapons/v_gl.md3");
406                         precache_model ("models/weapons/h_gl.iqm");
407                         precache_sound ("weapons/grenade_bounce1.wav");
408                         precache_sound ("weapons/grenade_bounce2.wav");
409                         precache_sound ("weapons/grenade_bounce3.wav");
410                         precache_sound ("weapons/grenade_bounce4.wav");
411                         precache_sound ("weapons/grenade_bounce5.wav");
412                         precache_sound ("weapons/grenade_bounce6.wav");
413                         precache_sound ("weapons/grenade_stick.wav");
414                         precache_sound ("weapons/grenade_fire.wav");
415                         MORTAR_SETTINGS(WEP_SKIPCVAR, WEP_SET_PROP)
416                         return TRUE;
417                 }
418                 case WR_CHECKAMMO1:
419                 {
420                         ammo_amount = self.WEP_AMMO(MORTAR) >= WEP_CVAR_PRI(mortar, ammo);
421                         ammo_amount += self.(weapon_load[WEP_MORTAR]) >= WEP_CVAR_PRI(mortar, ammo);
422                         return ammo_amount;
423                 }
424                 case WR_CHECKAMMO2:
425                 {
426                         ammo_amount = self.WEP_AMMO(MORTAR) >= WEP_CVAR_SEC(mortar, ammo);
427                         ammo_amount += self.(weapon_load[WEP_MORTAR]) >= WEP_CVAR_SEC(mortar, ammo);
428                         return ammo_amount;
429                 }
430                 case WR_CONFIG:
431                 {
432                         MORTAR_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
433                         return TRUE;
434                 }
435                 case WR_RELOAD:
436                 {
437                         W_Reload(min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), "weapons/reload.wav"); // WEAPONTODO
438                         return TRUE;
439                 }
440                 case WR_SUICIDEMESSAGE:
441                 {
442                         if(w_deathtype & HITTYPE_SECONDARY)
443                                 return WEAPON_MORTAR_SUICIDE_BOUNCE;
444                         else
445                                 return WEAPON_MORTAR_SUICIDE_EXPLODE;
446                 }
447                 case WR_KILLMESSAGE:
448                 {
449                         if(w_deathtype & HITTYPE_SECONDARY)
450                                 return WEAPON_MORTAR_MURDER_BOUNCE;
451                         else
452                                 return WEAPON_MORTAR_MURDER_EXPLODE;
453                 }
454         }
455         return TRUE;
456 }
457 #endif
458 #ifdef CSQC
459 float W_Mortar(float req)
460 {
461         switch(req)
462         {
463                 case WR_IMPACTEFFECT:
464                 {
465                         vector org2;
466                         org2 = w_org + w_backoff * 12;
467                         pointparticles(particleeffectnum("grenade_explode"), org2, '0 0 0', 1);
468                         if(!w_issilent)
469                                 sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
470                                 
471                         return TRUE;
472                 }
473                 case WR_INIT:
474                 {
475                         precache_sound("weapons/grenade_impact.wav");
476                         return TRUE;
477                 }
478                 case WR_ZOOMRETICLE:
479                 {
480                         // no weapon specific image for this weapon
481                         return FALSE;
482                 }
483         }
484         return TRUE;
485 }
486 #endif
487 #endif