/*
	server/utilities/sound_helper.qc

	Logic designed to assist in audio balancing and different types
    of audio channels.

	Copyright (C) 2021-2024 NZ:P Team

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

	See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to:

		Free Software Foundation, Inc.
		59 Temple Place - Suite 330
		Boston, MA  02111-1307, USA

*/

#define SOUND_PRIORITY_ALLOWSKIP    0
#define SOUND_PRIORITY_PLAYALWAYS   1

#define SOUND_TYPE_ZOMBIE_QUIET     0
#define SOUND_TYPE_ZOMBIE_LOUD      1
#define SOUND_TYPE_ZOMBIE_CRAWLER   2
#define SOUND_TYPE_MUSIC_ROUND      3
#define SOUND_TYPE_WEAPON_FIRE      4
#define SOUND_TYPE_WEAPON_OTHER     5
#define SOUND_TYPE_WEAPON_PACK      6
#define SOUND_TYPE_WEAPON_EXPLODE   7
#define SOUND_TYPE_PLAYER_VOICE     8
#define SOUND_TYPE_PLAYER_FOOTSTEP  9
#define SOUND_TYPE_ENV_OBJECT       10
#define SOUND_TYPE_ENV_CHING        11
#define SOUND_TYPE_ENV_VOICE        12
#define SOUND_TYPE_ENV_MUSIC        13
#define SOUND_TYPE_ENV_IMPORTANT    14

#define SOUND_MAX_ZOMBIE_GROANS     4
#define SOUND_MAX_WEAPON_EXPLODE    8
#define SOUND_MAX_ENV_VOICE         1

// On FTE, we should always be using SOUNDFLAG_FOLLOW because we want
// sounds like the longer zombie groans to not feel desynconized from
// the actual action, other engines don't support this though, so wrap
// it.
#ifdef FTE

#define PLAYSOUND(ent, channel, path, volume, attenuation) sound(ent, channel, path, volume, attenuation, 0, SOUNDFLAG_FOLLOW)

#else

#define PLAYSOUND(ent, channel, path, volume, attenuation) sound(ent, channel, path, volume, attenuation)

#endif // FTE

float zombie_groan_times[SOUND_MAX_ZOMBIE_GROANS];
float weapon_explode_times[SOUND_MAX_WEAPON_EXPLODE];
float environment_voice_times[SOUND_MAX_ENV_VOICE];

float() Sound_GetEnvironmentVoice =
{
    // Search for an available Explosion slot.
    for(float i = 0; i < SOUND_MAX_ENV_VOICE; i++) {
        if (environment_voice_times[i] < time) {
            return i;
        }
    }

    // None available.
    return -1;
};

void(float priority, float index) Sound_SetEnvironmentVoice =
{
    if (priority == SOUND_PRIORITY_PLAYALWAYS)
        return;

    environment_voice_times[index] = time + 2;
};

float() Sound_GetWeaponExplosion =
{
    // Search for an available Explosion slot.
    for(float i = 0; i < SOUND_MAX_WEAPON_EXPLODE; i++) {
        if (weapon_explode_times[i] < time) {
            return i;
        }
    }

    // None available.
    return -1;
};

void(float priority, float index) Sound_SetWeaponExplosion =
{
    if (priority == SOUND_PRIORITY_PLAYALWAYS)
        return;

    weapon_explode_times[index] = time + 1.2;
};

float() Sound_GetZombieGroan =
{
    // Search for an available Groan slot.
    for(float i = 0; i < SOUND_MAX_ZOMBIE_GROANS; i++) {
        if (zombie_groan_times[i] < time) {
            return i;
        }
    }

    // None available.
    return -1;
};

void(float priority, float index) Sound_SetZombieGroan =
{
    if (priority == SOUND_PRIORITY_PLAYALWAYS)
        return;

    zombie_groan_times[index] = time + 4.5;
};

void(entity source_ent, string path, float type, float priority) Sound_PlaySound =
{
    switch(type) {
        case SOUND_TYPE_ZOMBIE_QUIET:
            float zombie_quiet_groan = Sound_GetZombieGroan();
            if (priority == SOUND_PRIORITY_PLAYALWAYS || zombie_quiet_groan != -1) {
                PLAYSOUND(source_ent, CHAN_VOICE, path, 0.50, 2);
                Sound_SetZombieGroan(priority, zombie_quiet_groan);
            }
            break;
        case SOUND_TYPE_ZOMBIE_LOUD:
            float zombie_loud_groan = Sound_GetZombieGroan();
            if (priority == SOUND_PRIORITY_PLAYALWAYS || zombie_loud_groan != -1) {
                PLAYSOUND(source_ent, CHAN_WEAPON, path, 0.70, 1);
                Sound_SetZombieGroan(priority, zombie_loud_groan);
            }
            break;
        case SOUND_TYPE_ZOMBIE_CRAWLER:
            float zombie_crawler_groan = Sound_GetZombieGroan();
            if (priority == SOUND_PRIORITY_PLAYALWAYS || zombie_crawler_groan != -1) {
                PLAYSOUND(source_ent, CHAN_ITEM, path, 0.60, 1);
                Sound_SetZombieGroan(priority, zombie_crawler_groan);
            }
            break;
        case SOUND_TYPE_MUSIC_ROUND:
            PLAYSOUND(source_ent, CHAN_ITEM, path, 0.85, 0);
            break;
        case SOUND_TYPE_WEAPON_FIRE:
            PLAYSOUND(source_ent, CHAN_AUTO, path, 0.80, 2);
            break;
        case SOUND_TYPE_WEAPON_OTHER:
            PLAYSOUND(source_ent, CHAN_ITEM, path, 0.60, 2);
            break;
        case SOUND_TYPE_WEAPON_PACK:
            PLAYSOUND(source_ent, CHAN_ITEM, path, 0.95, 2);
            break;
        case SOUND_TYPE_WEAPON_EXPLODE:
            float weapon_explosion_index = Sound_GetWeaponExplosion();
            if (priority == SOUND_PRIORITY_PLAYALWAYS || weapon_explosion_index != -1) {
                PLAYSOUND(source_ent, 0, path, 0.70, 1);
                Sound_SetWeaponExplosion(priority, weapon_explosion_index);
            }
            break;
        case SOUND_TYPE_PLAYER_VOICE:
            PLAYSOUND(source_ent, CHAN_VOICE, path, 0.90, 2);
            break;
        case SOUND_TYPE_PLAYER_FOOTSTEP:
            PLAYSOUND(source_ent, 0, path, 0.40, 3);
            break;
        case SOUND_TYPE_ENV_OBJECT:
            PLAYSOUND(source_ent, CHAN_ITEM, path, 0.50, 2);
            break;
        case SOUND_TYPE_ENV_CHING:
            PLAYSOUND(source_ent, CHAN_AUTO, path, 0.75, 3);
            break;
        case SOUND_TYPE_ENV_VOICE:
            float environment_voice_index = Sound_GetEnvironmentVoice();
            if (priority == SOUND_PRIORITY_PLAYALWAYS || environment_voice_index != -1) {
                PLAYSOUND(source_ent, CHAN_VOICE, path, 0.80, 0);
                Sound_SetEnvironmentVoice(priority, environment_voice_index);
            }
            break;
        case SOUND_TYPE_ENV_MUSIC:
            PLAYSOUND(source_ent, CHAN_WEAPON, path, 0.75, 3);
            break;
        case SOUND_TYPE_ENV_IMPORTANT:
            PLAYSOUND(source_ent, CHAN_ITEM, path, 1, 2);
            break;
        default:
            break;
    }
};
