Vengo a aportar esta pequeña utilidad (ni tan pequeña jajaja), para poder precachear sonidos que se encuentren escritos en los modelos.
Código PHP:
// Eventos mayores o iguales que este son manejados por el cliente *tecnicamente*
const CI_EventClient = 5000;
// La cadena a precachear (sonido), debe tener un minimo de caracteres como indica esta cadena
new const CSZ_MinLenExample[] = "a.wav";
// Versión completa de 'SequencePrecache' (HLSDK/CS-SDK)
// Precachea sonidos utilizados por modelos (para usarse por ejemplo, con 'eventos' que son manejados por el servidor)
// (Creditos a Arkshine por esto: https://forums.alliedmods.net/showpost.php?p=2270332&postcount=8 & esto https://forums.alliedmods.net/showpost.php?p=1817050&postcount=11)
UIL_PrecacheModelSequences(const SZ_ModelName[])
{
// Verificar si es valido el modelo
if (!SZ_ModelName[0] || !strlen(SZ_ModelName))
return;
// Busca el archivo y lo abre
// AVISO: Solo toma desde la carpeta root del MOD (E.X: CS 1.6 -> cstrike)
new I_File = fopen(SZ_ModelName, "rb");
// Si puede abrirse (y por ende existe)
if (I_File)
{
// Offset de "studiohdr_t->numseq" (mayor información leer "engine/studio.h" en el HLSDK)
static const CI_NumSeqOFST = 164;
// Offset de "mstudioseqdesc_t->numevents" (mayor información leer "engine/studio.h" en el HLSDK)
static const CI_NumEventsOFST = 48;
// Offset de "mstudioevent_t->event" (mayor información leer "common/studio_event.h" en el HLSDK)
static const CI_EventIDOFST = 4;
// Tamaño de "mstudioevent_t->type" (es un "int" = 4 bytes en x86 SO(s))
// Mayor información leer "common/studio_event.h" en el HLSDK
static const CI_EventTypeOFSTSize = 4;
// Tamaño de la estructura "mstudioseqdesc_t "
// Mayor información leer "engine/studio.h" en el HLSDK
static const CI_StudioSeqDescStructSize = 176;
// Tamaño de la estructura "mstudioevent_t"
// Mayor información leer "common/studio_event.h" en el HLSDK
static const CI_StudioEventStructSize = 76;
static const CSZ_LogFile[] = "PMS_Info.LOG";
new I_TotalSequences, I_SequenceIndex;
new I_TotalEvents, I_EventIndex;
new I_EventID;
new I_CurrentEvent;
static SZ_SequenceName[32];
static SZ_EventOptions[64];
// Guarda la información del modelo
log_to_file(CSZ_LogFile, "PRECACHEANDO SONIDOS USADOS POR EL MODELO: ^"%s^"^n", SZ_ModelName);
// Direcciona el cursor del archivo (modelo) a la ubicación del número de secuencias en el modelo
// El direccionamiento es desde el inicio del archivo hasta la ubicación antes mencionada
fseek(I_File, CI_NumSeqOFST, SEEK_SET);
// Lee el total de secuencias usadas por el modelo
fread(I_File, I_TotalSequences, BLOCK_INT);
// Lee el index de la primera secuencia
fread(I_File, I_SequenceIndex, BLOCK_INT);
// Guarda la información del modelo
log_to_file(CSZ_LogFile, "^tTOTAL DE SECUENCIAS: %d^n", I_TotalSequences);
// Recorre todas las secuencias del modelo
for (new I_CurrentSequence; I_CurrentSequence < I_TotalSequences; I_CurrentSequence++)
{
// Direcciona el cursor a la estructura "mstudioseqdesc_t" de la secuencia actual
// Para mayor información lean "engine/studio.h" en el HLSDK
// El direccionamiento es desde el inicio del archivo hasta la ubicación antes mencionada
fseek(I_File, (I_SequenceIndex + (I_CurrentSequence * CI_StudioSeqDescStructSize)), SEEK_SET);
// Lee el nombre de la secuencia actual
fread_blocks(I_File, SZ_SequenceName, sizeof SZ_SequenceName, BLOCK_CHAR);
// Direcciona el cursor a la ubicación del número de eventos de la secuencia actual
// El direccionamiento es desde la ubicación actual hasta la ubicación antes mencionada
fseek(I_File, (CI_NumEventsOFST - sizeof SZ_SequenceName), SEEK_CUR);
// Lee el número de eventos de la secuencia actual
fread(I_File, I_TotalEvents, BLOCK_INT);
// Guarda la información del modelo
log_to_file(CSZ_LogFile, "^t^tTOTAL DE EVENTOS DE LA SECUENCIA ^"%s^": %d", SZ_SequenceName, I_TotalEvents);
// Si la secuencia actual no tiene eventos, ignorarla
if (!I_TotalEvents)
{
if (I_TotalSequences > 1)
log_to_file(CSZ_LogFile, "^n");
continue;
}
// Lee el index del primer evento de la secuencia actual
fread(I_File, I_EventIndex, BLOCK_INT);
// Recorre todos los eventos de la secuencia actual
for (I_CurrentEvent = 0; I_CurrentEvent < I_TotalEvents; I_CurrentEvent++)
{
// Direcciona el cursor a la estructura "mstudioevent_t" del evento actual
// Para mayor información lean "common/studio_event.h" en el HLSDK
// El direccionamiento es desde el inicio del archivo hasta la ubicación antes mencionada
fseek(I_File, (I_EventIndex + (I_CurrentEvent * CI_StudioEventStructSize) + CI_EventIDOFST), SEEK_SET);
// Lee el número de evento del evento actual
fread(I_File, I_EventID, BLOCK_INT);
// Direcciona el cursor hacia el sonido del evento actual (no necesariamente, puede ser cualquier cadena que se desee)
// El direccionamiento es desde la ubicación actual hasta la ubicación antes mencionada
fseek(I_File, CI_EventTypeOFSTSize, SEEK_CUR);
// Lee el sonido del evento actual para intentar precachearlo luego
fread_blocks(I_File, SZ_EventOptions, sizeof SZ_EventOptions, BLOCK_CHAR);
// Guarda la información del modelo
log_to_file(CSZ_LogFile, "^t^t^tEVENTO #%d: ^"%s^"", I_EventID, SZ_EventOptions);
// Si no es un sonido valido no se precacheara
// Para esto: el número de evento debe estar entre los del servidor...Y...No estar vacia la cadena del sonido...Y...No ser una cadena de sentencia
if (!IsSoundEvent(I_EventID, SZ_EventOptions) || IsEmptyString(SZ_EventOptions) || IsSentenceString(SZ_EventOptions[0]))
continue;
// Si el sonido no existe, ignorar
if (!SoundExists(SZ_EventOptions))
continue;
// Guarda la información del modelo
log_to_file(CSZ_LogFile, "^t^t^tPRECACHEANDO SONIDO: ^"%s^"", SZ_EventOptions);
// Precachea el sonido (llegado a este punto el sonido debe ser valido)
precache_sound(SZ_EventOptions);
}
log_to_file(CSZ_LogFile, "^n");
}
}
// Cierra el archivo luego de leerlo
fclose (I_File)
}
// Similar a 'IsSoundEvent' (HLSDK/CS-SDK)
// Devueve VERDADERO si el sonido/evento son apropiados (o FALSO en caso contrario)
bool:IsSoundEvent(const I_EventID, const SZ_EventOptions[])
{
// Si el número de evento pasado es de los manejados por el cliente, ignorar
if (I_EventID >= CI_EventClient)
return false;
new I_Len = strlen(SZ_EventOptions);
static I_MinLen;
I_MinLen = strlen(CSZ_MinLenExample);
// Si la cadena del sonido esta vacia...O...El número de caracteres es menor del minimo requerido, ignorar
if (!SZ_EventOptions[0] || I_Len < I_MinLen)
return false;
static SZ_Sentence[5];
copy(SZ_Sentence, charsmax(SZ_Sentence), SZ_EventOptions[I_Len - 4])
// Si la cadena del sonido no tiene la sentencia ".wav" al final (es decir, como extensión), ignorar
if (!equal(SZ_Sentence, ".wav"))
return false;
// El sonido es valido, entonces debe precachearse
return true;
}
// Equivalente a 'PR_IsEmptyString' (GoldSrc/Engine)
// Devuelve VERDADERO si la cadena que se paso tiene caracteres invalidos (incluido espacio)
bool:IsEmptyString(const SZ_String[])
return SZ_String[0] <= ' ';
// Devuelve VERDADERO si la cadena que se paso es de sentencia (inicia con el caracter "!")
bool:IsSentenceString(const SZ_String[])
return SZ_String[0] == '!';
// Devuelve VERDADERO si el sonido que se pasa existe en la carpeta del MOD
bool:SoundExists(const SZ_Sound[])
{
static SZ_FullSound[64];
formatex(SZ_FullSound, charsmax(SZ_FullSound), "sound/%s", SZ_Sound);
return bool:file_exists(SZ_FullSound);
}
La forma de uso es:
Código:
UIL_PrecacheModelSequences("ubicacion_+_modelo.mdl")
OBSERVACION: Al parecer solo puede usarse en modelos que se encuentre en la carpeta raíz del MOD (por ejemplo, CS 1.6/CZero -> cstrike). Asumo que esto ya viene indicado en el AMXX.
Un uso seria precachear sonidos de un NPC (su modelo). Y luego, por ejemplo, usando las funciones que trae el MOD por defecto podemos ejecutar dichos sonidos cuando se ejecutan ciertos eventos del modelo (tal cual como sucede con los modelos View de las armas al ejecutarse en el cliente). Con lo anterior, es más fácil modificar y ejecutar los sonidos en el momento más oportuno para nosotros.
Estas funciones son: StudioFrameAdvance, DispatchAnimEvents, y HandleAnimEvent (también opcionalmente ResetSequenceInfo).
En el HLSDK pueden encontrar más información.
Si gustan puedo postear las firmas de las funciones anteriores (para usarse con Orpheu).