mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-07 10:21:31 +00:00
1223 lines
39 KiB
C++
1223 lines
39 KiB
C++
/* ResidualVM - A 3D game interpreter
|
|
*
|
|
* ResidualVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
* file distributed with this source distribution.
|
|
*
|
|
* Additional copyright for this file:
|
|
* Copyright (C) 1999-2000 Revolution Software Ltd.
|
|
* This code is based on source code created by Revolution Software,
|
|
* used with permission.
|
|
*
|
|
* 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 the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "engines/icb/floors.h"
|
|
#include "engines/icb/speech.h"
|
|
#include "engines/icb/fn_routines.h"
|
|
#include "engines/icb/session.h"
|
|
#include "engines/icb/global_objects.h"
|
|
#include "engines/icb/global_switches.h"
|
|
#include "engines/icb/icon_list_manager.h"
|
|
#include "engines/icb/sound.h"
|
|
#include "engines/icb/mission.h"
|
|
#include "engines/icb/sound_lowlevel.h"
|
|
#include "engines/icb/res_man.h"
|
|
#include "engines/icb/common/ptr_util.h"
|
|
#include "engines/icb/sound/music_manager.h"
|
|
|
|
namespace ICB {
|
|
|
|
#define TEXT_MAX_WIDTH 300
|
|
#define IS_SPEECH_STARTED 1
|
|
#define SPEECH_ERROR 0
|
|
#define IS_SPEECH_ALREADY_PLAYING 0
|
|
|
|
uint32 GetCountReduction() { return 1; }
|
|
|
|
// This colour is used to display voice over text (normally player's speech colour).
|
|
uint8 voice_over_red = VOICE_OVER_DEFAULT_RED;
|
|
uint8 voice_over_green = VOICE_OVER_DEFAULT_GREEN;
|
|
uint8 voice_over_blue = VOICE_OVER_DEFAULT_BLUE;
|
|
|
|
mcodeFunctionReturnCodes fn_request_speech(int32 &result, int32 *params) { return (MS->fn_request_speech(result, params)); }
|
|
mcodeFunctionReturnCodes fn_add_talker(int32 &result, int32 *params) { return (MS->fn_add_talker(result, params)); }
|
|
mcodeFunctionReturnCodes fn_issue_speech_request(int32 &result, int32 *params) { return (MS->fn_issue_speech_request(result, params)); }
|
|
mcodeFunctionReturnCodes fn_anon_speech_invite(int32 &result, int32 *params) { return (MS->fn_anon_speech_invite(result, params)); }
|
|
mcodeFunctionReturnCodes fn_speak(int32 &result, int32 *params) { return (MS->fn_speak(result, params)); }
|
|
mcodeFunctionReturnCodes fn_confirm_requests(int32 &result, int32 *params) { return (MS->fn_confirm_requests(result, params)); }
|
|
mcodeFunctionReturnCodes fn_converse(int32 &result, int32 *params) { return (MS->fn_converse(result, params)); }
|
|
mcodeFunctionReturnCodes speak_object_face_object(int32 &result, int32 *params) { return (MS->speak_object_face_object(result, params)); }
|
|
mcodeFunctionReturnCodes speak_play_generic_anim(int32 &result, int32 *params) { return (MS->speak_play_generic_anim(result, params)); }
|
|
mcodeFunctionReturnCodes speak_wait_for_everyone(int32 &result, int32 *params) { return (MS->speak_wait_for_everyone(result, params)); }
|
|
mcodeFunctionReturnCodes speak_add_chooser_icon(int32 &result, int32 *params) { return (MS->speak_add_chooser_icon(result, params)); }
|
|
mcodeFunctionReturnCodes speak_user_chooser(int32 &result, int32 *params) { return (MS->speak_user_chooser(result, params)); }
|
|
mcodeFunctionReturnCodes speak_chosen(int32 &result, int32 *params) { return (MS->speak_chosen(result, params)); }
|
|
mcodeFunctionReturnCodes speak_new_menu(int32 &result, int32 *params) { return (MS->speak_new_menu(result, params)); }
|
|
mcodeFunctionReturnCodes speak_close_menu(int32 &result, int32 *params) { return (MS->speak_close_menu(result, params)); }
|
|
mcodeFunctionReturnCodes speak_menu_still_active(int32 &result, int32 *params) { return (MS->speak_menu_still_active(result, params)); }
|
|
mcodeFunctionReturnCodes speak_menu_choices_remain(int32 &result, int32 *params) { return (MS->speak_menu_choices_remain(result, params)); }
|
|
mcodeFunctionReturnCodes speak_end_conversation(int32 &result, int32 *params) { return (MS->speak_end_conversation(result, params)); }
|
|
mcodeFunctionReturnCodes speak_end_menu(int32 &result, int32 *params) { return (MS->speak_end_menu(result, params)); }
|
|
mcodeFunctionReturnCodes speak_add_special_chooser_icon(int32 &result, int32 *params) { return (MS->speak_add_special_chooser_icon(result, params)); }
|
|
mcodeFunctionReturnCodes speak_set_custom(int32 &result, int32 *params) { return (MS->speak_set_custom(result, params)); }
|
|
mcodeFunctionReturnCodes speak_play_custom_anim(int32 &result, int32 *params) { return (MS->speak_play_custom_anim(result, params)); }
|
|
mcodeFunctionReturnCodes fn_get_speech_status(int32 &result, int32 *params) { return (MS->fn_get_speech_status(result, params)); }
|
|
mcodeFunctionReturnCodes fn_kill_conversations(int32 &result, int32 *params) { return (MS->fn_kill_conversations(result, params)); }
|
|
mcodeFunctionReturnCodes fn_speech_colour(int32 &result, int32 *params) { return (MS->fn_speech_colour(result, params)); }
|
|
mcodeFunctionReturnCodes speak_reverse_custom_anim(int32 &result, int32 *params) { return (MS->speak_reverse_custom_anim(result, params)); }
|
|
mcodeFunctionReturnCodes speak_preload_custom_anim(int32 &result, int32 *params) { return (MS->speak_preload_custom_anim(result, params)); }
|
|
mcodeFunctionReturnCodes fn_set_voice_over_colour(int32 &result, int32 *params) { return (MS->fn_set_voice_over_colour(result, params)); }
|
|
mcodeFunctionReturnCodes fn_default_voice_over_colour(int32 &result, int32 *params) { return (MS->fn_default_voice_over_colour(result, params)); }
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_get_speech_status(int32 &result, int32 *) {
|
|
// tells us if a converation is already running or not
|
|
|
|
result = total_convs;
|
|
|
|
if ((cur_id == player.Fetch_player_id()) && (player.player_status == REMORA))
|
|
Fatal_error("fn_get_speech_status - player cant start conversation inside remora!");
|
|
|
|
if ((cur_id == player.Fetch_player_id()) && (g_oIconMenu->IsActive()))
|
|
g_oIconMenu->CloseDownIconMenu();
|
|
|
|
if ((g_oIconMenu->IsActive()) || (player.player_status == REMORA))
|
|
result = 1;
|
|
|
|
if ((result) && (cur_id == player.Fetch_player_id()))
|
|
Tdebug("speech_check.txt", "get status");
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_request_speech(int32 &result, int32 *params) {
|
|
// try to start up a conversation
|
|
// there may be one or several participants who must agree to join the conversation
|
|
|
|
// params 0 name of scene script
|
|
|
|
const char *scene_script_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
|
|
Zdebug("[%s] fn_request_speech [%s]", object->GetName(), scene_script_name);
|
|
|
|
// there cannot be any other conversations happening - change to initial spec as it is not used and memory is required
|
|
if (total_convs) {
|
|
if (cur_id == player.Fetch_player_id())
|
|
Tdebug("speech_check.txt", "request");
|
|
|
|
return IR_REPEAT; // just wait until other is done
|
|
}
|
|
|
|
if (player.player_status == REMORA)
|
|
return IR_REPEAT;
|
|
|
|
if ((cur_id == player.Fetch_player_id()) && (g_oIconMenu->IsActive()))
|
|
g_oIconMenu->CloseDownIconMenu();
|
|
|
|
if ((g_oIconMenu->IsActive()) || (player.player_status == REMORA))
|
|
return IR_REPEAT;
|
|
|
|
// not started yet
|
|
S.state = __PENDING;
|
|
|
|
// get the system now - in case another object tries to start a conversation
|
|
total_convs++; // to 1 ;)
|
|
|
|
// find the speech script
|
|
// form name of speech script
|
|
sprintf(temp_buf, "scenes::%s", scene_script_name);
|
|
|
|
S.script_pc = (char *)scripts->Try_fetch_item_by_name(temp_buf); // run init script
|
|
|
|
// conversation script doesnt exist
|
|
if (!S.script_pc)
|
|
Fatal_error("object [%d] tried to start conversation script [%s] which doesnt exist", cur_id, (const char *)temp_buf);
|
|
|
|
// reset number of subs
|
|
S.total_subscribers = 0; // everyone but us initially
|
|
|
|
S.current_subscribers = 0; // reset
|
|
|
|
for (uint32 j = 0; j < MAX_coms; j++)
|
|
S.coms[j].active = FALSE8;
|
|
|
|
menu_number = 0; // start at menu 0
|
|
|
|
result = 0; // means ok
|
|
|
|
Set_string(scene_script_name, speech_conv_name); // from, to
|
|
|
|
// set player to stand frame
|
|
if (cur_id == player.Fetch_player_id()) {
|
|
L->cur_anim_type = __STAND;
|
|
L->anim_pc = 0;
|
|
}
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_add_talker(int32 &, int32 *params) {
|
|
// a name of a mega to join the conversation is passed
|
|
// we add its id into the list of subscribers
|
|
|
|
// params 0 ascii name of object
|
|
|
|
uint32 talk_id;
|
|
|
|
const char *object_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
|
|
if (S.state != __PENDING)
|
|
Fatal_error("fn_add_talker called but in wrong order");
|
|
|
|
// convert the ascii name into an object id
|
|
talk_id = objects->Fetch_item_number_by_name(object_name);
|
|
|
|
// check for illegal object
|
|
if (talk_id >= total_objects)
|
|
Fatal_error("fn_add_talker finds [%s] is not a real object", object_name);
|
|
|
|
if (cur_id == talk_id)
|
|
Fatal_error("[%s] calls fn_add_talker('%s') which isnt necessary and may cause strange lock up effects!", object_name, object_name);
|
|
|
|
Zdebug("talk id %d", talk_id);
|
|
|
|
if (talk_id != 0xffffffff) {
|
|
|
|
if (S.total_subscribers == MAX_people_talking)
|
|
Fatal_error("fn_add_talker(%s) too many people in conversation", object_name);
|
|
|
|
S.subscribers_requested[S.total_subscribers] = talk_id;
|
|
|
|
// increase participant count
|
|
S.total_subscribers++;
|
|
|
|
} else {
|
|
// named object doesnt exist which is pretty serious
|
|
Fatal_error("tried to add non existent object [%s] to conversation", object_name);
|
|
}
|
|
|
|
// keep going
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_issue_speech_request(int32 &, int32 *) {
|
|
// once everyone required has been added to the subscriber list then we can ask each in turn if they want to join
|
|
uint32 j;
|
|
|
|
Zdebug("issue speech request");
|
|
Zdebug(" %d invitees", S.total_subscribers);
|
|
|
|
if (S.state != __PENDING)
|
|
Fatal_error("fn_issue_speech_request called but in wrong order");
|
|
|
|
// if (S.subscribers_requested.GetNoItems())
|
|
if (S.total_subscribers) {
|
|
// force each to re-run their context
|
|
for (j = 0; j < S.total_subscribers; j++) {
|
|
Zdebug("forcing context rerun for %d", S.subscribers_requested[j]);
|
|
|
|
if (S.subscribers_requested[j] >= total_objects)
|
|
Fatal_error("fn_issue_speech_request has illegal object in issue list");
|
|
|
|
Force_context_check(S.subscribers_requested[j]);
|
|
Set_objects_conversation_uid(S.subscribers_requested[j], 0); // participant id, conversation uid
|
|
}
|
|
}
|
|
|
|
// skip a cycle to allow the results to come back
|
|
return (IR_STOP);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_confirm_requests(int32 &result, int32 *params) {
|
|
// did everyone in this conversation subscribe?
|
|
// if yes then record the owner and let the conversation begin
|
|
|
|
Zdebug("fn-confirm-requests");
|
|
|
|
if (S.state != __PENDING)
|
|
Fatal_error("fn_confirm_requests called but in wrong order");
|
|
|
|
// did everyone subscrive and consequently fn-converse?
|
|
if (S.total_subscribers == S.current_subscribers) {
|
|
// same number accepted as were requested so we're all systems go
|
|
|
|
Zdebug(" conversation ok");
|
|
|
|
S.state = __PROCESS; // set to script processing mode
|
|
|
|
if (S.total_subscribers > MAX_people_talking)
|
|
Fatal_error("fn_confirm_requests finds too many people in conversation");
|
|
|
|
S.subscribers_requested[S.total_subscribers] = cur_id;
|
|
|
|
// increase participant count
|
|
S.total_subscribers++;
|
|
|
|
// set our conversation uid as we are about to become a lowly subscriber
|
|
Set_objects_conversation_uid(cur_id, 0); // participant id, conversation uid
|
|
|
|
L->do_not_disturb++; // stop events
|
|
|
|
conv_focus = 0; // us and always
|
|
|
|
result = TRUE8;
|
|
|
|
if (fn_stop_sting(result, params) == IR_REPEAT)
|
|
return IR_REPEAT;
|
|
} else {
|
|
// no
|
|
Zdebug(" conversation not fully subscribed");
|
|
|
|
End_conversation(CONV_ID);
|
|
|
|
result = FALSE8;
|
|
}
|
|
|
|
// and onward
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_anon_speech_invite(int32 &result, int32 *) {
|
|
// general context script check for being a participant in a conversation
|
|
// the idea is that if we are in a conversation then we continue with it regardless
|
|
|
|
// just having this in the logic context means no conversation can be interupted by another
|
|
|
|
Zdebug("check speech invite");
|
|
|
|
if (L->conversation_uid != NO_SPEECH_REQUEST) {
|
|
// yes, a request is pending
|
|
// if not on a floor - perhaps on a ladder or stair - then mask the request
|
|
if (M) {
|
|
if (!floor_def->On_a_floor(M)) {
|
|
Message_box("%d", player.player_status);
|
|
L->conversation_uid = NO_SPEECH_REQUEST;
|
|
result = 0; // no request
|
|
return IR_CONT;
|
|
}
|
|
}
|
|
|
|
L->do_not_disturb++; // stop events
|
|
result = 1;
|
|
return IR_CONT;
|
|
}
|
|
|
|
// no
|
|
result = 0;
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_kill_conversations(int32 &, int32 *) {
|
|
// current conversations end - allowing a new one to interupt
|
|
|
|
// if there are conversations on going them kill em (it)
|
|
if (total_convs)
|
|
End_conversation(CONV_ID);
|
|
|
|
return IR_STOP;
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_converse(int32 &, int32 *) {
|
|
// mega calls this to subscribe to a conversation
|
|
// we will be told when to quit though
|
|
|
|
uint32 j;
|
|
int32 result;
|
|
int32 params;
|
|
mcodeFunctionReturnCodes ret;
|
|
|
|
Zdebug("fn_converse [%s] - uid %d", object->GetName(), L->conversation_uid);
|
|
|
|
if (L->conversation_uid == NO_SPEECH_REQUEST) {
|
|
// conversation has ended!
|
|
|
|
L->do_not_disturb--; // restart events
|
|
|
|
// continue and fall off script letting us re-run our logic context
|
|
return (IR_CONT);
|
|
}
|
|
|
|
// re-register
|
|
speech_info[L->conversation_uid].current_subscribers++;
|
|
|
|
// if player then update buttons and check for skip speech
|
|
if (cur_id == player.Fetch_player_id()) {
|
|
// check keys/pads/etc. to see what the user is trying to do
|
|
player.Update_input_state();
|
|
|
|
if (((++no_click_zone) > 2)) {
|
|
if ((player.cur_state.IsButtonSet(__INTERACT)) && (!player.interact_lock) && (speech_info[CONV_ID].state == __SAYING)) {
|
|
player.interact_lock = TRUE8;
|
|
|
|
speech_info[CONV_ID].count = 0;
|
|
CancelSpeechPlayback();
|
|
|
|
} else if (!player.cur_state.IsButtonSet(__INTERACT)) {
|
|
player.interact_lock = FALSE8;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for instructions
|
|
for (j = 0; j < MAX_coms; j++) // loop to number of commands
|
|
if (speech_info[L->conversation_uid].coms[j].active == TRUE8) // this slot?
|
|
if (speech_info[L->conversation_uid].coms[j].id == cur_id) { // is the command for us
|
|
switch (speech_info[L->conversation_uid].coms[j].command) {
|
|
case __FACE_OBJECT:
|
|
Zdebug("face object [com %d] - [param %d]", j, speech_info[L->conversation_uid].coms[j].param1);
|
|
|
|
if (!speech_face_object(speech_info[L->conversation_uid].coms[j].param1)) {
|
|
speech_info[L->conversation_uid].coms[j].active = FALSE8;
|
|
}
|
|
break;
|
|
|
|
case __PLAY_GENERIC_ANIM:
|
|
params = MemoryUtil::encodePtr((uint8 *)speech_info[L->conversation_uid].coms[j].str_param1);
|
|
ret = fn_play_generic_anim(result, ¶ms);
|
|
if (ret == IR_CONT) {
|
|
speech_info[L->conversation_uid].coms[j].active = FALSE8;
|
|
}
|
|
break;
|
|
|
|
case __PLAY_CUSTOM_ANIM:
|
|
params = MemoryUtil::encodePtr((uint8 *)speech_info[L->conversation_uid].coms[j].str_param1);
|
|
ret = fn_easy_play_custom_anim(result, ¶ms);
|
|
if (ret == IR_CONT) {
|
|
Reset_cur_megas_custom_type();
|
|
speech_info[L->conversation_uid].coms[j].active = FALSE8;
|
|
}
|
|
break;
|
|
|
|
case __REVERSE_CUSTOM_ANIM:
|
|
params = MemoryUtil::encodePtr((uint8 *)speech_info[L->conversation_uid].coms[j].str_param1);
|
|
ret = fn_reverse_custom_anim(result, ¶ms);
|
|
if (ret == IR_CONT) {
|
|
Reset_cur_megas_custom_type();
|
|
speech_info[L->conversation_uid].coms[j].active = FALSE8;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Fatal_error("illegal speech com instruction");
|
|
break;
|
|
}
|
|
|
|
// do the first pending com - not any further ones which may be queued
|
|
return (IR_REPEAT); // keep returning until we're released
|
|
}
|
|
|
|
return (IR_REPEAT); // keep returning until we're released
|
|
}
|
|
|
|
void _game_session::Service_speech() {
|
|
// the system runs all speech scripts
|
|
|
|
c_game_object *speech_object;
|
|
uint32 ret;
|
|
|
|
// anything going on?
|
|
if (!total_convs)
|
|
return; // none
|
|
|
|
// not started yet
|
|
if (S.state == __PENDING)
|
|
return;
|
|
|
|
if (speech_info[CONV_ID].total_subscribers > speech_info[CONV_ID].current_subscribers) {
|
|
// oh no, conversation has to end as someones dropped out - perhaps they were shot, blown up or distracted
|
|
|
|
// in-case someone is mid sentence
|
|
CancelSpeechPlayback();
|
|
|
|
End_conversation(CONV_ID);
|
|
return;
|
|
}
|
|
|
|
// reset sub total for next cycles check
|
|
speech_info[CONV_ID].current_subscribers = 0;
|
|
|
|
// process the conversation
|
|
switch (speech_info[CONV_ID].state) {
|
|
case __PROCESS: // run the script
|
|
// get the dummy speech object
|
|
speech_object = (c_game_object *)objects->Fetch_item_by_name("scenes");
|
|
cur_id = objects->Fetch_item_number_by_name("scenes");
|
|
L = logic_structs[cur_id];
|
|
I = 0;
|
|
M = 0;
|
|
|
|
// run the script
|
|
ret = RunScript(const_cast<const char *&>(speech_info[CONV_ID].script_pc), speech_object);
|
|
|
|
if (ret == 1) {
|
|
// speech script has finished
|
|
End_conversation(CONV_ID);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
// on psx this waits until the speech is started then sets the timer for stopping it
|
|
case __WAITING_TO_SAY:
|
|
case __SAYING: // someone is talking
|
|
|
|
// set state to saying if we're actually playing
|
|
if (IS_SPEECH_STARTED)
|
|
speech_info[CONV_ID].state = __SAYING;
|
|
|
|
// count down the timer
|
|
// back into process mode when we're done
|
|
if (!speech_info[CONV_ID].count) { // done
|
|
speech_info[CONV_ID].current_talker = -1; // nobody talking now
|
|
speech_info[CONV_ID].state = __PROCESS;
|
|
|
|
// if this conversation has the focus then kill the text bloc
|
|
if (CONV_ID == conv_focus) {
|
|
// delete text_bloc;
|
|
text_speech_bloc->please_render = FALSE8;
|
|
}
|
|
} else
|
|
speech_info[CONV_ID].count -= GetCountReduction();
|
|
|
|
break;
|
|
|
|
default:
|
|
Fatal_error("illegal instruction found in conversation");
|
|
break;
|
|
}
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_speak(int32 &, int32 *params) {
|
|
// current conversation
|
|
|
|
// params 0 ascii name of person
|
|
// 1 ascii text line name
|
|
|
|
_TSrtn ret_code;
|
|
uint32 speaker_id;
|
|
PXvector pos;
|
|
// yesno
|
|
bool8 resu = FALSE8;
|
|
|
|
// screen pos
|
|
PXvector filmpos;
|
|
|
|
char error[] = "text file out of date!";
|
|
|
|
char *ascii;
|
|
|
|
const char *person_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
const char *text_label = (const char *)MemoryUtil::resolvePtr(params[1]);
|
|
|
|
// wait until last line is finished
|
|
if (IS_SPEECH_ALREADY_PLAYING)
|
|
return IR_REPEAT;
|
|
|
|
// Make the hash from the label
|
|
uint32 speechHash = HashString(text_label);
|
|
|
|
if (!PreloadSpeech(speechHash))
|
|
return IR_REPEAT;
|
|
|
|
if (text_speech_bloc->please_render == TRUE8)
|
|
Fatal_error("fn_speak - text block already exists!");
|
|
|
|
Zdebug("fn_speak [%s] [%s]", person_name, text_label);
|
|
|
|
// work out position using speakers position
|
|
// fetch the speaker
|
|
speaker_id = objects->Fetch_item_number_by_name(person_name);
|
|
if (speaker_id == PX_LINKED_DATA_FILE_ERROR)
|
|
Fatal_error("Unable to find object ID for [%s] in fn_speak()", person_name);
|
|
|
|
if (text) {
|
|
// retrieve text line
|
|
ascii = (char *)text->Try_fetch_item_by_name(text_label);
|
|
|
|
if (!ascii)
|
|
ascii = error;
|
|
|
|
else if (!*(ascii)) // null terminated string
|
|
ascii = error;
|
|
|
|
} else
|
|
ascii = error;
|
|
|
|
// work out length
|
|
Zdebug("[%s]", ascii);
|
|
|
|
{
|
|
_TSparams *text_params;
|
|
// build text block
|
|
text_params = text_speech_bloc->GetParams();
|
|
text_speech_bloc->please_render = TRUE8; // exists to draw
|
|
|
|
text_params->textLine = (uint8 *)ascii; // print id instead of real text for now
|
|
// set font
|
|
text_params->fontResource = (const char *)speech_font_one; //&test_font[0];
|
|
|
|
// Jake for clustering
|
|
text_params->fontResource_hash = speech_font_one_hash;
|
|
|
|
text_params->maxWidth = TEXT_MAX_WIDTH;
|
|
text_params->lineSpacing = 0;
|
|
text_params->charSpacing = 0;
|
|
text_params->errorChecking = 1; // enable error checking during AnalyseSentence
|
|
|
|
// Set the colour for the speech.
|
|
if (logic_structs[speaker_id]->mega) {
|
|
// Each mega has their own speech colour.
|
|
SetTextColour(logic_structs[speaker_id]->mega->speech_red, logic_structs[speaker_id]->mega->speech_green, logic_structs[speaker_id]->mega->speech_blue);
|
|
} else {
|
|
// Objects all share one value.
|
|
SetTextColour(voice_over_red, voice_over_green, voice_over_blue);
|
|
}
|
|
|
|
ret_code = text_speech_bloc->MakeTextSprite();
|
|
|
|
if (ret_code != TS_OK)
|
|
Fatal_error("line [%s] text formating is illegal [%s]", text_label, ascii);
|
|
|
|
if (g_px->display_mode == THREED) {
|
|
|
|
// get coords
|
|
if (logic_structs[speaker_id]->image_type == PROP) {
|
|
// has prop got coords?
|
|
if (logic_structs[speaker_id]->prop_coords_set == TRUE8) {
|
|
pos.x = logic_structs[speaker_id]->prop_xyz.x;
|
|
pos.y = logic_structs[speaker_id]->prop_xyz.y; // talks over head rather than from the feet
|
|
pos.z = logic_structs[speaker_id]->prop_xyz.z;
|
|
|
|
// setup camera
|
|
PXcamera &camera = GetCamera();
|
|
|
|
// compute screen coord
|
|
|
|
PXWorldToFilm(pos, camera, resu, filmpos);
|
|
|
|
if (!resu)
|
|
Zdebug(" position off film?");
|
|
|
|
text_speech_bloc->GetRenderCoords((int32)(filmpos.x + (SCREEN_WIDTH / 2)), (int32)((SCREEN_DEPTH / 2) - (filmpos.y)), PIN_AT_CENTRE_OF_BASE,
|
|
5); // margin
|
|
} else { // prop with no coords prints along bottom of screen
|
|
text_speech_bloc->renderX = 20;
|
|
text_speech_bloc->renderY = 400;
|
|
}
|
|
} else {
|
|
pos.x = logic_structs[speaker_id]->mega->actor_xyz.x;
|
|
pos.y = logic_structs[speaker_id]->mega->actor_xyz.y + 200; // talks over head rather than from the feet
|
|
pos.z = logic_structs[speaker_id]->mega->actor_xyz.z;
|
|
|
|
// setup camera
|
|
PXcamera &camera = GetCamera();
|
|
|
|
// compute screen coord
|
|
PXWorldToFilm(pos, camera, resu, filmpos);
|
|
|
|
if (!resu)
|
|
Zdebug(" position off film?");
|
|
|
|
text_speech_bloc->GetRenderCoords((int32)(filmpos.x + (SCREEN_WIDTH / 2)), (int32)((SCREEN_DEPTH / 2) - (filmpos.y)), PIN_AT_CENTRE_OF_BASE,
|
|
5); // margin
|
|
}
|
|
|
|
} else { // fixed position in nethack mode
|
|
text_speech_bloc->renderX = 20;
|
|
text_speech_bloc->renderY = 400;
|
|
}
|
|
|
|
speech_info[CONV_ID].count = SayLineOfSpeech(speechHash);
|
|
|
|
|
|
if (!speech_info[CONV_ID].count)
|
|
Fatal_error("Speech xa file is 0 game cycles see, int32");
|
|
}
|
|
|
|
no_click_zone = 0; // cant click past for specified period
|
|
|
|
// set conv mode
|
|
speech_info[CONV_ID].current_talker = speaker_id;
|
|
speech_info[CONV_ID].state = __WAITING_TO_SAY;
|
|
|
|
return (IR_STOP); // drop out
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_speech_colour(int32 &, int32 *params) {
|
|
Zdebug("fn_speech_colour( %d, %d, %d ) called by object ID %d", (int32)params[0], (int32)params[1], (int32)params[2], cur_id);
|
|
|
|
// Must be a mega calling this.
|
|
if (!logic_structs[cur_id]->mega)
|
|
Fatal_error("Non-mega %d called fn_speech_colour()", cur_id);
|
|
|
|
// Set the values in the mega's structure.
|
|
logic_structs[cur_id]->mega->speech_red = (uint8)params[0];
|
|
logic_structs[cur_id]->mega->speech_green = (uint8)params[1];
|
|
logic_structs[cur_id]->mega->speech_blue = (uint8)params[2];
|
|
|
|
// Calling script can continue.
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_set_voice_over_colour(int32 &, int32 *params) {
|
|
// This colour is used to display voice over text (normally player's speech colour).
|
|
voice_over_red = (uint8)params[0];
|
|
voice_over_green = (uint8)params[1];
|
|
voice_over_blue = (uint8)params[2];
|
|
|
|
// Calling script can continue.
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::fn_default_voice_over_colour(int32 &, int32 *) {
|
|
// This colour is used to display voice over text (normally player's speech colour).
|
|
voice_over_red = VOICE_OVER_DEFAULT_RED;
|
|
voice_over_green = VOICE_OVER_DEFAULT_GREEN;
|
|
voice_over_blue = VOICE_OVER_DEFAULT_BLUE;
|
|
|
|
// Calling script can continue.
|
|
return (IR_CONT);
|
|
}
|
|
|
|
extern int32 globalCharSpacing;
|
|
|
|
// This function computes the formatting of a paragraph of Remora text without going as far as making
|
|
// the sprite. This is so the Remora can decide how it is going to format text before it tries to draw
|
|
// it.
|
|
void _game_session::Format_remora_text(const char *pcText, int32 nLineSpacing, int32 nCharSpacing, uint32 nMaxWidth) {
|
|
_TSrtn eErrorCode;
|
|
_TSparams *psTextParams;
|
|
|
|
// Get the pointer to the parameter block that needs to be filled in before the functions of class text_sprite
|
|
// can do their stuff.
|
|
psTextParams = text_bloc->GetParams();
|
|
|
|
// Set the parameters required by text_sprite::AnalyseSentence().
|
|
psTextParams->textLine = (uint8 *)const_cast<char *>(pcText);
|
|
psTextParams->fontResource = (const char *)remora_font;
|
|
psTextParams->fontResource_hash = remora_font_hash;
|
|
psTextParams->maxWidth = nMaxWidth;
|
|
psTextParams->lineSpacing = nLineSpacing;
|
|
psTextParams->charSpacing = nCharSpacing + globalCharSpacing;
|
|
psTextParams->errorChecking = 1;
|
|
|
|
// Now I can call AnalyseSentence() to work out how the text will be formatted when it is drawn.
|
|
eErrorCode = text_bloc->AnalyseSentence();
|
|
|
|
// Check there was no error.
|
|
if (eErrorCode != TS_OK) {
|
|
Fatal_error("text_sprite::AnalyseSentence( '%s' ) failed with code %d in Format_remora_text", pcText, (int32)eErrorCode);
|
|
}
|
|
}
|
|
|
|
void _game_session::Create_remora_text(uint32 x, uint32 y, const char *ascii,
|
|
int32 margin, _pin_position pin_pos, int32 lineSpacing, int32 charSpacing,
|
|
uint32 maxWidth,
|
|
bool8 analysisAlreadyDone,
|
|
int32 stopAtLine) {
|
|
bool8 bRemoraLeftFormatting;
|
|
|
|
Zdebug("Create_remora_text %d,%d [%s]", x, y, ascii);
|
|
|
|
// if we are in focus then build a text bloc
|
|
// build text block
|
|
_TSparams *text_params;
|
|
text_params = text_bloc->GetParams();
|
|
text_params->textLine = (uint8 *)const_cast<char *>(ascii); // print id instead of real text for now
|
|
// set font
|
|
text_params->fontResource = (const char *)remora_font;
|
|
// Jake for clustering
|
|
text_params->fontResource_hash = remora_font_hash;
|
|
|
|
text_params->maxWidth = maxWidth;
|
|
text_params->lineSpacing = lineSpacing;
|
|
text_params->charSpacing = charSpacing + globalCharSpacing;
|
|
text_params->errorChecking = 1; // enable error checking during AnalyseSentence
|
|
|
|
// See if we need to trigger the special left formatting that only the Remora uses.
|
|
if (pin_pos == PIN_AT_TOP_LEFT)
|
|
bRemoraLeftFormatting = TRUE8;
|
|
else
|
|
bRemoraLeftFormatting = FALSE8;
|
|
|
|
int32 oldFlag = g_px->speechLineNumbers;
|
|
|
|
// Turn off line numbers for non-spoken lines of dialogue
|
|
if (*ascii == TS_NON_SPOKEN_LINE)
|
|
g_px->speechLineNumbers = 0;
|
|
|
|
text_bloc->MakeTextSprite(analysisAlreadyDone, stopAtLine, bRemoraLeftFormatting);
|
|
g_px->speechLineNumbers = (uint8)oldFlag;
|
|
text_bloc->GetRenderCoords(x, y, pin_pos, margin);
|
|
|
|
text_bloc->please_render = TRUE8;
|
|
}
|
|
|
|
void _game_session::Kill_remora_text() {
|
|
|
|
if (!text_bloc->please_render)
|
|
Fatal_error("Kill_remora_text - no text block exists!");
|
|
|
|
text_bloc->please_render = FALSE8;
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_object_face_object(int32 &, int32 *params) {
|
|
// first person turns to face the other
|
|
|
|
// params 0 ascii name of person to turn
|
|
// 1 ascii name of person to turn to
|
|
|
|
uint32 speaker_id;
|
|
uint32 tar_id;
|
|
uint32 com_no = 0;
|
|
|
|
const char *speaker_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
const char *target_name = (const char *)MemoryUtil::resolvePtr(params[1]);
|
|
|
|
// fetch the speaker
|
|
speaker_id = objects->Fetch_item_number_by_name(speaker_name);
|
|
|
|
// fetch target
|
|
tar_id = objects->Fetch_item_number_by_name(target_name);
|
|
|
|
// find next com slot
|
|
while ((speech_info[0].coms[com_no].active == TRUE8) && (speech_info[0].coms[com_no].id != speaker_id))
|
|
com_no++;
|
|
|
|
speech_info[CONV_ID].coms[com_no].active = TRUE8;
|
|
|
|
// set the commands owner id
|
|
speech_info[CONV_ID].coms[com_no].id = speaker_id;
|
|
|
|
// set command type
|
|
speech_info[CONV_ID].coms[com_no].command = __FACE_OBJECT;
|
|
|
|
// set the target id parameter
|
|
speech_info[CONV_ID].coms[com_no].param1 = tar_id;
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_play_generic_anim(int32 &, int32 *params) {
|
|
// object plays a generic animation
|
|
// links to fn_play_generic_anim
|
|
|
|
// params 0 ascii name of person to animate
|
|
// 1 ascii name of generic animation
|
|
|
|
uint32 speaker_id;
|
|
uint32 com_no = 0;
|
|
|
|
const char *person_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[1]);
|
|
|
|
Zdebug("speak_play_generic_anim [%s] to face [%s]", person_name, anim_name);
|
|
|
|
// fetch the speaker
|
|
speaker_id = objects->Fetch_item_number_by_name(person_name);
|
|
|
|
while ((speech_info[0].coms[com_no].active == TRUE8) && (speech_info[0].coms[com_no].id != speaker_id))
|
|
com_no++;
|
|
|
|
if (com_no == MAX_coms)
|
|
Fatal_error("too many speech commands");
|
|
|
|
speech_info[CONV_ID].coms[com_no].active = TRUE8;
|
|
|
|
// set the commands owner id
|
|
speech_info[CONV_ID].coms[com_no].id = speaker_id;
|
|
|
|
// set command type
|
|
speech_info[CONV_ID].coms[com_no].command = __PLAY_GENERIC_ANIM;
|
|
|
|
// set the target id parameter
|
|
Set_string(anim_name, speech_info[CONV_ID].coms[com_no].str_param1);
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_set_custom(int32 &, int32 *params) {
|
|
// set the custom set for a character
|
|
|
|
// params 0 ascii name of person to animate
|
|
// 1 ascii name of custom set
|
|
|
|
int32 speaker_id;
|
|
|
|
const char *person_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
const char *custom_name = (const char *)MemoryUtil::resolvePtr(params[1]);
|
|
|
|
// fetch the speaker
|
|
speaker_id = objects->Fetch_item_number_by_name(person_name);
|
|
|
|
if (speaker_id == -1)
|
|
Fatal_error("speak_set_custom cant find object [%s]", person_name);
|
|
|
|
Set_string(custom_name, logic_structs[speaker_id]->mega->custom_set, MAX_CUSTOM_NAME_LENGTH);
|
|
logic_structs[speaker_id]->mega->custom = TRUE8;
|
|
logic_structs[speaker_id]->looping = 0; // reset
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_preload_custom_anim(int32 &, int32 *params) {
|
|
// set a custom anim preloading
|
|
|
|
// params 0 name of mega
|
|
// 1 name of anim
|
|
|
|
int32 speaker_id;
|
|
|
|
const char *mega_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
/*const char *anim_name = (const char *)*/ MemoryUtil::resolvePtr(params[1]);
|
|
|
|
#define II logic_structs[speaker_id]->voxel_info
|
|
#define MM logic_structs[speaker_id]->mega
|
|
|
|
// fetch the speaker
|
|
speaker_id = objects->Fetch_item_number_by_name(mega_name);
|
|
|
|
// build that name
|
|
II->Init_custom_animation(mega_name);
|
|
|
|
// now set prebuilt flag
|
|
II->has_custom_path_built = FALSE8;
|
|
|
|
// start preloading
|
|
rs_anims->Res_open(II->get_anim_name(__NON_GENERIC), II->anim_name_hash[__NON_GENERIC], II->base_path, II->base_path_hash); //
|
|
|
|
MM->custom = FALSE8;
|
|
|
|
return IR_CONT;
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_play_custom_anim(int32 &, int32 *params) {
|
|
// object plays a custom animation
|
|
// links to fn_play_custom_anim
|
|
|
|
// params 0 ascii name of person to animate
|
|
// 1 ascii name of custom animation
|
|
|
|
uint32 speaker_id;
|
|
uint32 com_no = 0;
|
|
|
|
const char *speaker_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[1]);
|
|
|
|
// fetch the speaker
|
|
speaker_id = objects->Fetch_item_number_by_name(speaker_name);
|
|
|
|
while ((speech_info[0].coms[com_no].active == TRUE8) && (speech_info[0].coms[com_no].id != speaker_id))
|
|
com_no++;
|
|
|
|
if (com_no == MAX_coms)
|
|
Fatal_error("too many speech commands");
|
|
|
|
speech_info[CONV_ID].coms[com_no].active = TRUE8;
|
|
|
|
// set the commands owner id
|
|
speech_info[CONV_ID].coms[com_no].id = speaker_id;
|
|
|
|
// set command type
|
|
speech_info[CONV_ID].coms[com_no].command = __PLAY_CUSTOM_ANIM;
|
|
|
|
// set the target id parameter
|
|
Set_string(anim_name, speech_info[CONV_ID].coms[com_no].str_param1);
|
|
|
|
return (IR_STOP);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_reverse_custom_anim(int32 &, int32 *params) {
|
|
// object REVERSE plays a custom animation
|
|
|
|
// params 0 ascii name of person to animate
|
|
// 1 ascii name of custom animation
|
|
|
|
uint32 speaker_id;
|
|
uint32 com_no = 0;
|
|
|
|
const char *speaker_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
const char *anim_name = (const char *)MemoryUtil::resolvePtr(params[1]);
|
|
|
|
// fetch the speaker
|
|
speaker_id = objects->Fetch_item_number_by_name(speaker_name);
|
|
|
|
while ((speech_info[0].coms[com_no].active == TRUE8) && (speech_info[0].coms[com_no].id != speaker_id))
|
|
com_no++;
|
|
|
|
if (com_no == MAX_coms)
|
|
Fatal_error("too many speech commands");
|
|
|
|
speech_info[CONV_ID].coms[com_no].active = TRUE8;
|
|
|
|
// set the commands owner id
|
|
speech_info[CONV_ID].coms[com_no].id = speaker_id;
|
|
|
|
// set command type
|
|
speech_info[CONV_ID].coms[com_no].command = __REVERSE_CUSTOM_ANIM;
|
|
|
|
// set the target id parameter
|
|
Set_string(anim_name, speech_info[CONV_ID].coms[com_no].str_param1);
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
void _game_session::Exit_speech(uint32 id) {
|
|
// if we are talking then stop the sample
|
|
// called when this id takes a gun shot
|
|
// will drop out of speech and speech wav will stop playing if this id is currently speaking
|
|
|
|
if (logic_structs[id]->conversation_uid != NO_SPEECH_REQUEST) { // is talking
|
|
if (speech_info[CONV_ID].current_talker == (int32)id)
|
|
StopSpeechPlayback(); // target is talking
|
|
|
|
// will drop out
|
|
logic_structs[id]->conversation_uid = NO_SPEECH_REQUEST;
|
|
}
|
|
}
|
|
|
|
void _game_session::End_conversation(uint32 uid) {
|
|
// clean up conversation
|
|
|
|
uint32 j;
|
|
|
|
// decrease number of active conversations (from 1 to 0)
|
|
total_convs--;
|
|
|
|
// clean up and free the objects
|
|
// reset everyones conversation_uid if they are still registed as being in this conversation
|
|
if (speech_info[uid].total_subscribers)
|
|
for (j = 0; j < speech_info[uid].total_subscribers; j++) {
|
|
if (speech_info[uid].subscribers_requested[j] == player.Fetch_player_id()) {
|
|
if (logic_structs[player.Fetch_player_id()]->conversation_uid !=
|
|
NO_SPEECH_REQUEST) { // dont reset player if he never joined - for request failures on ladders, etc.
|
|
player.Pop_player_stat(); // stood or aiming, etc
|
|
}
|
|
}
|
|
|
|
if (speech_info[uid].subscribers_requested[j] >= total_objects)
|
|
Fatal_error("End_conversation find illegal id %d - total = %d", speech_info[uid].subscribers_requested[j],
|
|
speech_info[uid].total_subscribers);
|
|
|
|
Set_objects_conversation_uid(speech_info[uid].subscribers_requested[j], NO_SPEECH_REQUEST); // participant id, conversation uid - release the object
|
|
}
|
|
|
|
conv_focus = 0; // no one
|
|
|
|
if (text_speech_bloc->please_render == TRUE8) {
|
|
Zdebug("removing text bloc");
|
|
text_speech_bloc->please_render = FALSE8;
|
|
}
|
|
|
|
// end music
|
|
int32 r, p[1];
|
|
|
|
speak_end_music(r, p);
|
|
|
|
// reset list of subscribers
|
|
speech_info[uid].total_subscribers = 0;
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_wait_for_everyone(int32 &, int32 *) {
|
|
// waits until all pending coms are done
|
|
|
|
// are there any coms pending?
|
|
|
|
uint32 j;
|
|
|
|
for (j = 0; j < MAX_coms; j++)
|
|
if (speech_info[0].coms[j].active == TRUE8)
|
|
return (IR_REPEAT); // yes
|
|
|
|
// no
|
|
return (IR_CONT);
|
|
}
|
|
|
|
// ICON_LIST_MANAGER maximum must be this number +2 (inventory + remora)
|
|
#define MAX_number_menus 6
|
|
|
|
char menu_name_list[MAX_number_menus][4];
|
|
|
|
bool8 choosing[10];
|
|
uint32 item_count[10];
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_new_menu(int32 &, int32 *) {
|
|
// create a new menu
|
|
|
|
menu_number++;
|
|
if (menu_number >= MAX_number_menus) {
|
|
Fatal_error("too many menus MAX %d", MAX_number_menus);
|
|
}
|
|
|
|
g_oIconListManager->ResetList(menu_name_list[menu_number]);
|
|
|
|
sprintf(menu_name_list[menu_number], "m%02d", menu_number); // create a unique name
|
|
choosing[menu_number] = FALSE8;
|
|
item_count[menu_number] = 0;
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_close_menu(int32 &, int32 *) {
|
|
menu_number--;
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_add_chooser_icon(int32 &, int32 *params) {
|
|
// add icon to speech menu
|
|
|
|
// params 0 ascii name of icon
|
|
const char *icon_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
|
|
g_oIconListManager->AddIconToList(menu_name_list[menu_number], icon_name);
|
|
|
|
item_count[menu_number]++;
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_add_special_chooser_icon(int32 &, int32 *params) {
|
|
// add icon to speech menu
|
|
// these icons dont count as items that must be selected to finish with the menu
|
|
|
|
// params 0 ascii name of icon
|
|
const char *icon_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
|
|
Zdebug("speak_add_special_chooser_icon [%s]", icon_name);
|
|
|
|
g_oIconListManager->AddIconToList(menu_name_list[menu_number], icon_name);
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_user_chooser(int32 &, int32 *) {
|
|
// wait for user to choose an option
|
|
|
|
_input *state;
|
|
|
|
Zdebug("user chooser");
|
|
|
|
// check keys/pads/etc. to see what the user is trying to do
|
|
player.Update_input_state();
|
|
|
|
state = player.Fetch_input_state();
|
|
|
|
// first time in bring up the new menu
|
|
if (choosing[menu_number] == FALSE8) {
|
|
Zdebug("activating menu");
|
|
|
|
g_oIconListManager->ActivateIconMenu(menu_name_list[menu_number], FALSE8, FALSE8);
|
|
Zdebug("~activating menu");
|
|
choosing[menu_number] = TRUE8;
|
|
|
|
player.Push_control_mode(ACTOR_RELATIVE);
|
|
}
|
|
|
|
// safe to do it here as menu has been setup
|
|
g_oIconListManager->CycleInventoryLogic(*state);
|
|
|
|
if (g_oIconListManager->ItemHeld()) {
|
|
Zdebug("selected");
|
|
choosing[menu_number] = FALSE8; // reset
|
|
player.interact_lock = TRUE8;
|
|
|
|
player.Pop_control_mode();
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
return (IR_REPEAT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_chosen(int32 &result, int32 *params) {
|
|
// is the named icon the one that was chosen
|
|
|
|
const char *icon_name = (const char *)MemoryUtil::resolvePtr(params[0]);
|
|
|
|
if (g_oIconListManager->Holding(icon_name)) {
|
|
item_count[menu_number]--;
|
|
g_oIconListManager->RemoveIconFromList(menu_name_list[menu_number], icon_name);
|
|
g_oIconListManager->Drop();
|
|
result = TRUE8;
|
|
} else {
|
|
// no
|
|
result = FALSE8;
|
|
}
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_menu_still_active(int32 &result, int32 *) {
|
|
// says if menu is still active - i.e user hasnt chosen a quit icon
|
|
|
|
result = TRUE8;
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_menu_choices_remain(int32 &result, int32 *) {
|
|
// says if menu still has items left to choose
|
|
|
|
result = item_count[menu_number];
|
|
|
|
Zdebug("speak_menu_choices_remain reports %d options remain", result);
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_end_conversation(int32 &, int32 *) {
|
|
// kill current conversation at user request
|
|
// this will be called via Service_speech
|
|
|
|
End_conversation(CONV_ID);
|
|
|
|
return (IR_STOP);
|
|
}
|
|
|
|
mcodeFunctionReturnCodes _game_session::speak_end_menu(int32 &, int32 *) {
|
|
// end the current menu
|
|
|
|
// only destroy it if there are some options left
|
|
if (item_count[menu_number])
|
|
g_oIconListManager->DestroyList(menu_name_list[menu_number]);
|
|
|
|
// zero the items left count so that speak_menu_choices_remain will cancel the outer while loop
|
|
item_count[menu_number] = 0;
|
|
|
|
return (IR_CONT);
|
|
}
|
|
|
|
// Works out how int32 some text should be displayed to allow adequate time for it to be read. We might need to
|
|
// tinker with the formula in here 'till we get it right.
|
|
uint32 Get_reading_time(const char *pcString) {
|
|
uint32 nPos = 0;
|
|
uint32 nSpaceCount = 0;
|
|
|
|
// Base it on the wordcount.
|
|
do {
|
|
if (pcString[nPos] == ' ')
|
|
++nSpaceCount;
|
|
|
|
} while (pcString[nPos++] != '\0');
|
|
|
|
// Fixed 1/2 second plus 1/12 second for each word.
|
|
return (12 + (nSpaceCount * 2));
|
|
|
|
} // end Get_reading_time()
|
|
|
|
} // End of namespace ICB
|