Handling Bartering
Previously you read about how
the barter_frame state is used to render the bartering
scene in which the player can buy items from a character.
How does that state know what
items to sell? The only way the game initiates the
bartering state is when a script triggers it via the Barter-with-Character
script action.
That action, in turn, calls cApp::setup_barter, which configures the information
needed for the barter_frame function. This information includes the character
that is
selling the items, as well as the filename of the character inventory control
system
(ICS) item file:
void cApp::setup_barter(const char* ics_file)
{
strcpy(g_barter_ics_file, ics_file);
m_state_manager.push(barter_frame, this);
}
The barter_frame state function
scans the ICS that was loaded, displaying every item
contained with the character’s inventory list on the screen. If the player
clicks an
item and the player has the appropriate amount of money, that item is bought.
Once the player finishes dealing with the shopkeeper, the barter state is popped
from the state stack, and game-play returns.
Playing Sounds and
Music
Music and other sounds are
played during the game. Those game sounds, although
somewhat cheesy (as you can tell, I’m no recording artist!), are played by a
call to
play_sound. The only argument to play_sound is an index number to an array of
sound
files that you declare at the beginning of the application code:
To play one of the valid
sounds, you use the following function:
void cApp::play_sound(long index)
{
if(index >= 0 && index < array_num(g_sound_files))
{
m_sound_data.free();
if(m_sound_data.load_wav(g_sound_files[index]))
m_sound_channel.play(&m_sound_data, 100, 1);
}
}
The play_sound function needs to
load the sound to play, using the cSoundData object.
From there, the sound is played from memory. In much the same way that you call
the play_sound function, you can play different songs using the play_music
function.
The play_music function also
takes an index number into an array of song filenames.
No need for tracking the
number of songs here (we're living on the wild side!), so
you can jump right into the play_music function:
void cApp::play_music(long index)
{
// do not botther changing song if same already playing
if(g_cur_music == index)
return;
m_music_channel.stop();
m_music_channel.free();
// Fade music out, giving DirectMusic enough time to finish up last song or else new song doesn't play correctly.
// The 700 is based on play volume of music, so adjust ahead.
DWORD timer = timeGetTime() + 700;
while(timeGetTime() < timer)
{
DWORD level = (timer - timeGetTime()) / 10;
m_music_channel.set_volume(level);
}
// load and play new song
m_music_channel.load(g_music_files[index]);
m_music_channel.play(70, 0);
g_cur_music = index;
}
Before continuing, you want to
check whether a song is currently playing. A global
variable keeps track of which song was last played, and if that song is still
playing,
you don’t need to start playing the same song again (the current song continues
to
play). If a new song is to be played, fade out the volume, free the current
song,
load the new song, and start playing the music playing.
Other functions:
int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
DWORD pos_x = (get_screen_width() - CLIENT_WIDTH) / 2;
DWORD pos_y = (get_screen_height() - CLIENT_HEIGHT) / 4;
build_window(inst, "GameClass", g_title_name,
WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
pos_x, pos_y, CLIENT_WIDTH, CLIENT_HEIGHT);
cApp app;
app.run();
return 0;
}
void cGameSpells::play_spell_sound(long index)
{
m_app->play_sound(index);
}
/*********************************************************************************************************/
void cApp::win_game()
{
m_state_manager.pop_all(this);
g_menu_options = MENU_LOAD;
m_state_manager.push(menu_frame, this);
}
void cApp::start_of_combat()
{
m_combat_exp = 0;
m_combat_money = 0;
// trigger start of combat script
char filename[MAX_PATH];
sprintf(filename, "..\\Data\\SOC%lu.mls", m_scene_index);
m_game_script.execute(filename);
}
void cApp::end_of_combat()
{
g_player->char_def.money += m_combat_money;
g_player->char_def.exp += m_combat_exp;
m_text_header.set_text("Victory!", COLOR_WHITE);
m_text_window.set_text("", COLOR_WHITE);
char window_text[2000], gained[128];
// start constructing the main window text
strcpy(window_text, "\r\n\n");
if(m_combat_money)
{
sprintf(gained, "Gained %lu gold!\r\n", m_combat_money);
strcat(window_text, gained);
}
sprintf(gained, "Gained %lu experience!\r\n", m_combat_exp);
strcat(window_text, gained);
// process level up
for(int i = 0; i < array_num(g_level_up_exp); i++)
{
if(g_player->char_def.exp >= g_level_up_exp[i] && g_player->char_def.level < i+2)
{
g_player->char_def.level = i+2;
strcat(window_text, "Level up!\r\n");
// add bonuses for leveling up
g_player->char_def.health_points += 10;
g_player->char_def.mana_points += 10;
g_player->char_def.attack += 4;
g_player->char_def.defense += 2;
g_player->char_def.agility += 2;
g_player->char_def.resistance += 2;
g_player->char_def.mental += 2;
g_player->char_def.to_hit += 10;
strcat(window_text, "Stats up!\r\n");
// learn spells
if(g_player->char_def.level < SPELL_LEARN_TOP_LEVEL)
{
g_player->char_def.magic_spell[0] |= (1 << i);
sprintf(gained, "Learned spell %s\r\n", m_game_spells.get_spell(i)->name);
strcat(window_text, gained);
}
// max health and mana to match definition
g_player->health_points = g_player->char_def.health_points;
g_player->mana_points = g_player->char_def.mana_points;
}
}
// lock the keyboard and mouse
m_keyboard.m_locks[KEY_SPACE] = true;
m_keyboard.set_key_state(KEY_SPACE, false);
m_mouse.m_locks[MOUSE_LBUTTON] = true;
m_mouse.set_button_state(MOUSE_LBUTTON, false);
// render the scene while waiting for key press or button press
for(;;)
{
// break when space pressed
m_keyboard.acquire();
m_keyboard.read();
if(m_keyboard.get_key_state(KEY_SPACE))
break;
// break when left mouse button pressed
m_mouse.acquire();
m_mouse.read();
if(m_mouse.get_button_state(MOUSE_LBUTTON))
break;
// render the scene and text window
clear_display_zbuffer(1.0f);
begin_display_scene();
render_frame(0);
m_text_window.render(window_text, COLOR_WHITE);
m_text_header.render(NULL, COLOR_WHITE);
end_display_scene();
present_display();
}
// trigger end of combat script
char filename[MAX_PATH];
sprintf(filename, "..\\Data\\EOC%lu.mls", m_scene_index);
m_game_script.execute(filename);
}
bool cApp::last_point_reached(sCharacter* character)
{
if(character == NULL || character->ai != CHAR_ROUTE)
return false;
long last_index = character->num_points - 1;
sRoutePoint* last_point = &character->route[last_index];
// determine if character has reached point
float x_diff = fabs(character->pos_x - last_point->pos_x);
float y_diff = fabs(character->pos_y - last_point->pos_y);
float z_diff = fabs(character->pos_z - last_point->pos_z);
float dist = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;
float radius = m_game_chars.get_xz_radius(character) * 0.25f;
// return true if point being touched
return (dist < radius * radius);
}