HLFX.Ru Forum (https://hlfx.ru/forum/index.php)
- Half-Life SDK (https://hlfx.ru/forum/forumdisplay.php?forumid=8)
-- Sentry Turret: последние штрихи (https://hlfx.ru/forum/showthread.php?threadid=179)
Отправлено Ghoul [BB] 09-01-2006 в 22:54:
Sentry Turret: последние штрихи
Настал тот долгожданный день, когда я ПРАКТИЧЕСКИ закончил клепание кода для размещаемых игроком стационарных пушек.
Осталось самая малость, а именно ваша помошь в 2 вещах:
1. Правильное определение ОВНЕРА для туррели. У меня этого не получилось сделать....
2. Правильный спавн этой пушки и ее размещение на земле.. (отчего-то она иногда проваливается в пол или уходит в стены)
В общем, братцы, хелпните старому Мертвяку-Тульскому-Оружейнику!
КОД ТУРРЕЛИ:
==================================
C++ Source Code:
8 | #define TURRET_TURNRATE 30// angles per 0.1 second |
9 | #define TURRET_MAXWAIT 15// seconds turret will stay active w/o a target |
21 | class CBaseTurret : public CBaseMonster |
25 | virtual int Classify(void); |
26 | int BloodColor( void ) { return DONT_BLEED; } |
27 | void GibMonster( void ) {} |
29 | void EXPORT ActiveThink(void); |
30 | void EXPORT SearchThink(void); |
31 | void EXPORT AutoSearchThink(void); |
32 | void EXPORT TurretDeath(void); |
34 | void EXPORT Deploy(void); |
35 | void EXPORT Initialize(void); |
36 | virtual void Ping(void); |
39 | void SetTurretAnim(TURRET_ANIM anim); |
41 | virtual void Shoot(Vector &vecSrc, Vector &vecDirToEnemy) { }; |
46 | int m_iBaseTurnRate;// angles per second |
47 | float m_fTurnRate;// actual turn rate |
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 |
56 | Vector m_vecCurAngles; |
57 | Vector m_vecGoalAngles; |
64 | void CBaseTurret::Spawn() |
66 | pev->nextthink = gpGlobals->time + 1; |
67 | pev->movetype = MOVETYPE_BOUNCE; |
70 | pev->solid = SOLID_SLIDEBOX; |
71 | pev->takedamage = DAMAGE_AIM; |
80 | SetBoneController(0, 0); |
81 | SetBoneController(1, 0); |
82 | m_flFieldOfView = VIEW_FIELD_FULL; |
85 | m_hOwner = Instance(pev->owner); |
88 | void CBaseTurret::Initialize(void) |
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; |
97 | m_vecGoalAngles.x = 0; |
99 | m_flLastSight = gpGlobals->time + m_flMaxWait; |
100 | SetThink(AutoSearchThink); |
101 | pev->nextthink = gpGlobals->time + .1; |
104 | void CBaseTurret::Ping( void ) |
106 | if ( gpGlobals->time >= m_flPingTime ) |
108 | EMIT_SOUND(ENT(pev), CHAN_ITEM, "turret/tu_ping.wav", 1, ATTN_NORM); |
109 | m_flPingTime = gpGlobals->time + 1; |
113 | void CBaseTurret::ActiveThink(void) |
115 | // CBaseEntity *pOther = NULL; |
118 | Vector vecDirToEnemy; |
120 | pev->nextthink = gpGlobals->time + 0.1; |
121 | StudioFrameAdvance( ); |
124 | if ( pev->owner && pOther->edict() == pev->owner ) |
127 | m_flLastSight = gpGlobals->time + m_flMaxWait; |
128 | SetThink(SearchThink); |
133 | if ((!m_iOn) || (m_hEnemy == NULL)) |
136 | m_flLastSight = gpGlobals->time + m_flMaxWait; |
137 | SetThink(SearchThink); |
141 | // if it's dead, look for something new |
142 | if ( !m_hEnemy->IsAlive() ) |
146 | m_flLastSight = gpGlobals->time; |
150 | if (gpGlobals->time > m_flLastSight) |
153 | m_flLastSight = gpGlobals->time + m_flMaxWait; |
154 | SetThink(SearchThink); |
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); |
168 | // Current enmey is not visible. |
169 | if (!fEnemyVisible || (flDistToEnemy > 8192)) |
172 | m_flLastSight = gpGlobals->time; |
175 | // Should we look for a new target? |
176 | if (gpGlobals->time > m_flLastSight) |
179 | m_flLastSight = gpGlobals->time + m_flMaxWait; |
180 | SetThink(SearchThink); |
187 | m_vecLastSight = vecMidEnemy; |
189 | UTIL_MakeAimVectors(m_vecCurAngles); |
191 | Vector vecLOS = vecDirToEnemy; |
192 | vecLOS = vecLOS.Normalize(); |
194 | // Is the Gun looking at the target |
195 | if (DotProduct(vecLOS, gpGlobals->v_forward) <= 0.866) // 30 degree slop |
202 | Vector vecSrc, vecAng; |
203 | GetAttachment( 0, vecSrc, vecAng ); |
204 | Shoot(vecSrc, gpGlobals->v_forward ); |
208 | SetTurretAnim(TURRET_ANIM_SPIN); |
227 | else if (vec.x < m_iMinPitch) |
230 | m_vecGoalAngles.y = vec.y; |
231 | m_vecGoalAngles.x = vec.x; |
236 | void CBaseTurret::Deploy(void) |
238 | pev->nextthink = gpGlobals->time + 0.1; |
239 | StudioFrameAdvance( ); |
241 | if (pev->sequence != TURRET_ANIM_DEPLOY) |
244 | SetTurretAnim(TURRET_ANIM_DEPLOY); |
245 | SUB_UseTargets( this, USE_ON, 0 ); |
248 | if (m_fSequenceFinished) |
250 | pev->maxs.z = m_iDeployHeight; |
251 | pev->mins.z = -m_iDeployHeight; |
252 | UTIL_SetSize(pev, pev->mins, pev->maxs); |
254 | m_vecCurAngles.x = 0; |
256 | SetTurretAnim(TURRET_ANIM_SPIN); |
258 | SetThink(SearchThink); |
261 | m_flLastSight = gpGlobals->time + m_flMaxWait; |
264 | void CBaseTurret::SetTurretAnim(TURRET_ANIM anim) |
266 | if (pev->sequence != anim) |
270 | case TURRET_ANIM_FIRE: |
271 | case TURRET_ANIM_SPIN: |
272 | if (pev->sequence != TURRET_ANIM_FIRE && pev->sequence != TURRET_ANIM_SPIN) |
282 | pev->sequence = anim; |
283 | ResetSequenceInfo( ); |
287 | case TURRET_ANIM_RETIRE: |
289 | pev->framerate = -1.0; |
291 | case TURRET_ANIM_DIE: |
292 | pev->framerate = 1.0; |
298 | void CBaseTurret::SearchThink(void) |
301 | SetTurretAnim(TURRET_ANIM_SPIN); |
302 | StudioFrameAdvance( ); |
303 | pev->nextthink = gpGlobals->time + 0.1; |
307 | // If we have a target and we're still healthy |
308 | if (m_hEnemy != NULL) |
310 | if (!m_hEnemy->IsAlive() ) |
311 | m_hEnemy = NULL;// Dead enemy forces a search for new one |
315 | if (m_hEnemy == NULL) |
318 | m_hEnemy = BestVisibleEnemy(); |
321 | if (m_hEnemy != NULL) |
324 | SetThink(ActiveThink); |
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; |
336 | void CBaseTurret::AutoSearchThink(void) |
339 | StudioFrameAdvance( ); |
340 | pev->nextthink = gpGlobals->time + 0.3; |
342 | // If we have a target and we're still healthy |
343 | if (m_hEnemy != NULL) |
345 | if (!m_hEnemy->IsAlive() || m_hEnemy->edict() == pev->owner) |
346 | m_hEnemy = NULL;// Dead enemy forces a search for new one |
350 | if (m_hEnemy == NULL) |
353 | m_hEnemy = BestVisibleEnemy(); |
356 | if (m_hEnemy != NULL) |
360 | void CBaseTurret ::TurretDeath( void ) |
362 | BOOL iActive = FALSE; |
363 | StudioFrameAdvance( ); |
365 | if (pev->deadflag != DEAD_DEAD) |
367 | pev->deadflag = DEAD_DEAD; |
368 | SetTurretAnim(TURRET_ANIM_DIE); |
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 ); |
373 | if (m_hOwner != NULL) |
374 | RadiusDamage ( pev, m_hOwner->pev, pev->dmg, CLASS_NONE, DMG_BLAST ); |
376 | RadiusDamage ( pev, pev, pev->dmg, CLASS_NONE, DMG_BLAST ); |
378 | if (m_hOwner != NULL) |
379 | pev->owner = m_hOwner->edict(); |
386 | int CBaseTurret::MoveTurret(void) |
391 | if (m_vecCurAngles.x != m_vecGoalAngles.x) |
393 | float flDir = m_vecGoalAngles.x > m_vecCurAngles.x ? 1 : -1 ; |
395 | m_vecCurAngles.x += 0.1 * m_fTurnRate * flDir; |
397 | // if we started below the goal, and now we're past, peg to goal |
400 | if (m_vecCurAngles.x > m_vecGoalAngles.x) |
401 | m_vecCurAngles.x = m_vecGoalAngles.x; |
405 | if (m_vecCurAngles.x < m_vecGoalAngles.x) |
406 | m_vecCurAngles.x = m_vecGoalAngles.x; |
409 | SetBoneController(1, -m_vecCurAngles.x); |
413 | if (m_vecCurAngles.y != m_vecGoalAngles.y) |
415 | float flDir = m_vecGoalAngles.y > m_vecCurAngles.y ? 1 : -1 ; |
416 | float flDist = fabs(m_vecGoalAngles.y - m_vecCurAngles.y); |
420 | flDist = 360 - flDist; |
425 | if (m_fTurnRate < m_iBaseTurnRate * 10) |
427 | m_fTurnRate += m_iBaseTurnRate; |
430 | else if (m_fTurnRate > 45) |
432 | m_fTurnRate -= m_iBaseTurnRate; |
436 | m_fTurnRate += m_iBaseTurnRate; |
439 | m_vecCurAngles.y += 0.1 * m_fTurnRate * flDir; |
441 | if (m_vecCurAngles.y < 0) |
442 | m_vecCurAngles.y += 360; |
443 | else if (m_vecCurAngles.y >= 360) |
444 | m_vecCurAngles.y -= 360; |
446 | if (flDist < (0.05 * m_iBaseTurnRate)) |
447 | m_vecCurAngles.y = m_vecGoalAngles.y; |
449 | SetBoneController(0, m_vecCurAngles.y - pev->angles.y ); |
454 | m_fTurnRate = m_iBaseTurnRate; |
459 | int CBaseTurret::Classify ( void ) |
461 | return CLASS_MACHINE; |
466 | //========================================================= |
467 | // Sentry Turret - 9MM Minigun |
468 | //========================================================= |
469 | class CSentry : public CBaseTurret |
473 | void Shoot(Vector &vecSrc, Vector &vecDirToEnemy); |
474 | int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); |
476 | LINK_ENTITY_TO_CLASS( monster_sentry, CSentry ); |
480 | CBaseTurret::Spawn(); |
481 | SET_MODEL(ENT(pev), "models/turret_sentry.mdl"); |
484 | m_HackedGunPos = Vector(0,0,48); |
485 | pev->view_ofs.z = 48; |
487 | m_iRetractHeight = 64; |
488 | m_iDeployHeight = 64; |
490 | UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 32)); |
491 | SetThink(Initialize); |
492 | pev->nextthink = gpGlobals->time + 1; |
495 | void CSentry::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) |
497 | if ( gpGlobals->time >= m_flShootTime ) |
499 | SetTurretAnim(TURRET_ANIM_FIRE); |
500 | FireBullets( 3, vecSrc, vecDirToEnemy, VECTOR_CONE_8DEGREES, 8192, BULLET_MONSTER_9MM, 1 ); |
502 | EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/mp5_fire.wav", 1, ATTN_LOW_HIGH); |
503 | m_flShootTime = gpGlobals->time + 1; |
507 | int CSentry::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) |
509 | if ( bitsDamageType & DMG_BLIND) |
512 | if ( bitsDamageType & DMG_KNOCKFORVARD) |
515 | if ( bitsDamageType & DMG_NERVEGAS) |
518 | switch ( RANDOM_LONG( 0, 2 ) ) |
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; |
529 | pev->nextthink = gpGlobals->time + 0.1; |
532 | pev->health -= flDamage; |
534 | if (pev->health <= 0) |
537 | pev->takedamage = DAMAGE_NO; |
538 | pev->dmgtime = gpGlobals->time; |
540 | SetThink(TurretDeath); |
541 | SUB_UseTargets( this, USE_ON, 0 ); |
542 | pev->nextthink = gpGlobals->time + 0.1; |
А это кусок первичной атаки для оружия. Спавн пушки.
Я еще не решил, как именно создавать:
1. Либо рядом с игроком, примерно как сейчас и ли в ТФК,
2. Либо как в Анриале - кидаем из оружия диск и спавним в том месте пушку...
Итак, сама атака:
=========================================
C++ Source Code:
1 | void CCrowbar::SecondaryAttack() |
3 | UTIL_MakeVectors( m_pPlayer->pev->v_angle ); |
5 | Vector trace_origin = m_pPlayer->pev->origin; |
7 | if (m_pPlayer->pev->flags & FL_DUCKING) |
8 | trace_origin = trace_origin - (VEC_HULL_MIN - VEC_DUCK_HULL_MIN); |
10 | UTIL_TraceLine(trace_origin + gpGlobals->v_forward * 20, trace_origin + gpGlobals->v_forward * 150, dont_ignore_monsters, NULL, &tr ); |
12 | if (tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.25) |
14 | SendWeaponAnim(CROWBAR_ATTACK1HIT); |
15 | m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); |
17 | CBaseEntity::Create( "monster_sentry", tr.vecEndPos, m_pPlayer->pev->v_angle + gpGlobals->v_forward * 200 + m_pPlayer->pev->velocity, m_pPlayer->edict() ); |
19 | m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.5; |
20 | m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.5; |
Отправлено XaeroX 10-01-2006 в 07:39:
Ну код я не осилил. Да еще и подсветка синтаксиса глючит как обычно
В общем, советы такие:
1. размещать лучше как в анриле, т.е. кидать какую нибудь штуку. Трейсить ко стенам и если надо отодвинуть точку. Чтобы не проваливалась, попробуй убрать флаг FL_ONGROUND (и если надо, приподнять оригин на 1 по Z). И еще см. далее про овнера.
2. Овнера ты уже указал при создании. Это pev->owner. Только там есть одна тонкость. Объекты с овнерами почему-от делаются несолидными. Поэтому рекомендую хранить овнера именно в m_hOwner, а pev->owner после ее установки поставить в NULL. Собсно, а какие проблемы с его определением? Тут подробнее.
__________________