* * * Форумы на Наша-Life THREAD * * * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- THREAD : Раздаём оружие игрока для npc Started at 14-05-2015 15:25 by 6apMaJIeu' Visit at https://bbs.dods.ru/showthread.php?threadid=58002 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- [Post 1] Author : 6apMaJIeu' Date : 14-05-2015 15:25 Title : Раздаём оружие игрока для npc Всем привет! В данной теме предлагаю решать вопросы раздачи оружия для npc, которого они изначально не имеют, так как похожей темы не нашёл. Например: почему в hl2 никто, кроме игрока, не стреляет из арбалета, магнума, или гранатой из автомата? Анимаций-то для этого вроде сделано предостаточно, ну или их на крайняк можно спереть от другого оружия. Магнумы так вообще везде почти валяются, но их никто не использует... Посему вот пара туторов для того, чтобы немного разнообразить снаряжение некоторых npc: 1. Итак, делаем так, чтобы, имея smg1, солдат Альянса стрелял подствольными гранатами, а не кидал ручные. npc_combine.cpp добавляем в начале: #include "grenade_ar2.h" Далее ищем Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity ) Находим условие else if ( enewActivity == ACT_RANGE_ATTACK2 ) В нём меняем оружие "weapon_grenadelauncher" на "weapon_smg1" Далее находим описание события в анимации case COMBINE_AE_GREN_LAUNCH и переделываем его след. образом: [code] case COMBINE_AE_GREN_LAUNCH: { EmitSound( "NPC_Combine.GrenadeLaunch" ); Vector vecThrow = m_vecTossVelocity; CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", Weapon_ShootPosition(), vec3_angle, this ); pGrenade->SetAbsVelocity( vecThrow ); //CBaseEntity *pGrenade = CreateNoSpawn( "npc_contactgrenade", Weapon_ShootPosition(), vec3_angle, this ); //pGrenade->KeyValue( "velocity", m_vecTossVelocity ); //pGrenade->Spawn( ); m_iNumGrenades--; if ( g_pGameRules->IsSkillLevel(SKILL_HARD) ) m_flNextGrenadeCheck = gpGlobals->curtime + random->RandomFloat( 2, 5 );// wait a random amount of time before shooting again else m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown. } handledEvent = true; break; [/code] В void CNPC_Combine::Precache() добавляем PrecacheModel("models/Weapons/ar2_grenade.mdl"); Компилируем server.dll Теперь этот anim event надо добавить в исходник модельки. SourceSDK пожопился и не дал нам его, так что будем делать по-кривому. Декомпилируем combine_soldier.mdl и combine_soldier_anims.mdl Лучше брать последние версии моделей. combine_soldier_anims лучше декомпилировать именно CannonFodder's MDL Decompiler'ом, т.к. он хотябы некоторые анимации не испортит при декомпиляции. Из декомпилированного кала combine_soldier_anims, уж извините по другому не назовёшь, берём shootAR2g.smd (мне она показалась самой подходящей), переименовываем в grenLaunch.smd и переносим его в директорию декомпилированного combine_soldier. Открываем qc-файл от combine_soldier и дописываем после $sequence ragdoll "ragdoll" ACT_DIERAGDOLL 1 fps 30.00 нашу анимацию и event $sequence grenLaunch "grenLaunch" ACT_COMBINE_LAUNCH_GRENADE 1 { event 8 16 "" } fps 30.00 Компилируем. Аналогичные вещи проделываем с combine_soldier_prisonguard. (Конечно анимации должны по идее содержаться в combine_soldier_anims, но у меня не получилось его нормально декомпилировать. Так что могу предложить только такой способ.) Важно обратить внимание на движок игры при компиляции: - для старых версий, 2003 - 2005, используйте конфигурацию ер1. - для новых - с 2007-го - orangebox. отличаются они расположением файла studiomdl.exe: В первом случае: ...Source SDK/bin/ep1/bin Во втором: ...Source SDK/bin/orangebox/bin Если скомпилировать не на том движке - могут глючить анимации. Теперь заменяем скомпилированные phy-файлы на оригинальные. Это нужно, чтобы не было багов с ragdoll'ом модельки. Всё. Теперь солдат будет стрелять гранатами из автомата так же, как игрок. 2. Даём ГО-шнику револьвер "Магнум 357": Прежде всего: убираем нафиг оригинальную модельку и вставляем в директорию ../models/weapons ту, что я прикрепил к этому сообщению. Привязки и физика теперь впорядке, единственное - на оригинальных картах может лежать не совсем в тех же местах, т. к. в idle анимации оригинальной модельки ствол почему-то лежал на боку... Кодируем weapon_357.cpp: после всех #includ'оф в начале файла дописываем данные, предназначенные для персонажей: [code] //----------------------------------------------------------------------------- // CWeapon357 //----------------------------------------------------------------------------- class CWeapon357 : public CBaseHLCombatWeapon { DECLARE_DATADESC(); public: DECLARE_CLASS( CWeapon357, CBaseHLCombatWeapon ); CWeapon357(void); DECLARE_SERVERCLASS(); void Precache( void ); void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); void PrimaryAttack( void ); int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } virtual void Equip( CBaseCombatCharacter *pOwner ); virtual const Vector& GetBulletSpread( void ) { // Handle NPCs first static Vector npcCone = VECTOR_CONE_5DEGREES; if ( GetOwner() && GetOwner()->IsNPC() ) return npcCone; static Vector cone; cone = VECTOR_CONE_4DEGREES; return cone; } virtual int GetMinBurst() { return 1; } virtual int GetMaxBurst() { return 2; } virtual float GetFireRate( void ) { return 1.5f; } DECLARE_ACTTABLE(); }; IMPLEMENT_SERVERCLASS_ST(CWeapon357, DT_Weapon357) END_SEND_TABLE() LINK_ENTITY_TO_CLASS( weapon_357, CWeapon357 ); PRECACHE_WEAPON_REGISTER( weapon_357 ); BEGIN_DATADESC( CWeapon357 ) END_DATADESC() acttable_t CWeapon357::m_acttable[] = { { ACT_IDLE, ACT_IDLE_PISTOL, true }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_PISTOL, true }, { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, true }, { ACT_RELOAD, ACT_RELOAD_SMG1, true }, { ACT_WALK_AIM, ACT_WALK_AIM_PISTOL, true }, { ACT_RUN_AIM, ACT_RUN_AIM_PISTOL, true }, { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_PISTOL,true }, { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_PISTOL_LOW, false }, { ACT_COVER_LOW, ACT_COVER_PISTOL_LOW, false }, { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_PISTOL_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, false }, }; IMPLEMENT_ACTTABLE( CWeapon357 ); //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CWeapon357::CWeapon357( void ) { m_fMinRange1 = 24; m_fMaxRange1 = 1500; m_bReloadsSingly = false; m_bFiresUnderwater = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeapon357::Precache( void ) { BaseClass::Precache(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CWeapon357::Equip( CBaseCombatCharacter *pOwner ) { BaseClass::Equip( pOwner ); } [/code] Далее в том же файле после case event_weapon_reload дописываем ещё два. [code] case EVENT_WEAPON_HG_RELOAD: { CAI_BaseNPC *npc = pOperator->MyNPCPointer(); CEffectData data; for ( int i = 0; i < 6; i++ ) { data.m_vOrigin = npc->Weapon_ShootPosition() + RandomVector( -4, 4 ); data.m_vAngles = QAngle( 90, random->RandomInt( 0, 360 ), 0 ); data.m_nEntIndex = entindex(); DispatchEffect( "ShellEject", data ); } break; } case EVENT_WEAPON_PISTOL_FIRE: { Vector vecShootOrigin, vecShootDir; vecShootOrigin = pOperator->Weapon_ShootPosition(); CAI_BaseNPC *npc = pOperator->MyNPCPointer(); ASSERT( npc != NULL ); vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); CSoundEnt::InsertSound( SOUND_COMBAT, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, pOperator ); WeaponSound( SINGLE_NPC ); pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2 ); pOperator->DoMuzzleFlash(); m_iClip1 = m_iClip1 - 1; } break; default: BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); break; [/code] Добавляем название "EVENT_WEAPON_HG_RELOAD" в ..game_shared/npcevent.h и ставим ему значение 3019 на всякий случай. Теперь открываем npc_metropolice.cpp void CNPC_MetroPolice::Spawn( void ) Здесь под if'ом с пистолетом МОЖНО БЫЛО БЫ дописать: [code] if( !FClassnameIs( pWeapon, "weapon_357" ) ) { m_fWeaponDrawn = true; } [/code] ...но тогда этот дурак npc_metropolice перестанет доставать из кабуры и пистолет, и револьвер! Видимо, эта функция может работать только для ОДНОГО оружия, не важно какого. А вот почему - загадка на уровне чудес, которую я так и не раскопал... В том же файле void CNPC_MetroPolice::OnUpdateShotRegulator( ) под if'ом с weapon_pistol дописываем: [code] if( Weapon_OwnsThisType( "weapon_357" ) ) { if ( m_nBurstMode == BURST_NOT_ACTIVE ) { GetShotRegulator()->SetBurstShotCountRange(GetActiveWeapon()->GetMinBurst(), GetActiveWeapon()->GetMaxBurst() ); GetShotRegulator()->SetRestInterval( 1.2, 2.0 ); } } [/code] В том же файле внутри "Activity CNPC_MetroPolice::NPC_TranslateActivity( Activity newActivity )" дописываем if: [code] if ( Weapon_OwnsThisType( "weapon_357" ) ) { if ( newActivity == ACT_RELOAD || CapabilitiesGet() & bits_CAP_DUCK && newActivity == ACT_RELOAD_LOW ) { //Emit 6 shells for hand gun reloading animevent_t fakeEvent; fakeEvent.pSource = this; fakeEvent.event = EVENT_WEAPON_HG_RELOAD; GetActiveWeapon()->Operator_HandleAnimEvent( &fakeEvent, this ); } } [/code] В том же файле [code] WeaponProficiency_t CNPC_MetroPolice::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ) { if( FClassnameIs( pWeapon, "weapon_pistol" ) ) { return WEAPON_PROFICIENCY_POOR; } if( FClassnameIs( pWeapon, "weapon_smg1" ) ) { return WEAPON_PROFICIENCY_VERY_GOOD; } if( FClassnameIs( pWeapon, "weapon_357" ) ) { return WEAPON_PROFICIENCY_AVERAGE; } return BaseClass::CalcWeaponProficiency( pWeapon ); } [/code] Теперь открываем c_te_legacy_tempents.cpp в исходниках для client.dll, в корешке, и дописываем muzzleflash для 357: [code] case MUZZLEFLASH_357: if ( firstPerson ) { MuzzleFlash_357_Player( entityIndex, attachmentIndex ); } else { MuzzleFlash_357_NPC( entityIndex, attachmentIndex ); } break; [/code] Далее: [code] case MUZZLEFLASH_357: if ( firstPerson ) { MuzzleFlash_357_Player( entityIndex, 1 ); } else { MuzzleFlash_357_NPC( entityIndex, 1 ); } break; [/code] Далее после "void CTempEnts::MuzzleFlash_357_Player( int entityIndex, int attachmentIndex )" делаем ещё один void: [code] void CTempEnts::MuzzleFlash_357_NPC( int entityIndex, int attachmentIndex ) { FX_MuzzleEffectAttached( 0.5f, entityIndex, attachmentIndex, NULL, true ); } [/code] Открываем c_te_legacy_tempents.h Дописываем под void'ом для игрока void для персонажей: [code] // 357 void MuzzleFlash_357_Player( int entityIndex, int attachmentIndex ); void MuzzleFlash_357_NPC( int entityIndex, int attachmentIndex ); [/code] Компилируем client и server. Теперь обращаемся к игровым скриптам. Обычно лежат в ..hl2/scripts/ Внутри weapon_357.txt в "SoundData" дописываем: "single_shot_npc" "Weapon_357.NPC_Single" "reload_npc" "Weapon_357.NPC_Reload" Внутри game_sounds_weapons.txt "Weapon_357.NPC_Single" { "channel" "CHAN_WEAPON" "volume" "0.9" "soundlevel" "SNDLVL_GUNFIRE" "pitch" "68,73" "wave" "^weapons/357_fire2.wav" } "Weapon_357.NPC_Reload" { "channel" "CHAN_ITEM" "volume" "0.7" "soundlevel" "SNDLVL_NORM" "rndwave" { "wave" "weapons/357/357_reload1.wav" "wave" "weapons/357/357_reload4.wav" "wave" "weapons/357/357_reload3.wav" } } Пока всё! Используйте наздоровье! Хочу предупредить: неплохо целятся и больно бьют. skill.cfg трогать не советую, т.к. на трудном уровне сложности игрок будет наносить такой же урон, как и другие обладатели оружия. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- The messages has been download from Форумы на Наша-Life at https://bbs.dods.ru at 30.10.2024 07:12:32