HLFX.Ru Forum
профиль •  правила •  регистрация •  календарь •  народ •  FAQ •  поиск •  новое •  сутки •  главная •  выход  
HLFX.Ru Forum HLFX.Ru Forum > Теория и практика > Half-Life SDK > Sentry Turret: последние штрихи
  Предыдущая тема   Следующая тема
Автор
Тема Новая тема    Ответить
Ghoul [BB]
МРАЗЬ ОПАСНАЯ

Дата регистрации: Jan 2006
Проживает: ...и прожигает...
Сообщений: 2567

Рейтинг



Sentry Turret: последние штрихи

Настал тот долгожданный день, когда я ПРАКТИЧЕСКИ закончил клепание кода для размещаемых игроком стационарных пушек.
Осталось самая малость, а именно ваша помошь в 2 вещах:

1. Правильное определение ОВНЕРА для туррели. У меня этого не получилось сделать....

2. Правильный спавн этой пушки и ее размещение на земле.. (отчего-то она иногда проваливается в пол или уходит в стены)

В общем, братцы, хелпните старому Мертвяку-Тульскому-Оружейнику!

КОД ТУРРЕЛИ:

==================================

C++ Source Code:
1
#include "extdll.h"
2
#include "util.h"
3
#include "cbase.h"
4
#include "monsters.h"
5
#include "weapons.h"
6
#include "effects.h"
7
 
8
#define TURRET_TURNRATE			30// angles per 0.1 second
9
#define TURRET_MAXWAIT			15// seconds turret will stay active w/o a target
10
 
11
typedef enum
12
{
13
  TURRET_ANIM_NONE = 0,
14
  TURRET_ANIM_FIRE,
15
  TURRET_ANIM_SPIN,
16
  TURRET_ANIM_DEPLOY,
17
  TURRET_ANIM_RETIRE,
18
  TURRET_ANIM_DIE,
19
} TURRET_ANIM;
20
 
21
class CBaseTurret : public CBaseMonster
22
{
23
public:
24
  void Spawn(void);
25
  virtual int Classify(void);
26
  int BloodColor( void ) { return DONT_BLEED; }
27
  void GibMonster( void ) {}
28
 
29
  void EXPORT ActiveThink(void);
30
  void EXPORT SearchThink(void);
31
  void EXPORT AutoSearchThink(void);
32
  void EXPORT TurretDeath(void);
33
 
34
  void EXPORT Deploy(void);
35
  void EXPORT Initialize(void);
36
  virtual void Ping(void);
37
 
38
  // other functions
39
  void SetTurretAnim(TURRET_ANIM anim);
40
  int MoveTurret(void);
41
  virtual void Shoot(Vector &vecSrc, Vector &vecDirToEnemy) { };
42
 
43
  int	m_iDeployHeight;
44
  int	m_iRetractHeight;
45
  int 	m_iMinPitch;
46
  int 	m_iBaseTurnRate;// angles per second
47
  float 	m_fTurnRate;// actual turn rate
48
  int	m_iOn;
49
 
50
  Vector 	m_vecLastSight;
51
  float 	m_flLastSight;	// Last time we saw a target
52
  float 	m_flMaxWait;		// Max time to seach w/o a target
53
 
54
  // movement
55
  float	m_flStartYaw;
56
  Vector	m_vecCurAngles;
57
  Vector	m_vecGoalAngles;
58
  float	m_flPingTime;
59
  float	m_flShootTime;
60
  EHANDLE m_hOwner;
61
};
62
 
63
 
64
void CBaseTurret::Spawn()
65
{
66
  pev->nextthink		= gpGlobals->time + 1;
67
  pev->movetype		= MOVETYPE_BOUNCE;
68
  pev->sequence		= 0;
69
  pev->frame		= 0;
70
  pev->solid		= SOLID_SLIDEBOX;
71
  pev->takedamage		= DAMAGE_AIM;
72
  pev->angles.x 		= 0;
73
  pev->angles.y 		= 0;
74
  pev->angles.z 		= 0;
75
 
76
  pev->gravity 		= 2;
77
  pev->friction 		= 1;
78
 
79
  ResetSequenceInfo();
80
  SetBoneController(0, 0);
81
  SetBoneController(1, 0);
82
  m_flFieldOfView = VIEW_FIELD_FULL;
83
 
84
  if (pev->owner)
85
    m_hOwner = Instance(pev->owner);
86
}
87
 
88
void CBaseTurret::Initialize(void)
89
{
90
  m_iOn = 0;
91
  SetBoneController( 0, 0 );
92
  SetBoneController( 1, 0 );
93
  if (m_iBaseTurnRate == 0) m_iBaseTurnRate = TURRET_TURNRATE;
94
  if (m_flMaxWait == 0) m_flMaxWait = TURRET_MAXWAIT;
95
  m_flStartYaw = pev->angles.y;
96
 
97
  m_vecGoalAngles.x = 0;
98
 
99
  m_flLastSight = gpGlobals->time + m_flMaxWait;
100
  SetThink(AutoSearchThink);
101
  pev->nextthink = gpGlobals->time + .1;
102
}
103
 
104
void CBaseTurret::Ping( void )
105
{
106
  if ( gpGlobals->time >= m_flPingTime )
107
  {
108
    EMIT_SOUND(ENT(pev), CHAN_ITEM, "turret/tu_ping.wav", 1, ATTN_NORM);
109
    m_flPingTime = gpGlobals->time + 1;
110
  }
111
}
112
 
113
void CBaseTurret::ActiveThink(void)
114
{
115
  //	CBaseEntity *pOther = NULL;
116
 
117
  int fAttack = 0;
118
  Vector vecDirToEnemy;
119
 
120
  pev->nextthink = gpGlobals->time + 0.1;
121
  StudioFrameAdvance( );
122
 
123
  /*
124
  	if ( pev->owner && pOther->edict() == pev->owner )
125
  	{
126
  		m_hEnemy = NULL;
127
  		m_flLastSight = gpGlobals->time + m_flMaxWait;
128
  		SetThink(SearchThink);
129
  		return;
130
  	}
131
  */
132
 
133
  if ((!m_iOn) || (m_hEnemy == NULL))
134
  {
135
    m_hEnemy = NULL;
136
    m_flLastSight = gpGlobals->time + m_flMaxWait;
137
    SetThink(SearchThink);
138
    return;
139
  }
140
 
141
  // if it's dead, look for something new
142
    if ( !m_hEnemy->IsAlive() )
143
    {
144
      if (!m_flLastSight)
145
      {
146
        m_flLastSight = gpGlobals->time;
147
      }
148
    else
149
    {
150
      if (gpGlobals->time > m_flLastSight)
151
      {
152
        m_hEnemy = NULL;
153
        m_flLastSight = gpGlobals->time + m_flMaxWait;
154
        SetThink(SearchThink);
155
        return;
156
      }
157
    }
158
  }
159
 
160
  Vector vecMid = pev->origin + pev->view_ofs;
161
  Vector vecMidEnemy = m_hEnemy->BodyTarget( vecMid );
162
  // Look for our current enemy
163
  int fEnemyVisible = FBoxVisible(pev, m_hEnemy->pev, vecMidEnemy );
164
  vecDirToEnemy = vecMidEnemy - vecMid;	// calculate dir and dist to enemy
165
  float flDistToEnemy = vecDirToEnemy.Length();
166
  Vector vec = UTIL_VecToAngles(vecMidEnemy - vecMid);
167
 
168
  // Current enmey is not visible.
169
  if (!fEnemyVisible || (flDistToEnemy > 8192))
170
  {
171
    if (!m_flLastSight)
172
      m_flLastSight = gpGlobals->time;
173
    else
174
    {
175
      // Should we look for a new target?
176
      if (gpGlobals->time > m_flLastSight)
177
      {
178
        m_hEnemy = NULL;
179
        m_flLastSight = gpGlobals->time + m_flMaxWait;
180
        SetThink(SearchThink);
181
        return;
182
      }
183
    }
184
    fEnemyVisible = 0;
185
  }
186
  else
187
    m_vecLastSight = vecMidEnemy;
188
 
189
  UTIL_MakeAimVectors(m_vecCurAngles);
190
 
191
  Vector vecLOS = vecDirToEnemy;
192
  vecLOS = vecLOS.Normalize();
193
 
194
  // Is the Gun looking at the target
195
  if (DotProduct(vecLOS, gpGlobals->v_forward) <= 0.866) // 30 degree slop
196
    fAttack = FALSE;
197
  else
198
    fAttack = TRUE;
199
 
200
  if (fAttack)
201
  {
202
    Vector vecSrc, vecAng;
203
    GetAttachment( 0, vecSrc, vecAng );
204
    Shoot(vecSrc, gpGlobals->v_forward );
205
  }
206
  else
207
  {
208
    SetTurretAnim(TURRET_ANIM_SPIN);
209
  }
210
 
211
  if (fEnemyVisible)
212
  {
213
    if (vec.y > 360)
214
      vec.y -= 360;
215
 
216
    if (vec.y < 0)
217
      vec.y += 360;
218
 
219
    if (vec.x < -180)
220
      vec.x += 360;
221
 
222
    if (vec.x > 180)
223
      vec.x -= 360;
224
 
225
    if (vec.x > 90)
226
      vec.x = 90;
227
    else if (vec.x < m_iMinPitch)
228
      vec.x = m_iMinPitch;
229
 
230
    m_vecGoalAngles.y = vec.y;
231
    m_vecGoalAngles.x = vec.x;
232
  }
233
  MoveTurret();
234
}
235
 
236
void CBaseTurret::Deploy(void)
237
{
238
  pev->nextthink = gpGlobals->time + 0.1;
239
  StudioFrameAdvance( );
240
 
241
  if (pev->sequence != TURRET_ANIM_DEPLOY)
242
  {
243
    m_iOn = 1;
244
    SetTurretAnim(TURRET_ANIM_DEPLOY);
245
    SUB_UseTargets( this, USE_ON, 0 );
246
  }
247
 
248
  if (m_fSequenceFinished)
249
  {
250
    pev->maxs.z = m_iDeployHeight;
251
    pev->mins.z = -m_iDeployHeight;
252
    UTIL_SetSize(pev, pev->mins, pev->maxs);
253
 
254
    m_vecCurAngles.x = 0;
255
 
256
    SetTurretAnim(TURRET_ANIM_SPIN);
257
    pev->framerate = 0;
258
    SetThink(SearchThink);
259
  }
260
 
261
  m_flLastSight = gpGlobals->time + m_flMaxWait;
262
}
263
 
264
void CBaseTurret::SetTurretAnim(TURRET_ANIM anim)
265
{
266
  if (pev->sequence != anim)
267
  {
268
    switch(anim)
269
    {
270
    case TURRET_ANIM_FIRE:
271
      case TURRET_ANIM_SPIN:
272
          if (pev->sequence != TURRET_ANIM_FIRE && pev->sequence != TURRET_ANIM_SPIN)
273
          {
274
            pev->frame = 0;
275
          }
276
        break;
277
      default:
278
          pev->frame = 0;
279
        break;
280
      }
281
 
282
    pev->sequence = anim;
283
    ResetSequenceInfo( );
284
 
285
    switch(anim)
286
    {
287
    case TURRET_ANIM_RETIRE:
288
        pev->frame		= 255;
289
      pev->framerate		= -1.0;
290
      break;
291
    case TURRET_ANIM_DIE:
292
        pev->framerate		= 1.0;
293
      break;
294
    }
295
  }
296
}
297
 
298
void CBaseTurret::SearchThink(void)
299
{
300
  // ensure rethink
301
  SetTurretAnim(TURRET_ANIM_SPIN);
302
  StudioFrameAdvance( );
303
  pev->nextthink = gpGlobals->time + 0.1;
304
 
305
  Ping( );
306
 
307
  // If we have a target and we're still healthy
308
  if (m_hEnemy != NULL)
309
  {
310
    if (!m_hEnemy->IsAlive() )
311
      m_hEnemy = NULL;// Dead enemy forces a search for new one
312
  }
313
 
314
  // Acquire Target
315
  if (m_hEnemy == NULL)
316
  {
317
    Look(8192);
318
    m_hEnemy = BestVisibleEnemy();
319
  }
320
 
321
  if (m_hEnemy != NULL)
322
  {
323
    m_flLastSight = 0;
324
    SetThink(ActiveThink);
325
  }
326
  else
327
  {
328
    // generic hunt for new victims
329
    m_vecGoalAngles.y = (m_vecGoalAngles.y + 0.1 * m_fTurnRate);
330
    if (m_vecGoalAngles.y >= 360)
331
      m_vecGoalAngles.y -= 360;
332
    MoveTurret();
333
  }
334
}
335
 
336
void CBaseTurret::AutoSearchThink(void)
337
{
338
  // ensure rethink
339
  StudioFrameAdvance( );
340
  pev->nextthink = gpGlobals->time + 0.3;
341
 
342
  // If we have a target and we're still healthy
343
  if (m_hEnemy != NULL)
344
  {
345
    if (!m_hEnemy->IsAlive() || m_hEnemy->edict() == pev->owner)
346
      m_hEnemy = NULL;// Dead enemy forces a search for new one
347
  }
348
 
349
  // Acquire Target
350
  if (m_hEnemy == NULL)
351
  {
352
    Look(8192);
353
    m_hEnemy = BestVisibleEnemy();
354
  }
355
 
356
  if (m_hEnemy != NULL)
357
    SetThink(Deploy);
358
}
359
 
360
void CBaseTurret ::TurretDeath( void )
361
{
362
  BOOL iActive = FALSE;
363
  StudioFrameAdvance( );
364
 
365
  if (pev->deadflag != DEAD_DEAD)
366
  {
367
    pev->deadflag = DEAD_DEAD;
368
    SetTurretAnim(TURRET_ANIM_DIE);
369
  }
370
 
371
  PLAYBACK_EVENT_FULL( FEV_GLOBAL, edict(), g_sExplosion, 0.0, (float *)&pev->origin, (float *)&pev->velocity, 0.0, 0.0, 0, EXPLOSION_TURRET, 0, 0 );
372
 
373
  if (m_hOwner != NULL)
374
    RadiusDamage ( pev, m_hOwner->pev, pev->dmg, CLASS_NONE, DMG_BLAST );
375
  else
376
    RadiusDamage ( pev, pev, pev->dmg, CLASS_NONE, DMG_BLAST );
377
 
378
  if (m_hOwner != NULL)
379
    pev->owner = m_hOwner->edict();
380
 
381
  pev->framerate = 0;
382
  SetThink( NULL );
383
  UTIL_Remove( this );
384
}
385
 
386
int CBaseTurret::MoveTurret(void)
387
{
388
  int state = 0;
389
  // any x movement?
390
 
391
  if (m_vecCurAngles.x != m_vecGoalAngles.x)
392
  {
393
    float flDir = m_vecGoalAngles.x > m_vecCurAngles.x ? 1 : -1 ;
394
 
395
    m_vecCurAngles.x += 0.1 * m_fTurnRate * flDir;
396
 
397
    // if we started below the goal, and now we're past, peg to goal
398
      if (flDir == 1)
399
      {
400
        if (m_vecCurAngles.x > m_vecGoalAngles.x)
401
          m_vecCurAngles.x = m_vecGoalAngles.x;
402
      }
403
    else
404
    {
405
      if (m_vecCurAngles.x < m_vecGoalAngles.x)
406
        m_vecCurAngles.x = m_vecGoalAngles.x;
407
    }
408
 
409
    SetBoneController(1, -m_vecCurAngles.x);
410
    state = 1;
411
  }
412
 
413
  if (m_vecCurAngles.y != m_vecGoalAngles.y)
414
  {
415
    float flDir = m_vecGoalAngles.y > m_vecCurAngles.y ? 1 : -1 ;
416
    float flDist = fabs(m_vecGoalAngles.y - m_vecCurAngles.y);
417
 
418
    if (flDist > 180)
419
    {
420
      flDist = 360 - flDist;
421
      flDir = -flDir;
422
    }
423
    if (flDist > 30)
424
    {
425
      if (m_fTurnRate < m_iBaseTurnRate * 10)
426
      {
427
        m_fTurnRate += m_iBaseTurnRate;
428
      }
429
    }
430
    else if (m_fTurnRate > 45)
431
    {
432
      m_fTurnRate -= m_iBaseTurnRate;
433
    }
434
    else
435
    {
436
      m_fTurnRate += m_iBaseTurnRate;
437
    }
438
 
439
    m_vecCurAngles.y += 0.1 * m_fTurnRate * flDir;
440
 
441
    if (m_vecCurAngles.y < 0)
442
      m_vecCurAngles.y += 360;
443
    else if (m_vecCurAngles.y >= 360)
444
      m_vecCurAngles.y -= 360;
445
 
446
    if (flDist < (0.05 * m_iBaseTurnRate))
447
      m_vecCurAngles.y = m_vecGoalAngles.y;
448
 
449
    SetBoneController(0, m_vecCurAngles.y - pev->angles.y );
450
    state = 1;
451
  }
452
 
453
  if (!state)
454
    m_fTurnRate = m_iBaseTurnRate;
455
 
456
  return state;
457
}
458
 
459
int	CBaseTurret::Classify ( void )
460
{
461
  return CLASS_MACHINE;
462
}
463
 
464
 
465
 
466
//=========================================================
467
// Sentry Turret - 9MM Minigun
468
//=========================================================
469
class CSentry : public CBaseTurret
470
{
471
public:
472
  void Spawn( );
473
  void Shoot(Vector &vecSrc, Vector &vecDirToEnemy);
474
  int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType);
475
};
476
LINK_ENTITY_TO_CLASS( monster_sentry, CSentry );
477
 
478
void CSentry::Spawn()
479
{
480
  CBaseTurret::Spawn();
481
  SET_MODEL(ENT(pev), "models/turret_sentry.mdl");
482
  pev->health		= 150;
483
  pev->dmg 		= 100;
484
  m_HackedGunPos		= Vector(0,0,48);
485
  pev->view_ofs.z		= 48;
486
  m_flMaxWait 		= 1E6;
487
  m_iRetractHeight 	= 64;
488
  m_iDeployHeight 	= 64;
489
  m_iMinPitch		= -60;
490
  UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 32));
491
  SetThink(Initialize);
492
  pev->nextthink = gpGlobals->time + 1;
493
}
494
 
495
void CSentry::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
496
{
497
  if ( gpGlobals->time >= m_flShootTime )
498
  {
499
    SetTurretAnim(TURRET_ANIM_FIRE);
500
    FireBullets( 3, vecSrc, vecDirToEnemy, VECTOR_CONE_8DEGREES, 8192, BULLET_MONSTER_9MM, 1 );
501
 
502
    EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/mp5_fire.wav", 1, ATTN_LOW_HIGH);
503
    m_flShootTime = gpGlobals->time + 1;
504
  }
505
}
506
 
507
int CSentry::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType)
508
{
509
  if ( bitsDamageType & DMG_BLIND)
510
    flDamage = 0;
511
 
512
  if ( bitsDamageType & DMG_KNOCKFORVARD)
513
    flDamage = 0.2;
514
 
515
  if ( bitsDamageType & DMG_NERVEGAS)
516
    flDamage = 0;
517
 
518
  switch ( RANDOM_LONG( 0, 2 ) )
519
  {
520
    case 0:	EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/ric1.wav", 1, ATTN_NORM);	break;
521
    case 1:	EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/ric2.wav", 1, ATTN_NORM);	break;
522
    case 2:	EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/ric3.wav", 1, ATTN_NORM);	break;
523
  }
524
 
525
  if (!m_iOn)
526
  {
527
    SetThink( Deploy );
528
    SetUse( NULL );
529
    pev->nextthink = gpGlobals->time + 0.1;
530
  }
531
 
532
  pev->health -= flDamage;
533
 
534
  if (pev->health <= 0)
535
  {
536
    pev->health = 0;
537
    pev->takedamage = DAMAGE_NO;
538
    pev->dmgtime = gpGlobals->time;
539
    SetUse(NULL);
540
    SetThink(TurretDeath);
541
    SUB_UseTargets( this, USE_ON, 0 );
542
    pev->nextthink = gpGlobals->time + 0.1;
543
    return 0;
544
  }
545
  return 1;
546
}



А это кусок первичной атаки для оружия. Спавн пушки.
Я еще не решил, как именно создавать:

1. Либо рядом с игроком, примерно как сейчас и ли в ТФК,
2. Либо как в Анриале - кидаем из оружия диск и спавним в том месте пушку...

Итак, сама атака:

=========================================

C++ Source Code:
1
void CCrowbar::SecondaryAttack()
2
{
3
  UTIL_MakeVectors( m_pPlayer->pev->v_angle );
4
  TraceResult tr;
5
  Vector trace_origin = m_pPlayer->pev->origin;
6
 
7
  if (m_pPlayer->pev->flags & FL_DUCKING)
8
    trace_origin = trace_origin - (VEC_HULL_MIN - VEC_DUCK_HULL_MIN);
9
 
10
  UTIL_TraceLine(trace_origin + gpGlobals->v_forward * 20, trace_origin + gpGlobals->v_forward * 150, dont_ignore_monsters, NULL, &tr );
11
 
12
  if (tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.25)
13
  {
14
    SendWeaponAnim(CROWBAR_ATTACK1HIT);
15
    m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
16
 
17
    CBaseEntity::Create( "monster_sentry", tr.vecEndPos, m_pPlayer->pev->v_angle + gpGlobals->v_forward * 200 + m_pPlayer->pev->velocity, m_pPlayer->edict() );
18
 
19
    m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.5;
20
    m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.5;
21
  }
22
}
23
 

Сообщить модератору | | IP: Записан
Сообщение: 2720

Старое сообщение 09-01-2006 22:54
- За что?
 XaeroX
Crystice Softworks

Дата регистрации: Oct 2005
Проживает: Торонто
Сообщений: 34527
Нанёс повреждений: 514 ед.
Возраст: 37

Рейтинг



Награды
 
[1 награда]


Ну код я не осилил. Да еще и подсветка синтаксиса глючит как обычно В общем, советы такие:
1. размещать лучше как в анриле, т.е. кидать какую нибудь штуку. Трейсить ко стенам и если надо отодвинуть точку. Чтобы не проваливалась, попробуй убрать флаг FL_ONGROUND (и если надо, приподнять оригин на 1 по Z). И еще см. далее про овнера.
2. Овнера ты уже указал при создании. Это pev->owner. Только там есть одна тонкость. Объекты с овнерами почему-от делаются несолидными. Поэтому рекомендую хранить овнера именно в m_hOwner, а pev->owner после ее установки поставить в NULL. Собсно, а какие проблемы с его определением? Тут подробнее.

__________________

Сообщить модератору | | IP: Записан
Сообщение: 2729

Старое сообщение 10-01-2006 07:39
-
Тема: (Опционально)
Ваш ответ:



Переводчик транслита


[проверить длину сообщения]
Опции: Автоматическое формирование ссылок: автоматически добавлять [url] и [/url] вокруг интернет адресов.
Уведомление по E-Mail: отправить вам уведомление, если кто-то ответил в тему (только для зарегистрированных пользователей).
Отключить смайлики в сообщении: не преобразовывать текстовые смайлики в картинки.
Показать подпись: добавить вашу подпись в конец сообщения (только зарегистрированные пользователи могут иметь подписи).

Временная зона GMT. Текущее время 14:30. Новая тема    Ответить
  Предыдущая тема   Следующая тема
HLFX.Ru Forum HLFX.Ru Forum > Теория и практика > Half-Life SDK > Sentry Turret: последние штрихи
Версия для печати | Отправить тему по E-Mail | Подписаться на эту тему

Быстрый переход:
Оцените эту тему:

Правила Форума:
Вы not можете создавать новые темы
Вы not можете отвечать в темы
Вы not можете прикреплять вложения
Вы not можете редактировать ваши сообщения
HTML Код ВЫКЛ
vB Код ВКЛ
Смайлики ВКЛ
[IMG] Код ВКЛ
 

< Обратная связь - HLFX.ru >

На основе vBulletin
Авторское право © 2000 - 2002, Jelsoft Enterprises Limited.
Дизайн и программирование: Crystice Softworks © 2005 - 2024