Custom Sounds
Custom sounds для монстров в VHE
Смысл туториала: многие новички хотят создать например, нового монстра. Для этого просто копируют zombie.cpp , а потом просто преиминовывают классы, пути к модели, звукам, но это мягко говоря - "неразумно". Вообщем не буду сильно "расходиться". Страдает оптимизация, в первую очередь и гибкость создания карты, или мода. Дядя Миша писал когда-то про наследование класов, но, но, но это тоже немного не то, что нужно новичку. Я предлагаю решить проблему так: Я буду работать на примере зомби, но так можно манипулировать со всеми монстрами. (Важно!!! Этот код для спирита, хотя если заменять целые похожие конструкции, то cкорей всего это будет работать и на сдк23!). Допустим, новичок поменял модель своего зомби в редакторе, но возник новый вопрос: а как собственно поменять звуки? Решение: Открываем zombie.cpp
Опускаемся ниже строчки:
static const char *pAttackMissSounds[];
И пишем:
//Звуки моего монстра(ов) static const char *pUndeadAlertSounds[]; static const char *pUndeadAttackSounds[]; static const char *pUndeadPainSounds[];
В нашем случае, пусть монстр будет называться undead. Далее спускаемся ниже строчек:
const char *CZombie::pPainSounds[] = { "zombie/zo_pain1.wav", "zombie/zo_pain2.wav", };
И добавляем:
//Собственно, сами звуки const char *CZombie::pUndeadAlertSounds[] = //Звуки, когда монстр вас увидит { "undead/undead_threat1.wav", "undead/undead_threat2.wav", "undead/undead_threat3.wav", }; const char *CZombie::pUndeadAttackSounds[] = //Звуки аттаки { "undead/undead_striking1.wav", "undead/undead_striking2.wav", }; const char *CZombie::pUndeadPainSounds[] = //Когда по монстру попадают { "undead/undead_ouch0.wav", "undead/undead_ouch1.wav", };
Далее мы должны выставить условия, при которых будет проигрываться наш звук, так вот он будет на прямую зависть от того, какую модель вы выбрали для монстра. Заменяем строки:
void CZombie :: PainSound( void ) { int pitch = 95 + RANDOM_LONG(0,9); if (RANDOM_LONG(0,5) < 2) EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); } void CZombie :: AlertSound( void ) { int pitch = 95 + RANDOM_LONG(0,9); EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); } void CZombie :: IdleSound( void ) { int pitch = 95 + RANDOM_LONG(0,9); // Play a random idle sound EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); } void CZombie :: AttackSound( void ) { // Play a random attack sound EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); }
На:
void CZombie :: PainSound( void ) { int pitch = 95 + RANDOM_LONG(0,9); if (RANDOM_LONG(0,5) < 2) { if ( FStrEq( STRING(pev->model), "models/undead.mdl" ) ) EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pUndeadPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pUndeadPainSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); else EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); } } void CZombie :: AlertSound( void ) { int pitch = 95 + RANDOM_LONG(0,9); if ( FStrEq( STRING(pev->model), "models/undead.mdl" ) ) EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pUndeadAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pUndeadAlertSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); else EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); } void CZombie :: IdleSound( void ) { int pitch = 95 + RANDOM_LONG(0,9); // Если вы хотите изменить и звуки ожидания, то нужно вначале их идентефицировать(на примере Pain,Alert,Attack) EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); } void CZombie :: AttackSound( void ) { if ( FStrEq( STRING(pev->model), "models/undead.mdl" ) ) EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pUndeadAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pUndeadAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); else EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); }
Мы почти завершили, осталось только выставить звуки и модель в список кэширования, и !!! если ваша модель больше или меньше стандартной, изменить "физический" ее размер, тоесть через UTIL_SetSize. Заменяем код:
//========================================================= // Spawn //========================================================= void CZombie :: Spawn() { Precache( ); if (pev->model) SET_MODEL(ENT(pev), STRING(pev->model)); //LRC else SET_MODEL(ENT(pev), "models/zombie.mdl"); UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_STEP; m_bloodColor = BLOOD_COLOR_GREEN; if (pev->health == 0) pev->health = gSkillData.zombieHealth; pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin. m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) m_MonsterState = MONSTERSTATE_NONE; m_afCapability = bits_CAP_DOORS_GROUP; MonsterInit(); }
На:
//========================================================= // Spawn //========================================================= void CZombie :: Spawn() { Precache( ); if (pev->model) SET_MODEL(ENT(pev), STRING(pev->model)); //LRC else SET_MODEL(ENT(pev), "models/zombie.mdl"); // Размеры для вашего монстра: if ( FStrEq( STRING(pev->model), "models/undead.mdl" ) ) UTIL_SetSize( pev, Vector(-18, -18, 0), Vector(18, 18, 74) ); else UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_STEP; m_bloodColor = BLOOD_COLOR_RED; if (pev->health == 0) pev->health = gSkillData.zombieHealth; pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin. m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) m_MonsterState = MONSTERSTATE_NONE; m_afCapability = bits_CAP_DOORS_GROUP; MonsterInit(); }
Осталось дело за малым, а именно прокэшировать наши ресурсы, для этого, заменяем:
//========================================================= // Precache - precaches all resources this monster needs //========================================================= void CZombie :: Precache() { int i; if (pev->model) PRECACHE_MODEL((char*)STRING(pev->model)); //LRC else PRECACHE_MODEL("models/zombie.mdl"); for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) PRECACHE_SOUND((char *)pAttackHitSounds[i]); for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) PRECACHE_SOUND((char *)pAttackMissSounds[i]); for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) PRECACHE_SOUND((char *)pAttackSounds[i]); for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) PRECACHE_SOUND((char *)pIdleSounds[i]); for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) PRECACHE_SOUND((char *)pAlertSounds[i]); for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) PRECACHE_SOUND((char *)pPainSounds[i]); }
На:
//========================================================= // Precache - precaches all resources this monster needs //========================================================= void CZombie :: Precache() { int i; if ( FStrEq( STRING(pev->model), "models/undead.mdl" ) ) { PRECACHE_MODEL("models/undead.mdl"); for ( i = 0; i < ARRAYSIZE( pUndeadAlertSounds ); i++ ) PRECACHE_SOUND((char *)pUndeadAlertSounds[i]); for ( i = 0; i < ARRAYSIZE( pUndeadAttackSounds ); i++ ) PRECACHE_SOUND((char *)pUndeadAttackSounds[i]); for ( i = 0; i < ARRAYSIZE( pUndeadPainSounds ); i++ ) PRECACHE_SOUND((char *)pUndeadPainSounds[i]); } else { if (pev->model) PRECACHE_MODEL((char*)STRING(pev->model)); else PRECACHE_MODEL("models/zombie.mdl"); for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) PRECACHE_SOUND((char *)pAlertSounds[i]); for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) PRECACHE_SOUND((char *)pAttackSounds[i]); for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) PRECACHE_SOUND((char *)pPainSounds[i]); } for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) PRECACHE_SOUND((char *)pIdleSounds[i]); for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) PRECACHE_SOUND((char *)pAttackHitSounds[i]); for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) PRECACHE_SOUND((char *)pAttackMissSounds[i]); }
Данный способ является более гибким, чем предложил Дядя Миша, и более оптимизирован. Для большей гибкости, можно использовать еще "else if ( FStrEq( STRING(pev->model), "models/undead2.mdl" ) )" Что позволит добовлять огромное кол-во мостров одного типа. В исходный код мы добавили максимум 1.5 кб. Теперь осталось откомпилировать, и в свойствах своего монстра поставить модель undead.mdl (undead - название модели) В дальнейшем я попробую реализовать еще более гибкий способ добавления звуков к вашему монстру, которые позволят всего лишь раз лезть в исходный код, для его редактирования. Если вы использовали обычный SDK, то нужно подредактировать ваш fgd, для этого нужно открыть его и в конец добавить:
@PointClass base(Monster) size(-16 -16 0, 16 16 72) studio () = monster_zombie : "Scientist Zombie" [ model(studio) : "Model" ]