목요일, 10월 19, 2006

Purpose: 게임내에서 wave 파일을 재생하자!

Purpose: 게임내에서 wave 파일을 재생하자!
Date: July. 21. 2004
Source: 모드나라 (http://mod.zoa.to)
Author: godmode2k (godmode2k@hotmail.com | MSN IM)
HL1 SDK: 2.3


Description:
가끔 국내 CS 1.5 서버에 들어가 보면(외국은 모르겠습니다만,) 채팅창에 어떤 글을 써 넣으면 재미있는 목소리가 나오지요?
물론 Admin MOD (?) 란 프로그램을 사용하면 된다고 알고는 있습니다만, 저는 직접해보기로 했습니다.

나중에 분명 wave 파일을 재생할 날이 있을꺼란 생각에 테스트겸 해봅니다.

저는 HL1 SDK 코드를 잘 알지는 못합니다만, 한나절 투자끝에 되는군요...
wave 출력해주는 함수는 player.cpp 파일을 참고했습니다.
의외로 player.cpp 파일은 참고할 만한 코드가 상당히 많습니다.



[ 주의 ]
* 여기에서의 라디오 메시지는 자신의 PC에서 밖엔 소리가 나지 않습니다.
code를 추가해 줄 때,

// slc 시작 [
// slc 끝 ]

사이 부분의 code를 추가해 주시면 됩니다.

그럼 시작하겠습니다. ^___^


STEP 1:
play 가능한 wav 파일을
C:/Games/Sierra/Steam/SteamApps/Steam_ID(스팀 아이디)/half-life/My_MOD_NAME(나의 모드)/sound 안에 넣어줍니다.
여기에선 sound/comic/ 에 wav 파일을 넣고 테스트를 할 것입니다.

지료실에 사용된 wave 파일을 올려 놓았습니다.
파일명: comic.zip
이 code에서 사용한 wave 파일들 입니다.
wave 파일은 제가 CS 1.5 할 당시 Ansan SPEED-UP PC방 서버에서 사용했던 wave 파일들입니다.
sound/misc 쪽에 다운로드 되어있더군요...

STEP 2:
wav 파일을 준비가 끝났으면 코드 작업으로 들어가 보겠습니다.
hl project에서 client.cpp 파일을 열고 전역변수로 아래와 같이 wav 파일 리스트를 만들어줍니다.

작업위치: "hl files"->client.cpp

// slc 시작 [
// nfo: slc is "sound_list_comic"
// source: Ansan SPEED-UP PC-ROOM from Ansan, South of Korea
char *slc[58][2] = {
{"back1", "comic/back1.wav"},
{"back2", "comic/back2.wav"},
{"back3", "comic/back3.wav"},
{"camp1", "comic/camp1.wav"},
{"camp2", "comic/camp2.wav"},
{"cmj2", "comic/cmj2.wav"},
{"cmj3", "comic/cmj3.wav"},
{"come1", "comic/come1.wav"},
{"come2", "comic/come2.wav"},
{"haha1", "comic/haha1.wav"},
{"haha2", "comic/haha2.wav"},
{"haha3", "comic/haha3.wav"},
{"haha4", "comic/haha4.wav"},
{"jj3", "comic/jj3.wav"},
{"jj4", "comic/jj4.wav"},
{"jja1", "comic/jja1.wav"},
{"jja2", "comic/jja2.wav"},
{"juk1", "comic/juk1.wav"},
{"kmulti", "comic/kmulti.wav"},
{"ne1", "comic/ne1.wav"},
{"ne2", "comic/ne2.wav"},
{"nol1", "comic/nol1.wav"},
{"nol2", "comic/nol2.wav"},
{"obba", "comic/obba.wav"},
{"oh1", "comic/oh1.wav"},
{"oh2", "comic/oh2.wav"},
{"oh3", "comic/oh3.wav"},
{"oh4", "comic/oh4.wav"},
{"sap1", "comic/sap1.wav"},
{"good1", "comic/good1.wav"},
{"move01", "comic/move01.wav"},
{"move02", "comic/move02.wav"},
{"new_camp1", "comic/new_camp1.wav"},
{"new_camp2", "comic/new_camp2.wav"},
{"new_camp3", "comic/new_camp3.wav"},
{"new_dak1", "comic/new_dak1.wav"},
{"new_dak2", "comic/new_dak2.wav"},
{"new_dak3", "comic/new_dak3.wav"},
{"new_db1", "comic/new_db1.wav"},
{"new_db2", "comic/new_db2.wav"},
{"new_haha1", "comic/new_haha1.wav"},
{"new_haha2", "comic/new_haha2.wav"},
{"new_haha3", "comic/new_haha3.wav"},
{"new_jja1", "comic/new_jja1.wav"},
{"new_jja2", "comic/new_jja2.wav"},
{"new_jja3", "comic/new_jja3.wav"},
{"new_mo1", "comic/new_mo1.wav"},
{"new_mo2", "comic/new_mo2.wav"},
{"new_ne1", "comic/new_ne1.wav"},
{"new_ne2", "comic/new_ne2.wav"},
{"new_ne3", "comic/new_ne3.wav"},
{"new_oh1", "comic/new_oh1.wav"},
{"new_oh2", "comic/new_oh2.wav"},
{"new_oh3", "comic/new_oh3.wav"},
{"new_zot1", "comic/new_zot1.wav"},
{"new_zot2", "comic/new_zot2.wav"},
{"sprayer", "comic/sprayer.wav"},
{'\0', '\0'}
};
// slc 끝 ]


STEP 3:
이젠 리스트에 있는 wav 파일을 play 해주는 code를 작성해 보겠습니다.

작업위치: "hl files"->client.cpp, go to line 224

약 224 line을 보게 되면 몇 line 윗쪽에 아래와 같은 함수를 볼 수 있습니다.
void Host_Say( edict_t *pEntity, int teamonly )
{

우리는 이 함수 안에 code를 작성할 것입니다.
이 함수 시작 위치에서 (224 line) 지역변수가 선언된 곳에 아래의 code를 작성해 줍니다.

...
const char *cpSayTeam = "say_team";
const char *pcmd = CMD_ARGV(0);

// slc 시작 [
char get_text[128] = {0}; // 채팅창에 쓴 text 원본을 가져오기
// slc 끝 ]

// We can get a raw string now, without the "say " prepended
if ( CMD_ARGC() == 0 )
return;
...


아래로 65 line을 보면 다음과 같은 code가 있습니다. 추가해 줍니다.
...
strcat( text, p );
strcat( text, "\n" );

// slc 시작 [
// 변경된 값이 아닌 원본을 copy
strcpy( get_text, p );
get_text[sizeof(get_text)+1] = '\0';
// slc 끝 ]


player->m_flNextChatTime = gpGlobals->time + CHAT_INTERVAL;
...

아래로 39 line 정도 내려가면 다음과 같은 코드가 있습니다. 추가해 줍니다.

// echo to server console
g_engfuncs.pfnServerPrint( text );


// slc 시작 [
//
// Purpose: Sound 출력
// nfo: sound 출력시 edict_t 나 entvars_t의 pointer를 얻어와야 한다.
// i.e.,: edict_t *pev; or entvars_t *pev;
// EMIT_SOUND(ENT(pev), CHAN_VOICE, "comic/new_ne2.wav", 1, ATTN_NORM);
//
ClientPrint(pev, MSG_ONE, "Preparing to play sound...\n");
for( int slc_i=0; slc[slc_i][1] != '\0'; slc_i++ )
{
if( !strcmp( get_text, slc[slc_i][0] ) )
{
ClientPrint(pev, MSG_ONE, "Playing sound...\n");
ClientPrint(pev, MSG_ONE, "get_text(.wav file) = %s\n", (char *)&get_text);

EMIT_SOUND(ENT(pev), CHAN_VOICE, slc[slc_i][1], 1, ATTN_NORM);
}
}
//
// slc 끝 ]


char * temp;
if ( teamonly )
temp = "say_team";


STEP 4:
어떠한 wav 파일들을 play 하기 위해서는 반드시 Precache 되어야 합니다.
내 맘대로 편하게 하면 좋겠지만, 규칙을 따르지요. 당연한거 아닌가요? ㅎㅎ

작업위치: "hl files"->client.cpp, go to line 699

STEP 3 까지 작업이 끝났다면 그 client.cpp code에서 약 699 line으로 내려가면 다음과 같은 함수가 있습니다.

void ClientPrecache( void )
{

이 함수가 wav 파일을 Precache 해주는 함수입니다.
따라서 여기에서 사용할 모든 wav 파일을 지정해 줍니다.
우리는 제일 끝 부분에 추가를 해주겠습니다.

다시 한번 말씀 드리지만, 여기에서의 wav 파일 경로는,
C:/Games/Sierra/Steam/SteamApps/Steam_ID(스팀 아이디)/half-life/My_MOD_NAME(나의 모드)/sound 입니다.
즉, sound/comic/ 에 wav 파일을 넣고 테스트를 합니다.
때문에 PRECACHE_SOUND(" xxx.wav "); .wav 확장자를 포함한 wave 파일 이름의 경로를 정확히 지정해야 합니다.

여기에선 sound/comic/에 wav 파일이 있기 때문에 다음과 같이 합니다.
PRECACHE_SOUND("comic/new_ne2.wav");

모든 wav 파일은 sound 디렉토리 안에서 찾기 때문에 "sound/" 는 빼는것에 주의!
만약 sound/test/my_sound 라면,
PRECACHE_SOUND("test/my_sound/new_ne2.wav"); 이렇게 하면 됩니다.

이제 아래와 같이 code를 추가해 줍니다.

...
PRECACHE_SOUND("player/geiger1.wav");

// slc 시작 [
// nfo: gcf 파일이라고 해도 MOD 디렉토리에 sound 디렉토리가 있다면 그곳도 참조한다.
PRECACHE_SOUND("comic/back1.wav");
PRECACHE_SOUND("comic/back2.wav");
PRECACHE_SOUND("comic/back3.wav");
PRECACHE_SOUND("comic/camp1.wav");
PRECACHE_SOUND("comic/camp2.wav");
PRECACHE_SOUND("comic/cmj2.wav");
PRECACHE_SOUND("comic/cmj3.wav");
PRECACHE_SOUND("comic/come1.wav");
PRECACHE_SOUND("comic/come2.wav");
PRECACHE_SOUND("comic/haha1.wav");
PRECACHE_SOUND("comic/haha2.wav");
PRECACHE_SOUND("comic/haha3.wav");
PRECACHE_SOUND("comic/haha4.wav");
PRECACHE_SOUND("comic/jj3.wav");
PRECACHE_SOUND("comic/jj4.wav");
PRECACHE_SOUND("comic/jja1.wav");
PRECACHE_SOUND("comic/jja2.wav");
PRECACHE_SOUND("comic/juk1.wav");
PRECACHE_SOUND("comic/kmulti.wav");
PRECACHE_SOUND("comic/ne1.wav");
PRECACHE_SOUND("comic/ne2.wav");
PRECACHE_SOUND("comic/nol1.wav");
PRECACHE_SOUND("comic/nol2.wav");
PRECACHE_SOUND("comic/obba.wav");
PRECACHE_SOUND("comic/oh1.wav");
PRECACHE_SOUND("comic/oh2.wav");
PRECACHE_SOUND("comic/oh3.wav");
PRECACHE_SOUND("comic/oh4.wav");
PRECACHE_SOUND("comic/sap1.wav");
PRECACHE_SOUND("comic/good1.wav");
PRECACHE_SOUND("comic/move01.wav");
PRECACHE_SOUND("comic/move02.wav");
PRECACHE_SOUND("comic/new_camp1.wav");
PRECACHE_SOUND("comic/new_camp2.wav");
PRECACHE_SOUND("comic/new_camp3.wav");
PRECACHE_SOUND("comic/new_dak1.wav");
PRECACHE_SOUND("comic/new_dak2.wav");
PRECACHE_SOUND("comic/new_dak3.wav");
PRECACHE_SOUND("comic/new_db1.wav");
PRECACHE_SOUND("comic/new_db2.wav");
PRECACHE_SOUND("comic/new_haha1.wav");
PRECACHE_SOUND("comic/new_haha2.wav");
PRECACHE_SOUND("comic/new_haha3.wav");
PRECACHE_SOUND("comic/new_jja1.wav");
PRECACHE_SOUND("comic/new_jja2.wav");
PRECACHE_SOUND("comic/new_jja3.wav");
PRECACHE_SOUND("comic/new_mo1.wav");
PRECACHE_SOUND("comic/new_mo2.wav");
PRECACHE_SOUND("comic/new_ne1.wav");
PRECACHE_SOUND("comic/new_ne2.wav");
PRECACHE_SOUND("comic/new_ne3.wav");
PRECACHE_SOUND("comic/new_oh1.wav");
PRECACHE_SOUND("comic/new_oh2.wav");
PRECACHE_SOUND("comic/new_oh3.wav");
PRECACHE_SOUND("comic/new_zot1.wav");
PRECACHE_SOUND("comic/new_zot2.wav");
PRECACHE_SOUND("comic/sprayer.wav");
// slc 끝 ]

if (giPrecacheGrunt)
UTIL_PrecacheOther("monster_human_grunt");
}


STEP 5:
이제는 마지막으로 테스트만 하면 되겠습니다.
STEAM 으로 자신의 MOD를 실행하고 play 상태에서 chat 명령인 'y' 를 누르면
"say: " 이렇게 나옵니다.

이때 char *scl[y][x] = { {"wav_name", "wav_file.wav" }; 에서 정의 해줬던 이름을 적어줍니다.
여기에선 "back1"을 하겠습니다.

즉, "say: back1" 이라고 하면,
comic/back1.wav 파일이 재생 됩니다.

여기까지 입니다.
수고 하셨습니다.

오류 정정은 아래의 email로 보내주세요.

godmode2k@hotmail.com (MSN IM)

이상입니다.

- godmode2k

댓글 없음:

댓글 쓰기