/*
* Seven Kingdoms: Ancient Adversaries
*
* Copyright 1997,1998 Enlight Software Ltd.
*
* 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, see .
*
*/
// Filename : OSERES.CPP
// Description : Sound resource
// search a sound effect id from subject verb object
// pass the id to se_ctrl.request to trigger the sound effect
// Owner : Gilbert
#include
#include
#include
#include
#include
#include
#include
// ---------- Define constant ----------//
#define SERES_DB "SOUNDRES"
unsigned long SERes::last_select_time = 0;
unsigned long SERes::last_command_time = 0;
unsigned long SERes::select_sound_length = 600; // time between successive select sound
// --------- begin of function SEInfo::match ----------//
int SEInfo::match(char subjectType, short subjectId, char *act,
char objectType, short objectId)
{
// object_type == -1, match all object type
// object_id == -1, match all object id of that type
return subject_type == subjectType &&
(subject_id == -1 || subject_id == subjectId) &&
strncmp(action, act, VERB_LEN )== 0 &&
(object_type == -1 || (object_type == objectType &&
(object_id == -1 || object_id == objectId)));
}
// --------- end of function SEInfo::match ----------//
// --------- begin of function SERes::SERes ----------//
SERes::SERes()
{
init_flag = 0;
se_array_count = 0;
se_array = NULL;
se_index_count = 0;
se_index_array = NULL;
type_index_count = 0;
type_index_array = NULL;
}
// --------- end of function SERes::SERes ----------//
// --------- begin of function SERes::~SERes ----------//
SERes::~SERes()
{
deinit();
}
// --------- end of function SERes::~SERes ----------//
// --------- begin of function SERes::init1 ----------//
void SERes::init1()
{
deinit();
seed = m.get_time();
load_info();
sort_info();
build_index();
init_flag = 1;
}
// --------- end of function SERes::init1 ----------//
// --------- begin of function SERes::init2 ----------//
// called after se_ctrl is init
void SERes::init2(SECtrl *seCtrl)
{
err_when( !seCtrl || !seCtrl->init_flag );
err_when( init_flag == 0 );
se_output = seCtrl;
int i;
SEInfo *seInfo;
for(i = 0, seInfo = se_array; i < se_array_count; ++i, ++seInfo )
{
seInfo->effect_id = seCtrl->search_effect_id(seInfo->file_name);
}
init_flag = 2;
}
// --------- end of function SERes::init2 ----------//
// --------- begin of function SERes::deinit ----------//
void SERes::deinit()
{
if( init_flag )
{
mem_del(se_array);
se_array = NULL;
se_array_count = 0;
mem_del(se_index_array);
se_index_array = NULL;
se_index_count = 0;
mem_del(type_index_array);
type_index_array = NULL;
type_index_count = 0;
init_flag = 0;
}
}
// --------- end of function SERes::deinit ----------//
// --------- begin of function SERes::load_info ----------//
void SERes::load_info()
{
SERec *seRec;
SEInfo *seInfo;
int i;
Database *dbSE = game_set.open_db(SERES_DB);
se_array_count = dbSE->rec_count();
se_array = (SEInfo *)mem_add(sizeof(SEInfo) * se_array_count);
memset( se_array, 0, sizeof(SEInfo) * se_array_count );
for( i = 0; i < se_array_count; ++i )
{
seRec = (SERec *) dbSE->read(i+1);
seInfo = se_array+i;
// ------- copy subject ---------//
seInfo->subject_type = seRec->subject_type;
seInfo->subject_id = m.atoi(seRec->subject_id, seRec->RECNO_LEN);
// -------- copy verb ---------//
memcpy( seInfo->action, seRec->action, seRec->VERB_LEN );
seInfo->action[seInfo->VERB_LEN] = '\0';
m.rtrim( seInfo->action );
// --------- copy object ---------//
if( seRec->object_type == ' ' || seRec->object_type == '\0')
{
seInfo->object_type = 0;
seInfo->object_id = 0;
}
else if( seRec->object_type == '*' )
{
seInfo->object_type = -1; // all object
seInfo->object_id = -1;
}
else
{
seInfo->object_type = seRec->object_type;
if( seRec->object_id[0] != '*' )
seInfo->object_id = m.atoi(seRec->object_id, seRec->RECNO_LEN);
else
seInfo->object_id = -1; // all of the objectType
}
// -------- copy out frame ---------//
seInfo->out_frame = m.atoi(seRec->out_frame, seRec->OUT_FRAME_LEN);
err_when(seInfo->out_frame <= 0);
// -------- copy file name --------//
memcpy(seInfo->file_name, seRec->file_name, seRec->FILE_NAME_LEN);
seInfo->file_name[seInfo->FILE_NAME_LEN] = '\0';
m.rtrim(seInfo->file_name);
seInfo->effect_id = 0;
}
}
// --------- end of function SERes::load_info ----------//
// --------- begin of function SERes::sort_info --------//
static int seinfo_cmp(const void *r1, const void *r2)
{
return memcmp(r1, r2, sizeof(SEInfo));
}
void SERes::sort_info()
{
// notice object_id -1 are put after any other object_id
qsort(se_array, se_array_count, sizeof(SEInfo), seinfo_cmp);
}
// --------- end of function SERes::sort_info --------//
// --------- begin of function SERes::build_index -------//
// build index on (subject_type, subject_id)
void SERes::build_index()
{
// ---------- first pass, count the size of index ---------//
int i,j,k;
SEInfo *seInfo;
char lastType = -1;
short lastId;
type_index_count = 0;
se_index_count = 0;
for(i = 0, seInfo = se_array; i < se_array_count; ++i, ++seInfo)
{
if( lastType != seInfo->subject_type)
{
type_index_count++;
se_index_count++;
lastType = seInfo->subject_type;
lastId = seInfo->subject_id;
}
else if( lastId != seInfo->subject_id)
{
se_index_count++;
lastId = seInfo->subject_id;
}
}
// --------- allocate memory for index ----------//
SEInfoIndex *seIndex = se_index_array = (SEInfoIndex *)
mem_resize( se_index_array, sizeof(SEInfoIndex) * se_index_count);
memset( se_index_array, 0, sizeof(SEInfoIndex) * se_index_count);
SETypeIndex *typeIndex = type_index_array = (SETypeIndex *)
mem_resize( type_index_array, sizeof(SETypeIndex) * type_index_count);
memset( type_index_array, 0, sizeof(SETypeIndex) * type_index_count);
// ---------- pass 2, build indices -----------//
seIndex--; // move one step backward
typeIndex--;
j = -1;
k = -1;
lastType = -1;
for(i = 0, seInfo = se_array; i < se_array_count; ++i, ++seInfo)
{
if( lastType != seInfo->subject_type)
{
// ----------- new (type,Id) ---------//
++seIndex;
++j;
seIndex->subject_type = seInfo->subject_type;
seIndex->subject_id = seInfo->subject_id;
seIndex->start_rec = seIndex->end_rec = i;
// ----------- new type ---------//
++typeIndex;
++k;
typeIndex->subject_type = seInfo->subject_type;
typeIndex->start_rec = typeIndex->end_rec = j;
lastType = seInfo->subject_type;
lastId = seInfo->subject_id;
}
else
{
err_when(typeIndex < type_index_array); // must not enter here for the first time
if( lastId != seInfo->subject_id)
{
// ----------- new (type,Id) ---------//
++seIndex;
++j;
seIndex->subject_type = seInfo->subject_type;
seIndex->subject_id = seInfo->subject_id;
seIndex->start_rec = i;
seIndex->end_rec = i;
lastId = seInfo->subject_id;
}
else
{
err_when(seIndex < se_index_array);
seIndex->end_rec = i;
}
typeIndex->end_rec = j;
}
}
}
// --------- end of function SERes::build_index -------//
// --------- begin of function SERes::scan ----------//
// search the SEInfo whose subject, action and object match
// subjectType type of subject 'S'= sprite,
// 'U'=unit, 'R'=race, 'F'=firm, 'T'=town
// subjectId sprite_id, unit_id, race_id or firm_id ...
// act name of the action, first four chars are significant
// [char] objectType type of object 'S'= sprite,
// 'U'=unit, 'R'=race, 'F'=firm, 'T'=town
// default : 0 (none)
// [short] objectId sprite_id, unit_id, race_id or firm_id ...
// default : 0 (none)
//
// return NULL if not found
//
SEInfo* SERes::scan(char subjectType, short subjectId, char *act,
char objectType, short objectId, int findFirst)
{
err_when(!init_flag);
int startRec, endRec, i;
SETypeIndex *typeIndex;
SEInfoIndex *seIndex;
SEInfo *seInfo;
// ---------- search the type_index_array ---------//
int foundFlag = 0;
for( i = 0, typeIndex = type_index_array; i < type_index_count; ++i, ++typeIndex)
{
if( subjectType == typeIndex->subject_type)
{
startRec = typeIndex->start_rec;
endRec = typeIndex->end_rec;
foundFlag = 1;
break;
}
}
if( !foundFlag )
return NULL;
// ---------- search the se_index_array ---------//
foundFlag = 0;
for( i = startRec, seIndex = se_index_array + startRec; i <= endRec;
++i, ++seIndex)
{
if( subjectId == seIndex->subject_id && subjectType == seIndex->subject_type)
{
startRec = seIndex->start_rec;
endRec = seIndex->end_rec;
foundFlag = 1;
break;
}
}
if( !foundFlag ) // not found
return NULL;
// ----------- search the se_array ----------//
for(i = startRec, seInfo = se_array + startRec; i <= endRec;
++i, ++seInfo)
{
if( seInfo->match(subjectType, subjectId, act, objectType, objectId) )
break;
}
if( i <= endRec ) // found
{
if( findFirst )
return seInfo;
int found = 1;
SEInfo *retSEInfo = seInfo;
for( ++i, ++seInfo ; i <= endRec && found <= 4 &&
seInfo->match(subjectType, subjectId, act, objectType, objectId);
++i, ++seInfo )
{
++found;
if( random(found+1) == 0)
retSEInfo = seInfo;
}
return retSEInfo;
}
return NULL;
}
// --------- end of function SERes::scan ----------//
// --------- begin of function SERes::scan_id ----------//
short SERes::scan_id(char subjectType, short subjectId, char *act,
char objectType, short objectId, int findFirst)
{
SEInfo *seInfo;
if( (seInfo = scan(subjectType, subjectId, act, objectType, objectId, findFirst))
!= NULL)
return seInfo->effect_id;
return 0;
}
// --------- end of function SERes::scan_id ----------//
// --------- begin of function SERes::operator[] ----------//
SEInfo* SERes::operator[] (int i)
{
err_when(!init_flag || i <= 0 || i > se_array_count);
return se_array + i - 1;
}
// --------- end of function SERes::operator[] ----------//
// --------- begin of function SERes::sound ----------//
void SERes::sound(short xLoc, short yLoc, short frame,
char subjectType,short subjectId, char *action, char objectType,short objectId)
{
//### begin trevor 20/8 ###//
if( !config.sound_effect_flag )
return;
//### end trevor 20/8 ###//
short relXLoc = xLoc - (world.zoom_matrix->top_x_loc + world.zoom_matrix->disp_x_loc/2);
short relYLoc = yLoc - (world.zoom_matrix->top_y_loc + world.zoom_matrix->disp_y_loc/2);
PosVolume posv(relXLoc, relYLoc);
RelVolume relVolume( posv );
if( !config.pan_control )
relVolume.ds_pan = 0;
SEInfo *seInfo;
if( relVolume.rel_vol < 5)
return;
if( (seInfo=scan(subjectType, subjectId, action, objectType, objectId))
!= NULL && frame == seInfo->out_frame)
{
se_output->request(seInfo->effect_id, relVolume );
}
}
// --------- end of function SERes::sound ----------//
// --------- begin of function SERes::far_sound ----------//
//
// as same as far_sound, but no cut_off volume
// usually used in acknowlege voice
//
void SERes::far_sound(short xLoc, short yLoc, short frame,
char subjectType,short subjectId, char *action, char objectType,short objectId)
{
//### begin trevor 20/8 ###//
if( !config.sound_effect_flag )
return;
//### end trevor 20/8 ###//
short relXLoc = xLoc - (world.zoom_matrix->top_x_loc + world.zoom_matrix->disp_x_loc/2);
short relYLoc = yLoc - (world.zoom_matrix->top_y_loc + world.zoom_matrix->disp_y_loc/2);
PosVolume posv(relXLoc, relYLoc);
RelVolume relVolume( posv, 200, MAX_MAP_WIDTH );
if( !config.pan_control )
relVolume.ds_pan = 0;
SEInfo *seInfo;
if( relVolume.rel_vol < 80)
relVolume.rel_vol = 80;
if( (seInfo=scan(subjectType, subjectId, action, objectType, objectId))
!= NULL && frame == seInfo->out_frame)
{
se_output->request(seInfo->effect_id, relVolume );
}
}
// --------- end of function SERes::far_sound ----------//
// --------- begin of function SERes::mark_select_object_time ----------//
int SERes::mark_select_object_time() // return false if this sound should be skipped due to too frequent
{
unsigned long t = m.get_time();
if( t - last_select_time >= select_sound_length )
{
last_select_time = t;
return 1;
}
return 0;
}
// --------- end of function SERes::mark_select_object_time ----------//
// --------- begin of function SERes::mark_select_object_time ----------//
int SERes::mark_command_time() // return false if this sound should be skipped due to too frequent
{
unsigned long t = m.get_time();
// if( t - last_command_time >= select_sound_length )
// {
// last_command_time = t;
// return 1;
// }
if( t - last_select_time >= select_sound_length )
{
last_select_time = t;
return 1;
}
return 0;
}
// --------- end of function SERes::mark_select_object_time ----------//
//-------------- Begin Function SERes::random ---------//
unsigned SERes::random(unsigned bound)
{
#define MULTIPLIER 0x015a4e35L
#define INCREMENT 1
seed = MULTIPLIER * seed + INCREMENT;
return seed % bound;
}
//-------------- End Function SERes::random ---------//