diff --git a/README.md b/README.md index 6ffedc0..7212d31 100644 --- a/README.md +++ b/README.md @@ -103,8 +103,8 @@ Current milestones are separated by "Tiers", which are as follows: - Add build instructions. **[DONE]** - Global Model Replacement. **[DONE]** - Global Sound Replacement. **[DONE]** -- Miscellaneous customization options, such as blood color. -- Individual sound replacement: "soundlist" keyvalue for monsters. +- Miscellaneous customization options, such as blood color. **[DONE]** +- Individual sound replacement: "soundlist" keyvalue for monsters. **[DONE]** - Sentences support for speakable monsters. - Attempt to fix bugs as they appear. diff --git a/src/dlls/cmbasemonster.h b/src/dlls/cmbasemonster.h index 270c13e..cd6e2a6 100644 --- a/src/dlls/cmbasemonster.h +++ b/src/dlls/cmbasemonster.h @@ -104,6 +104,10 @@ public: string_t m_szMonsterName; // Monster name to display on HUD int m_iClassifyOverride; // Overriden classification for this monster + + REPLACER::REPLACER *m_srSoundList; // individual sound replacements for this monster + int m_isrSounds; // number of replaced sounds + float m_flLastYawTime; void KeyValue( KeyValueData *pkvd ); diff --git a/src/dlls/dllapi.cpp b/src/dlls/dllapi.cpp index 90c0f8f..9ab5958 100644 --- a/src/dlls/dllapi.cpp +++ b/src/dlls/dllapi.cpp @@ -1255,7 +1255,13 @@ int mmDispatchSpawn( edict_t *pent ) for (index = 0; index < MAX_MONSTER_ENTS; index++) { if (monsters[index].pMonster != NULL) + { + // free the soundlists first! + if (monsters[index].pMonster->m_srSoundList != NULL) + free(monsters[index].pMonster->m_srSoundList); + delete monsters[index].pMonster; + } } // free any allocated keyvalue memory diff --git a/src/dlls/globalreplace.cpp b/src/dlls/globalreplace.cpp index 28d02ac..5e98afb 100644 --- a/src/dlls/globalreplace.cpp +++ b/src/dlls/globalreplace.cpp @@ -12,12 +12,6 @@ namespace REPLACER { -typedef struct -{ - char source[128]; - char destination[128]; -} REPLACER; - REPLACER *GMR; REPLACER *GSR; int numModels; @@ -77,6 +71,32 @@ bool AddGlobalSound(const char *from, const char *to) return false; } +bool AddIndividualSound(edict_t *pMonster, const char *from, const char *to) +{ + CMBaseMonster *castMonster = GetClassPtr((CMBaseMonster *)VARS(pMonster)); + + if ( castMonster == NULL ) + return false; + + int m_iSounds = castMonster->m_isrSounds; + + if (m_iSounds < MAX_REPLACEMENTS) + { + // allocate for the first time + if (!m_iSounds) + castMonster->m_srSoundList = (REPLACER*)calloc(MAX_REPLACEMENTS, sizeof(*castMonster->m_srSoundList)); + + strcpy(castMonster->m_srSoundList[m_iSounds].source, from); + strcpy(castMonster->m_srSoundList[m_iSounds].destination, to); + + castMonster->m_isrSounds++; + return true; + } + + LOG_MESSAGE(PLID, "Can't replace sound '%s', too many sounds in list.", from); + return false; +} + const char* FindModelReplacement( edict_t *pMonster, const char *from ) { // Individually set models takes priority! @@ -103,14 +123,26 @@ const char* FindSoundReplacement( edict_t *pMonster, const char *from ) if ( pMonster ) { CMBaseMonster *castMonster = GetClassPtr((CMBaseMonster *)VARS(pMonster)); - //placeholder for soundlist keyvalue; + if (castMonster->m_isrSounds) + { + for (int sound = 0; sound < castMonster->m_isrSounds; sound++) + { + if (strcmp(castMonster->m_srSoundList[sound].source, from) == 0) + { + // If found, use it + return castMonster->m_srSoundList[sound].destination; + } + } + + // If nothing is found stick to GSR if available + } } for (int sound = 0; sound < numSounds; sound++) { if (strcmp(GSR[sound].source, from) == 0) { - // If found, use that model instead + // If found, use that sound instead return GSR[sound].destination; } } diff --git a/src/dlls/globalreplace.h b/src/dlls/globalreplace.h index 3be64d9..c9f8b1b 100644 --- a/src/dlls/globalreplace.h +++ b/src/dlls/globalreplace.h @@ -5,9 +5,16 @@ namespace REPLACER { +typedef struct +{ + char source[128]; + char destination[128]; +} REPLACER; + void Init(void); bool AddGlobalModel(const char *from, const char *to); bool AddGlobalSound(const char *from, const char *to); +bool AddIndividualSound(edict_t *pMonster, const char *from, const char *to); const char* FindModelReplacement( edict_t *pMonster, const char *from ); inline const char* FindModelReplacement( const char *from ) { return FindModelReplacement( NULL, from ); } diff --git a/src/dlls/monster_config.cpp b/src/dlls/monster_config.cpp index 30a5b0d..7a2a17f 100644 --- a/src/dlls/monster_config.cpp +++ b/src/dlls/monster_config.cpp @@ -68,6 +68,80 @@ bool get_input(FILE *fp, char *input) return FALSE; // no input found } +void scan_monster_sound(FILE *fp, edict_t *pMonster ) +{ + char input[1024]; + + while (get_input(fp, input)) + { + char *source = strtok(input, " "); + char *destination = strtok(NULL, " "); + + // Remove all quotes + char parse[128] = {0}; + int skip; + + // source + skip = 0; + for (unsigned i = 0; i < strlen(source); i++) + { + if (source[i] == '"') + { + skip++; + continue; + } + parse[i-skip] = source[i]; + } + parse[strlen(parse)] = '\0'; + strcpy(source, parse); + + // destination + memset(parse, 0, sizeof(parse)); + skip = 0; + for (unsigned i = 0; i < strlen(destination); i++) + { + if (destination[i] == '"') + { + skip++; + continue; + } + parse[i-skip] = destination[i]; + } + parse[strlen(parse)] = '\0'; + strcpy(destination, parse); + + if ( pMonster ) + REPLACER::AddIndividualSound( pMonster, source, destination ); + else + PRECACHE_SOUND2(destination); + } +} + +void process_monster_sound(edict_t *pMonster, char *fileName) +{ + char game_dir[256]; + FILE *fp = NULL; + + // find the directory name of the currently running MOD... + (*g_engfuncs.pfnGetGameDir)(game_dir); + + char srPath[192]; + + // SC soundlist path starts from sound/MAPNAME + sprintf(srPath, "%s/sound/%s/%s", game_dir, STRING(gpGlobals->mapname), fileName); + + if (access(srPath, 0) == 0) + { + if ((fp = fopen(srPath, "r")) != NULL) + { + scan_monster_sound(fp, pMonster); + fclose(fp); + } + } + + return; +} + void scan_monster_cfg(FILE *fp) { // Let's make a full rework of this. -Giegue @@ -277,6 +351,22 @@ void scan_monster_cfg(FILE *fp) } } } + else if (strcmp(data[i].key, "soundlist") == 0) + { + if (monster) + { + // file handling, string must not be empty + if (strlen(data[i].value)) + { + // process and precache the replacement sounds + process_monster_sound( NULL, data[i].value ); + + // the entity will need the keyvalue + strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].key, data[i].key); + strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].value, data[i].value); + } + } + } else if (strcmp(data[i].key, "new_model") == 0) { if (monster) @@ -640,6 +730,22 @@ void scan_monster_bsp(void) } } } + else if (strcmp(data[i].key, "soundlist") == 0) + { + if (monster) + { + // file handling, string must not be empty + if (strlen(data[i].value)) + { + // process and precache the replacement sounds + process_monster_sound( NULL, data[i].value ); + + // the entity will need the keyvalue + strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].key, data[i].key); + strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].value, data[i].value); + } + } + } else if (strcmp(data[i].key, "new_model") == 0) { if (monster) @@ -946,7 +1052,6 @@ bool process_monster_cfg(void) return FALSE; // all ok } - bool scan_monster_precache_cfg(FILE *fp) { char input[1024]; diff --git a/src/dlls/monstermaker.cpp b/src/dlls/monstermaker.cpp index c2b2d3f..210adb5 100644 --- a/src/dlls/monstermaker.cpp +++ b/src/dlls/monstermaker.cpp @@ -254,6 +254,17 @@ void CMMonsterMaker::MakeMonster( void ) pent->v.renderamt = pev->renderamt; pent->v.rendercolor = pev->rendercolor; + // Soundlist isn't "exactly" a keyvalue so pass it here + if ( m_srSoundList != NULL ) + { + // it needs to be allocated first + CMBaseMonster *pChild = GetClassPtr((CMBaseMonster *)VARS(pent)); + pChild->m_srSoundList = (REPLACER::REPLACER*)calloc(MAX_REPLACEMENTS, sizeof(*pChild->m_srSoundList)); + + memcpy(pChild->m_srSoundList, m_srSoundList, sizeof(REPLACER::REPLACER)); + pChild->m_isrSounds = m_isrSounds; + } + m_cLiveChildren++;// count this monster m_cNumMonsters--; diff --git a/src/dlls/monsters.cpp b/src/dlls/monsters.cpp index 1b10c63..7afb3f8 100644 --- a/src/dlls/monsters.cpp +++ b/src/dlls/monsters.cpp @@ -41,7 +41,7 @@ extern CGraph WorldGraph;// the world node graph extern cvar_t *monster_turn_coeficient; - +extern void process_monster_sound(edict_t *pMonster, char *fileName); //========================================================= // Eat - makes a monster full for a little while. @@ -2643,6 +2643,14 @@ void CMBaseMonster :: KeyValue( KeyValueData *pkvd ) } pkvd->fHandled = TRUE; } + else if (FStrEq(pkvd->szKeyName, "soundlist")) + { + if (strlen( pkvd->szValue )) + { + process_monster_sound(edict(), pkvd->szValue); + } + pkvd->fHandled = TRUE; + } else { CMBaseToggle::KeyValue( pkvd );