1232 lines
39 KiB
C++
1232 lines
39 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* 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 "ags/engine/ac/dialog.h"
|
|
#include "ags/shared/ac/common.h"
|
|
#include "ags/engine/ac/character.h"
|
|
#include "ags/shared/ac/characterinfo.h"
|
|
#include "ags/shared/ac/dialogtopic.h"
|
|
#include "ags/engine/ac/display.h"
|
|
#include "ags/engine/ac/draw.h"
|
|
#include "ags/engine/ac/gamestate.h"
|
|
#include "ags/shared/ac/gamesetupstruct.h"
|
|
#include "ags/engine/ac/global_character.h"
|
|
#include "ags/engine/ac/global_dialog.h"
|
|
#include "ags/engine/ac/global_display.h"
|
|
#include "ags/engine/ac/global_game.h"
|
|
#include "ags/engine/ac/global_gui.h"
|
|
#include "ags/engine/ac/global_room.h"
|
|
#include "ags/engine/ac/global_translation.h"
|
|
#include "ags/engine/ac/keycode.h"
|
|
#include "ags/engine/ac/overlay.h"
|
|
#include "ags/engine/ac/mouse.h"
|
|
#include "ags/engine/ac/parser.h"
|
|
#include "ags/engine/ac/sys_events.h"
|
|
#include "ags/engine/ac/string.h"
|
|
#include "ags/engine/ac/dynobj/scriptdialogoptionsrendering.h"
|
|
#include "ags/engine/ac/dynobj/scriptdrawingsurface.h"
|
|
#include "ags/engine/ac/system.h"
|
|
#include "ags/engine/debugging/debug_log.h"
|
|
#include "ags/shared/font/fonts.h"
|
|
#include "ags/engine/script/cc_instance.h"
|
|
#include "ags/shared/gui/guimain.h"
|
|
#include "ags/shared/gui/guitextbox.h"
|
|
#include "ags/engine/main/game_run.h"
|
|
#include "ags/engine/platform/base/agsplatformdriver.h"
|
|
#include "ags/engine/script/script.h"
|
|
#include "ags/shared/ac/spritecache.h"
|
|
#include "ags/engine/gfx/ddb.h"
|
|
#include "ags/engine/gfx/gfx_util.h"
|
|
#include "ags/engine/gfx/graphicsdriver.h"
|
|
#include "ags/engine/ac/mouse.h"
|
|
#include "ags/engine/media/audio/audio_system.h"
|
|
#include "ags/shared/debugging/out.h"
|
|
#include "ags/engine/script/script_api.h"
|
|
#include "ags/engine/script/script_runtime.h"
|
|
#include "ags/engine/ac/dynobj/scriptstring.h"
|
|
#include "ags/globals.h"
|
|
#include "ags/ags.h"
|
|
|
|
namespace AGS3 {
|
|
|
|
using namespace AGS::Shared;
|
|
|
|
extern int in_new_room;
|
|
extern CharacterInfo *playerchar;
|
|
extern AGSPlatformDriver *platform;
|
|
extern int cur_mode, cur_cursor;
|
|
extern IGraphicsDriver *gfxDriver;
|
|
|
|
DialogTopic *dialog;
|
|
ScriptDialogOptionsRendering ccDialogOptionsRendering;
|
|
ScriptDrawingSurface *dialogOptionsRenderingSurface;
|
|
|
|
int said_speech_line; // used while in dialog to track whether screen needs updating
|
|
|
|
// Old dialog support
|
|
std::vector< std::shared_ptr<unsigned char> > old_dialog_scripts;
|
|
std::vector<String> old_speech_lines;
|
|
|
|
int said_text = 0;
|
|
int longestline = 0;
|
|
|
|
|
|
|
|
|
|
void Dialog_Start(ScriptDialog *sd) {
|
|
RunDialog(sd->id);
|
|
}
|
|
|
|
#define CHOSE_TEXTPARSER -3053
|
|
#define SAYCHOSEN_USEFLAG 1
|
|
#define SAYCHOSEN_YES 2
|
|
#define SAYCHOSEN_NO 3
|
|
|
|
int Dialog_DisplayOptions(ScriptDialog *sd, int sayChosenOption) {
|
|
if ((sayChosenOption < 1) || (sayChosenOption > 3))
|
|
quit("!Dialog.DisplayOptions: invalid parameter passed");
|
|
|
|
int chose = show_dialog_options(sd->id, sayChosenOption, (_GP(game).options[OPT_RUNGAMEDLGOPTS] != 0));
|
|
if (chose != CHOSE_TEXTPARSER) {
|
|
chose++;
|
|
}
|
|
return chose;
|
|
}
|
|
|
|
void Dialog_SetOptionState(ScriptDialog *sd, int option, int newState) {
|
|
SetDialogOption(sd->id, option, newState);
|
|
}
|
|
|
|
int Dialog_GetOptionState(ScriptDialog *sd, int option) {
|
|
return GetDialogOption(sd->id, option);
|
|
}
|
|
|
|
int Dialog_HasOptionBeenChosen(ScriptDialog *sd, int option) {
|
|
if ((option < 1) || (option > dialog[sd->id].numoptions))
|
|
quit("!Dialog.HasOptionBeenChosen: Invalid option number specified");
|
|
option--;
|
|
|
|
if (dialog[sd->id].optionflags[option] & DFLG_HASBEENCHOSEN)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void Dialog_SetHasOptionBeenChosen(ScriptDialog *sd, int option, bool chosen) {
|
|
if (option < 1 || option > dialog[sd->id].numoptions) {
|
|
quit("!Dialog.HasOptionBeenChosen: Invalid option number specified");
|
|
}
|
|
option--;
|
|
if (chosen) {
|
|
dialog[sd->id].optionflags[option] |= DFLG_HASBEENCHOSEN;
|
|
} else {
|
|
dialog[sd->id].optionflags[option] &= ~DFLG_HASBEENCHOSEN;
|
|
}
|
|
}
|
|
|
|
int Dialog_GetOptionCount(ScriptDialog *sd) {
|
|
return dialog[sd->id].numoptions;
|
|
}
|
|
|
|
int Dialog_GetShowTextParser(ScriptDialog *sd) {
|
|
return (dialog[sd->id].topicFlags & DTFLG_SHOWPARSER) ? 1 : 0;
|
|
}
|
|
|
|
const char *Dialog_GetOptionText(ScriptDialog *sd, int option) {
|
|
if ((option < 1) || (option > dialog[sd->id].numoptions))
|
|
quit("!Dialog.GetOptionText: Invalid option number specified");
|
|
|
|
option--;
|
|
|
|
return CreateNewScriptString(get_translation(dialog[sd->id].optionnames[option]));
|
|
}
|
|
|
|
int Dialog_GetID(ScriptDialog *sd) {
|
|
return sd->id;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
#define RUN_DIALOG_STAY -1
|
|
#define RUN_DIALOG_STOP_DIALOG -2
|
|
#define RUN_DIALOG_GOTO_PREVIOUS -4
|
|
// dialog manager stuff
|
|
|
|
void get_dialog_script_parameters(unsigned char *&script, unsigned short *param1, unsigned short *param2) {
|
|
script++;
|
|
*param1 = *script;
|
|
script++;
|
|
*param1 += *script * 256;
|
|
script++;
|
|
|
|
if (param2) {
|
|
*param2 = *script;
|
|
script++;
|
|
*param2 += *script * 256;
|
|
script++;
|
|
}
|
|
}
|
|
|
|
int run_dialog_script(DialogTopic *dtpp, int dialogID, int offse, int optionIndex) {
|
|
said_speech_line = 0;
|
|
int result = RUN_DIALOG_STAY;
|
|
|
|
if (_G(dialogScriptsInst)) {
|
|
char funcName[100];
|
|
sprintf(funcName, "_run_dialog%d", dialogID);
|
|
RunTextScriptIParam(_G(dialogScriptsInst), funcName, RuntimeScriptValue().SetInt32(optionIndex));
|
|
result = _G(dialogScriptsInst)->returnValue;
|
|
} else {
|
|
// old dialog format
|
|
if (offse == -1)
|
|
return result;
|
|
|
|
unsigned char *script = old_dialog_scripts[dialogID].get() + offse;
|
|
|
|
unsigned short param1 = 0;
|
|
unsigned short param2 = 0;
|
|
bool script_running = true;
|
|
|
|
while (script_running) {
|
|
switch (*script) {
|
|
case DCMD_SAY:
|
|
get_dialog_script_parameters(script, ¶m1, ¶m2);
|
|
|
|
if (param1 == DCHAR_PLAYER)
|
|
param1 = _GP(game).playercharacter;
|
|
|
|
if (param1 == DCHAR_NARRATOR)
|
|
Display(get_translation(old_speech_lines[param2]));
|
|
else
|
|
DisplaySpeech(get_translation(old_speech_lines[param2]), param1);
|
|
|
|
said_speech_line = 1;
|
|
break;
|
|
|
|
case DCMD_OPTOFF:
|
|
get_dialog_script_parameters(script, ¶m1, nullptr);
|
|
SetDialogOption(dialogID, param1 + 1, 0, true);
|
|
break;
|
|
|
|
case DCMD_OPTON:
|
|
get_dialog_script_parameters(script, ¶m1, nullptr);
|
|
SetDialogOption(dialogID, param1 + 1, DFLG_ON, true);
|
|
break;
|
|
|
|
case DCMD_RETURN:
|
|
script_running = false;
|
|
break;
|
|
|
|
case DCMD_STOPDIALOG:
|
|
result = RUN_DIALOG_STOP_DIALOG;
|
|
script_running = false;
|
|
break;
|
|
|
|
case DCMD_OPTOFFFOREVER:
|
|
get_dialog_script_parameters(script, ¶m1, nullptr);
|
|
SetDialogOption(dialogID, param1 + 1, DFLG_OFFPERM, true);
|
|
break;
|
|
|
|
case DCMD_RUNTEXTSCRIPT:
|
|
get_dialog_script_parameters(script, ¶m1, nullptr);
|
|
result = run_dialog_request(param1);
|
|
script_running = (result == RUN_DIALOG_STAY);
|
|
break;
|
|
|
|
case DCMD_GOTODIALOG:
|
|
get_dialog_script_parameters(script, ¶m1, nullptr);
|
|
result = param1;
|
|
script_running = false;
|
|
break;
|
|
|
|
case DCMD_PLAYSOUND:
|
|
get_dialog_script_parameters(script, ¶m1, nullptr);
|
|
play_sound(param1);
|
|
break;
|
|
|
|
case DCMD_ADDINV:
|
|
get_dialog_script_parameters(script, ¶m1, nullptr);
|
|
add_inventory(param1);
|
|
break;
|
|
|
|
case DCMD_SETSPCHVIEW:
|
|
get_dialog_script_parameters(script, ¶m1, ¶m2);
|
|
SetCharacterSpeechView(param1, param2);
|
|
break;
|
|
|
|
case DCMD_NEWROOM:
|
|
get_dialog_script_parameters(script, ¶m1, nullptr);
|
|
NewRoom(param1);
|
|
in_new_room = 1;
|
|
result = RUN_DIALOG_STOP_DIALOG;
|
|
script_running = false;
|
|
break;
|
|
|
|
case DCMD_SETGLOBALINT:
|
|
get_dialog_script_parameters(script, ¶m1, ¶m2);
|
|
SetGlobalInt(param1, param2);
|
|
break;
|
|
|
|
case DCMD_GIVESCORE:
|
|
get_dialog_script_parameters(script, ¶m1, nullptr);
|
|
GiveScore(param1);
|
|
break;
|
|
|
|
case DCMD_GOTOPREVIOUS:
|
|
result = RUN_DIALOG_GOTO_PREVIOUS;
|
|
script_running = false;
|
|
break;
|
|
|
|
case DCMD_LOSEINV:
|
|
get_dialog_script_parameters(script, ¶m1, nullptr);
|
|
lose_inventory(param1);
|
|
break;
|
|
|
|
case DCMD_ENDSCRIPT:
|
|
result = RUN_DIALOG_STOP_DIALOG;
|
|
script_running = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in_new_room > 0)
|
|
return RUN_DIALOG_STOP_DIALOG;
|
|
|
|
if (said_speech_line > 0) {
|
|
// the line below fixes the problem with the close-up face remaining on the
|
|
// screen after they finish talking; however, it makes the dialog options
|
|
// area flicker when going between topics.
|
|
DisableInterface();
|
|
UpdateGameOnce(); // redraw the screen to make sure it looks right
|
|
EnableInterface();
|
|
// if we're not about to abort the dialog, switch back to arrow
|
|
if (result != RUN_DIALOG_STOP_DIALOG)
|
|
set_mouse_cursor(CURS_ARROW);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int write_dialog_options(Bitmap *ds, bool ds_has_alpha, int dlgxp, int curyp, int numdisp, int mouseison, int areawid,
|
|
int bullet_wid, int usingfont, DialogTopic *dtop, char *disporder, short *dispyp,
|
|
int linespacing, int utextcol, int padding) {
|
|
int ww;
|
|
|
|
color_t text_color;
|
|
for (ww = 0; ww < numdisp; ww++) {
|
|
|
|
if ((dtop->optionflags[(int)disporder[ww]] & DFLG_HASBEENCHOSEN) &&
|
|
(_GP(play).read_dialog_option_colour >= 0)) {
|
|
// 'read' colour
|
|
text_color = ds->GetCompatibleColor(_GP(play).read_dialog_option_colour);
|
|
} else {
|
|
// 'unread' colour
|
|
text_color = ds->GetCompatibleColor(playerchar->talkcolor);
|
|
}
|
|
|
|
if (mouseison == ww) {
|
|
if (text_color == ds->GetCompatibleColor(utextcol))
|
|
text_color = ds->GetCompatibleColor(13); // the normal colour is the same as highlight col
|
|
else text_color = ds->GetCompatibleColor(utextcol);
|
|
}
|
|
|
|
break_up_text_into_lines(get_translation(dtop->optionnames[(int)disporder[ww]]), Lines, areawid - (2 * padding + 2 + bullet_wid), usingfont);
|
|
dispyp[ww] = curyp;
|
|
if (_GP(game).dialog_bullet > 0) {
|
|
draw_gui_sprite_v330(ds, _GP(game).dialog_bullet, dlgxp, curyp, ds_has_alpha);
|
|
}
|
|
if (_GP(game).options[OPT_DIALOGNUMBERED] == kDlgOptNumbering) {
|
|
char tempbfr[20];
|
|
int actualpicwid = 0;
|
|
if (_GP(game).dialog_bullet > 0)
|
|
actualpicwid = _GP(game).SpriteInfos[_GP(game).dialog_bullet].Width + 3;
|
|
|
|
sprintf(tempbfr, "%d.", ww + 1);
|
|
wouttext_outline(ds, dlgxp + actualpicwid, curyp, usingfont, text_color, tempbfr);
|
|
}
|
|
for (size_t cc = 0; cc < Lines.Count(); cc++) {
|
|
wouttext_outline(ds, dlgxp + ((cc == 0) ? 0 : 9) + bullet_wid, curyp, usingfont, text_color, Lines[cc]);
|
|
curyp += linespacing;
|
|
}
|
|
if (ww < numdisp - 1)
|
|
curyp += data_to_game_coord(_GP(game).options[OPT_DIALOGGAP]);
|
|
}
|
|
return curyp;
|
|
}
|
|
|
|
|
|
|
|
#define GET_OPTIONS_HEIGHT {\
|
|
needheight = 0;\
|
|
for (int i = 0; i < numdisp; ++i) {\
|
|
break_up_text_into_lines(get_translation(dtop->optionnames[(int)disporder[i]]), Lines, areawid-(2*padding+2+bullet_wid), usingfont);\
|
|
needheight += getheightoflines(usingfont, Lines.Count()) + data_to_game_coord(_GP(game).options[OPT_DIALOGGAP]);\
|
|
}\
|
|
if (parserInput) needheight += parserInput->Height + data_to_game_coord(_GP(game).options[OPT_DIALOGGAP]);\
|
|
}
|
|
|
|
|
|
void draw_gui_for_dialog_options(Bitmap *ds, GUIMain *guib, int dlgxp, int dlgyp) {
|
|
if (guib->BgColor != 0) {
|
|
color_t draw_color = ds->GetCompatibleColor(guib->BgColor);
|
|
ds->FillRect(Rect(dlgxp, dlgyp, dlgxp + guib->Width, dlgyp + guib->Height), draw_color);
|
|
}
|
|
if (guib->BgImage > 0)
|
|
GfxUtil::DrawSpriteWithTransparency(ds, _GP(spriteset)[guib->BgImage], dlgxp, dlgyp);
|
|
}
|
|
|
|
bool get_custom_dialog_options_dimensions(int dlgnum) {
|
|
ccDialogOptionsRendering.Reset();
|
|
ccDialogOptionsRendering.dialogID = dlgnum;
|
|
|
|
_GP(getDialogOptionsDimensionsFunc).params[0].SetDynamicObject(&ccDialogOptionsRendering, &ccDialogOptionsRendering);
|
|
run_function_on_non_blocking_thread(&_GP(getDialogOptionsDimensionsFunc));
|
|
|
|
if ((ccDialogOptionsRendering.width > 0) &&
|
|
(ccDialogOptionsRendering.height > 0)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#define MAX_TOPIC_HISTORY 50
|
|
#define DLG_OPTION_PARSER 99
|
|
|
|
struct DialogOptions {
|
|
int dlgnum;
|
|
bool runGameLoopsInBackground;
|
|
|
|
int dlgxp;
|
|
int dlgyp;
|
|
int dialog_abs_x; // absolute dialog position on screen
|
|
int padding;
|
|
int usingfont;
|
|
int lineheight;
|
|
int linespacing;
|
|
int curswas;
|
|
int bullet_wid;
|
|
int needheight;
|
|
IDriverDependantBitmap *ddb;
|
|
Bitmap *subBitmap;
|
|
GUITextBox *parserInput;
|
|
DialogTopic *dtop;
|
|
|
|
char disporder[MAXTOPICOPTIONS];
|
|
short dispyp[MAXTOPICOPTIONS];
|
|
|
|
int numdisp;
|
|
int chose;
|
|
|
|
Bitmap *tempScrn;
|
|
int parserActivated;
|
|
|
|
int curyp;
|
|
bool wantRefresh;
|
|
bool usingCustomRendering;
|
|
int orixp;
|
|
int oriyp;
|
|
int areawid;
|
|
int is_textwindow;
|
|
int dirtyx;
|
|
int dirtyy;
|
|
int dirtywidth;
|
|
int dirtyheight;
|
|
|
|
int mouseison;
|
|
int mousewason;
|
|
|
|
int forecol;
|
|
|
|
void Prepare(int _dlgnum, bool _runGameLoopsInBackground);
|
|
void Show();
|
|
void Redraw();
|
|
bool Run();
|
|
void Close();
|
|
};
|
|
|
|
void DialogOptions::Prepare(int _dlgnum, bool _runGameLoopsInBackground) {
|
|
dlgnum = _dlgnum;
|
|
runGameLoopsInBackground = _runGameLoopsInBackground;
|
|
|
|
dlgyp = get_fixed_pixel_size(160);
|
|
usingfont = FONT_NORMAL;
|
|
lineheight = getfontheight_outlined(usingfont);
|
|
linespacing = getfontspacing_outlined(usingfont);
|
|
curswas = cur_cursor;
|
|
bullet_wid = 0;
|
|
ddb = nullptr;
|
|
subBitmap = nullptr;
|
|
parserInput = nullptr;
|
|
dtop = nullptr;
|
|
|
|
if ((dlgnum < 0) || (dlgnum >= _GP(game).numdialog))
|
|
quit("!RunDialog: invalid dialog number specified");
|
|
|
|
can_run_delayed_command();
|
|
|
|
_GP(play).in_conversation ++;
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
if (_GP(game).dialog_bullet > 0)
|
|
bullet_wid = _GP(game).SpriteInfos[_GP(game).dialog_bullet].Width + 3;
|
|
|
|
// numbered options, leave space for the numbers
|
|
if (_GP(game).options[OPT_DIALOGNUMBERED] == kDlgOptNumbering)
|
|
bullet_wid += wgettextwidth_compensate("9. ", usingfont);
|
|
|
|
said_text = 0;
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
const Rect &ui_view = _GP(play).GetUIViewport();
|
|
tempScrn = BitmapHelper::CreateBitmap(ui_view.GetWidth(), ui_view.GetHeight(), _GP(game).GetColorDepth());
|
|
|
|
set_mouse_cursor(CURS_ARROW);
|
|
|
|
dtop = &dialog[dlgnum];
|
|
|
|
chose = -1;
|
|
numdisp = 0;
|
|
|
|
parserActivated = 0;
|
|
if ((dtop->topicFlags & DTFLG_SHOWPARSER) && (_GP(play).disable_dialog_parser == 0)) {
|
|
parserInput = new GUITextBox();
|
|
parserInput->Height = lineheight + get_fixed_pixel_size(4);
|
|
parserInput->SetShowBorder(true);
|
|
parserInput->Font = usingfont;
|
|
}
|
|
|
|
numdisp = 0;
|
|
for (int i = 0; i < dtop->numoptions; ++i) {
|
|
if ((dtop->optionflags[i] & DFLG_ON) == 0) continue;
|
|
ensure_text_valid_for_font(dtop->optionnames[i], usingfont);
|
|
disporder[numdisp] = i;
|
|
numdisp++;
|
|
}
|
|
}
|
|
|
|
void DialogOptions::Show() {
|
|
if (numdisp < 1) quit("!DoDialog: all options have been turned off");
|
|
// Don't display the options if there is only one and the parser
|
|
// is not enabled.
|
|
if (!((numdisp > 1) || (parserInput != nullptr) || (_GP(play).show_single_dialog_option))) {
|
|
chose = disporder[0]; // only one choice, so select it
|
|
return;
|
|
}
|
|
|
|
is_textwindow = 0;
|
|
forecol = _GP(play).dialog_options_highlight_color;
|
|
|
|
mouseison = -1;
|
|
mousewason = -10;
|
|
const Rect &ui_view = _GP(play).GetUIViewport();
|
|
dirtyx = 0;
|
|
dirtyy = 0;
|
|
dirtywidth = ui_view.GetWidth();
|
|
dirtyheight = ui_view.GetHeight();
|
|
usingCustomRendering = false;
|
|
|
|
|
|
dlgxp = 1;
|
|
if (get_custom_dialog_options_dimensions(dlgnum)) {
|
|
usingCustomRendering = true;
|
|
dirtyx = data_to_game_coord(ccDialogOptionsRendering.x);
|
|
dirtyy = data_to_game_coord(ccDialogOptionsRendering.y);
|
|
dirtywidth = data_to_game_coord(ccDialogOptionsRendering.width);
|
|
dirtyheight = data_to_game_coord(ccDialogOptionsRendering.height);
|
|
dialog_abs_x = dirtyx;
|
|
} else if (_GP(game).options[OPT_DIALOGIFACE] > 0) {
|
|
GUIMain *guib = &_GP(guis)[_GP(game).options[OPT_DIALOGIFACE]];
|
|
if (guib->IsTextWindow()) {
|
|
// text-window, so do the QFG4-style speech options
|
|
is_textwindow = 1;
|
|
forecol = guib->FgColor;
|
|
} else {
|
|
dlgxp = guib->X;
|
|
dlgyp = guib->Y;
|
|
|
|
dirtyx = dlgxp;
|
|
dirtyy = dlgyp;
|
|
dirtywidth = guib->Width;
|
|
dirtyheight = guib->Height;
|
|
dialog_abs_x = guib->X;
|
|
|
|
areawid = guib->Width - 5;
|
|
padding = TEXTWINDOW_PADDING_DEFAULT;
|
|
|
|
GET_OPTIONS_HEIGHT
|
|
|
|
if (_GP(game).options[OPT_DIALOGUPWARDS]) {
|
|
// They want the options upwards from the bottom
|
|
dlgyp = (guib->Y + guib->Height) - needheight;
|
|
}
|
|
|
|
}
|
|
} else {
|
|
//dlgyp=(_GP(play).viewport.GetHeight()-numdisp*txthit)-1;
|
|
const Rect &uiView = _GP(play).GetUIViewport();
|
|
areawid = uiView.GetWidth() - 5;
|
|
padding = TEXTWINDOW_PADDING_DEFAULT;
|
|
GET_OPTIONS_HEIGHT
|
|
dlgyp = uiView.GetHeight() - needheight;
|
|
|
|
dirtyx = 0;
|
|
dirtyy = dlgyp - 1;
|
|
dirtywidth = uiView.GetWidth();
|
|
dirtyheight = uiView.GetHeight() - dirtyy;
|
|
dialog_abs_x = 0;
|
|
}
|
|
if (!is_textwindow)
|
|
areawid -= data_to_game_coord(_GP(play).dialog_options_x) * 2;
|
|
|
|
orixp = dlgxp;
|
|
oriyp = dlgyp;
|
|
wantRefresh = false;
|
|
mouseison = -10;
|
|
|
|
update_polled_stuff_if_runtime();
|
|
if (!_GP(play).mouse_cursor_hidden)
|
|
ags_domouse(DOMOUSE_ENABLE);
|
|
update_polled_stuff_if_runtime();
|
|
|
|
Redraw();
|
|
while (Run() && !SHOULD_QUIT) {
|
|
}
|
|
|
|
if (!_GP(play).mouse_cursor_hidden)
|
|
ags_domouse(DOMOUSE_DISABLE);
|
|
}
|
|
|
|
void DialogOptions::Redraw() {
|
|
wantRefresh = true;
|
|
|
|
if (usingCustomRendering) {
|
|
tempScrn = recycle_bitmap(tempScrn, _GP(game).GetColorDepth(),
|
|
data_to_game_coord(ccDialogOptionsRendering.width),
|
|
data_to_game_coord(ccDialogOptionsRendering.height));
|
|
}
|
|
|
|
tempScrn->ClearTransparent();
|
|
Bitmap *ds = tempScrn;
|
|
|
|
dlgxp = orixp;
|
|
dlgyp = oriyp;
|
|
const Rect &ui_view = _GP(play).GetUIViewport();
|
|
|
|
bool options_surface_has_alpha = false;
|
|
|
|
if (usingCustomRendering) {
|
|
ccDialogOptionsRendering.surfaceToRenderTo = dialogOptionsRenderingSurface;
|
|
ccDialogOptionsRendering.surfaceAccessed = false;
|
|
dialogOptionsRenderingSurface->linkedBitmapOnly = tempScrn;
|
|
dialogOptionsRenderingSurface->hasAlphaChannel = ccDialogOptionsRendering.hasAlphaChannel;
|
|
options_surface_has_alpha = dialogOptionsRenderingSurface->hasAlphaChannel != 0;
|
|
|
|
_GP(renderDialogOptionsFunc).params[0].SetDynamicObject(&ccDialogOptionsRendering, &ccDialogOptionsRendering);
|
|
run_function_on_non_blocking_thread(&_GP(renderDialogOptionsFunc));
|
|
|
|
if (!ccDialogOptionsRendering.surfaceAccessed)
|
|
debug_script_warn("dialog_options_get_dimensions was implemented, but no dialog_options_render function drew anything to the surface");
|
|
|
|
if (parserInput) {
|
|
parserInput->X = data_to_game_coord(ccDialogOptionsRendering.parserTextboxX);
|
|
curyp = data_to_game_coord(ccDialogOptionsRendering.parserTextboxY);
|
|
areawid = data_to_game_coord(ccDialogOptionsRendering.parserTextboxWidth);
|
|
if (areawid == 0)
|
|
areawid = tempScrn->GetWidth();
|
|
}
|
|
ccDialogOptionsRendering.needRepaint = false;
|
|
} else if (is_textwindow) {
|
|
// text window behind the options
|
|
areawid = data_to_game_coord(_GP(play).max_dialogoption_width);
|
|
int biggest = 0;
|
|
padding = _GP(guis)[_GP(game).options[OPT_DIALOGIFACE]].Padding;
|
|
for (int i = 0; i < numdisp; ++i) {
|
|
break_up_text_into_lines(get_translation(dtop->optionnames[(int)disporder[i]]), Lines, areawid - ((2 * padding + 2) + bullet_wid), usingfont);
|
|
if (longestline > biggest)
|
|
biggest = longestline;
|
|
}
|
|
if (biggest < areawid - ((2 * padding + 6) + bullet_wid))
|
|
areawid = biggest + ((2 * padding + 6) + bullet_wid);
|
|
|
|
if (areawid < data_to_game_coord(_GP(play).min_dialogoption_width)) {
|
|
areawid = data_to_game_coord(_GP(play).min_dialogoption_width);
|
|
if (_GP(play).min_dialogoption_width > _GP(play).max_dialogoption_width)
|
|
quit("!game.min_dialogoption_width is larger than game.max_dialogoption_width");
|
|
}
|
|
|
|
GET_OPTIONS_HEIGHT
|
|
|
|
int savedwid = areawid;
|
|
int txoffs = 0, tyoffs = 0, yspos = ui_view.GetHeight() / 2 - (2 * padding + needheight) / 2;
|
|
int xspos = ui_view.GetWidth() / 2 - areawid / 2;
|
|
// shift window to the right if QG4-style full-screen pic
|
|
if ((_GP(game).options[OPT_SPEECHTYPE] == 3) && (said_text > 0))
|
|
xspos = (ui_view.GetWidth() - areawid) - get_fixed_pixel_size(10);
|
|
|
|
// needs to draw the right text window, not the default
|
|
Bitmap *text_window_ds = nullptr;
|
|
draw_text_window(&text_window_ds, false, &txoffs, &tyoffs, &xspos, &yspos, &areawid, nullptr, needheight, _GP(game).options[OPT_DIALOGIFACE]);
|
|
options_surface_has_alpha = _GP(guis)[_GP(game).options[OPT_DIALOGIFACE]].HasAlphaChannel();
|
|
// since draw_text_window incrases the width, restore it
|
|
areawid = savedwid;
|
|
|
|
dirtyx = xspos;
|
|
dirtyy = yspos;
|
|
dirtywidth = text_window_ds->GetWidth();
|
|
dirtyheight = text_window_ds->GetHeight();
|
|
dialog_abs_x = txoffs + xspos;
|
|
|
|
GfxUtil::DrawSpriteWithTransparency(ds, text_window_ds, xspos, yspos);
|
|
// TODO: here we rely on draw_text_window always assigning new bitmap to text_window_ds;
|
|
// should make this more explicit
|
|
delete text_window_ds;
|
|
|
|
// Ignore the dialog_options_x/y offsets when using a text window
|
|
txoffs += xspos;
|
|
tyoffs += yspos;
|
|
dlgyp = tyoffs;
|
|
curyp = write_dialog_options(ds, options_surface_has_alpha, txoffs, tyoffs, numdisp, mouseison, areawid, bullet_wid, usingfont, dtop, disporder, dispyp, linespacing, forecol, padding);
|
|
if (parserInput)
|
|
parserInput->X = txoffs;
|
|
} else {
|
|
|
|
if (wantRefresh) {
|
|
// redraw the black background so that anti-alias
|
|
// fonts don't re-alias themselves
|
|
if (_GP(game).options[OPT_DIALOGIFACE] == 0) {
|
|
color_t draw_color = ds->GetCompatibleColor(16);
|
|
ds->FillRect(Rect(0, dlgyp - 1, ui_view.GetWidth() - 1, ui_view.GetHeight() - 1), draw_color);
|
|
} else {
|
|
GUIMain *guib = &_GP(guis)[_GP(game).options[OPT_DIALOGIFACE]];
|
|
if (!guib->IsTextWindow())
|
|
draw_gui_for_dialog_options(ds, guib, dlgxp, dlgyp);
|
|
}
|
|
}
|
|
|
|
dirtyx = 0;
|
|
dirtywidth = ui_view.GetWidth();
|
|
|
|
if (_GP(game).options[OPT_DIALOGIFACE] > 0) {
|
|
// the whole GUI area should be marked dirty in order
|
|
// to ensure it gets drawn
|
|
GUIMain *guib = &_GP(guis)[_GP(game).options[OPT_DIALOGIFACE]];
|
|
dirtyheight = guib->Height;
|
|
dirtyy = dlgyp;
|
|
options_surface_has_alpha = guib->HasAlphaChannel();
|
|
} else {
|
|
dirtyy = dlgyp - 1;
|
|
dirtyheight = needheight + 1;
|
|
options_surface_has_alpha = false;
|
|
}
|
|
|
|
dlgxp += data_to_game_coord(_GP(play).dialog_options_x);
|
|
dlgyp += data_to_game_coord(_GP(play).dialog_options_y);
|
|
|
|
// if they use a negative dialog_options_y, make sure the
|
|
// area gets marked as dirty
|
|
if (dlgyp < dirtyy)
|
|
dirtyy = dlgyp;
|
|
|
|
//curyp = dlgyp + 1;
|
|
curyp = dlgyp;
|
|
curyp = write_dialog_options(ds, options_surface_has_alpha, dlgxp, curyp, numdisp, mouseison, areawid, bullet_wid, usingfont, dtop, disporder, dispyp, linespacing, forecol, padding);
|
|
|
|
/*if (curyp > _GP(play).viewport.GetHeight()) {
|
|
dlgyp = _GP(play).viewport.GetHeight() - (curyp - dlgyp);
|
|
ds->FillRect(Rect(0,dlgyp-1,_GP(play).viewport.GetWidth()-1,_GP(play).viewport.GetHeight()-1);
|
|
goto redraw_options;
|
|
}*/
|
|
if (parserInput)
|
|
parserInput->X = dlgxp;
|
|
}
|
|
|
|
if (parserInput) {
|
|
// Set up the text box, if present
|
|
parserInput->Y = curyp + data_to_game_coord(_GP(game).options[OPT_DIALOGGAP]);
|
|
parserInput->Width = areawid - get_fixed_pixel_size(10);
|
|
parserInput->TextColor = playerchar->talkcolor;
|
|
if (mouseison == DLG_OPTION_PARSER)
|
|
parserInput->TextColor = forecol;
|
|
|
|
if (_GP(game).dialog_bullet) { // the parser X will get moved in a second
|
|
draw_gui_sprite_v330(ds, _GP(game).dialog_bullet, parserInput->X, parserInput->Y, options_surface_has_alpha);
|
|
}
|
|
|
|
parserInput->Width -= bullet_wid;
|
|
parserInput->X += bullet_wid;
|
|
|
|
parserInput->Draw(ds);
|
|
parserInput->IsActivated = false;
|
|
}
|
|
|
|
wantRefresh = false;
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
subBitmap = recycle_bitmap(subBitmap, tempScrn->GetColorDepth(), dirtywidth, dirtyheight);
|
|
subBitmap = ReplaceBitmapWithSupportedFormat(subBitmap);
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
if (usingCustomRendering) {
|
|
subBitmap->Blit(tempScrn, 0, 0, 0, 0, tempScrn->GetWidth(), tempScrn->GetHeight());
|
|
invalidate_rect(dirtyx, dirtyy, dirtyx + subBitmap->GetWidth(), dirtyy + subBitmap->GetHeight(), false);
|
|
} else {
|
|
subBitmap->Blit(tempScrn, dirtyx, dirtyy, 0, 0, dirtywidth, dirtyheight);
|
|
}
|
|
|
|
if ((ddb != nullptr) &&
|
|
((ddb->GetWidth() != dirtywidth) ||
|
|
(ddb->GetHeight() != dirtyheight))) {
|
|
gfxDriver->DestroyDDB(ddb);
|
|
ddb = nullptr;
|
|
}
|
|
|
|
if (ddb == nullptr)
|
|
ddb = gfxDriver->CreateDDBFromBitmap(subBitmap, options_surface_has_alpha, false);
|
|
else
|
|
gfxDriver->UpdateDDBFromBitmap(ddb, subBitmap, options_surface_has_alpha);
|
|
|
|
if (runGameLoopsInBackground) {
|
|
render_graphics(ddb, dirtyx, dirtyy);
|
|
}
|
|
}
|
|
|
|
bool DialogOptions::Run() {
|
|
const bool new_custom_render = usingCustomRendering && _GP(game).options[OPT_DIALOGOPTIONSAPI] >= 0;
|
|
|
|
if (runGameLoopsInBackground) {
|
|
_GP(play).disabled_user_interface++;
|
|
UpdateGameOnce(false, ddb, dirtyx, dirtyy);
|
|
_GP(play).disabled_user_interface--;
|
|
} else {
|
|
update_audio_system_on_game_loop();
|
|
render_graphics(ddb, dirtyx, dirtyy);
|
|
}
|
|
|
|
if (new_custom_render) {
|
|
_GP(runDialogOptionRepExecFunc).params[0].SetDynamicObject(&ccDialogOptionsRendering, &ccDialogOptionsRendering);
|
|
run_function_on_non_blocking_thread(&_GP(runDialogOptionRepExecFunc));
|
|
}
|
|
|
|
int gkey;
|
|
if (run_service_key_controls(gkey) && !_GP(play).IsIgnoringInput()) {
|
|
if (parserInput) {
|
|
wantRefresh = true;
|
|
// type into the parser
|
|
if ((gkey == 361) || ((gkey == ' ') && (strlen(parserInput->Text) == 0))) {
|
|
// write previous contents into textbox (F3 or Space when box is empty)
|
|
for (unsigned int i = strlen(parserInput->Text); i < strlen(_GP(play).lastParserEntry); i++) {
|
|
parserInput->OnKeyPress(_GP(play).lastParserEntry[i]);
|
|
}
|
|
//ags_domouse(DOMOUSE_DISABLE);
|
|
Redraw();
|
|
return true; // continue running loop
|
|
} else if ((gkey >= 32) || (gkey == 13) || (gkey == 8)) {
|
|
parserInput->OnKeyPress(gkey);
|
|
if (!parserInput->IsActivated) {
|
|
//ags_domouse(DOMOUSE_DISABLE);
|
|
Redraw();
|
|
return true; // continue running loop
|
|
}
|
|
}
|
|
} else if (new_custom_render) {
|
|
_GP(runDialogOptionKeyPressHandlerFunc).params[0].SetDynamicObject(&ccDialogOptionsRendering, &ccDialogOptionsRendering);
|
|
_GP(runDialogOptionKeyPressHandlerFunc).params[1].SetInt32(GetKeyForKeyPressCb(gkey));
|
|
run_function_on_non_blocking_thread(&_GP(runDialogOptionKeyPressHandlerFunc));
|
|
}
|
|
// Allow selection of options by keyboard shortcuts
|
|
else if (_GP(game).options[OPT_DIALOGNUMBERED] >= kDlgOptKeysOnly &&
|
|
gkey >= '1' && gkey <= '9') {
|
|
gkey -= '1';
|
|
if (gkey < numdisp) {
|
|
chose = disporder[gkey];
|
|
return false; // end dialog options running loop
|
|
}
|
|
}
|
|
}
|
|
mousewason = mouseison;
|
|
mouseison = -1;
|
|
if (new_custom_render); // do not automatically detect option under mouse
|
|
else if (usingCustomRendering) {
|
|
if ((_G(mousex) >= dirtyx) && (_G(mousey) >= dirtyy) &&
|
|
(_G(mousex) < dirtyx + tempScrn->GetWidth()) &&
|
|
(_G(mousey) < dirtyy + tempScrn->GetHeight())) {
|
|
_GP(getDialogOptionUnderCursorFunc).params[0].SetDynamicObject(&ccDialogOptionsRendering, &ccDialogOptionsRendering);
|
|
run_function_on_non_blocking_thread(&_GP(getDialogOptionUnderCursorFunc));
|
|
|
|
if (!_GP(getDialogOptionUnderCursorFunc).atLeastOneImplementationExists)
|
|
quit("!The script function dialog_options_get_active is not implemented. It must be present to use a custom dialogue system.");
|
|
|
|
mouseison = ccDialogOptionsRendering.activeOptionID;
|
|
} else {
|
|
ccDialogOptionsRendering.activeOptionID = -1;
|
|
}
|
|
} else if (_G(mousex) >= dialog_abs_x && _G(mousex) < (dialog_abs_x + areawid) &&
|
|
_G(mousey) >= dlgyp && _G(mousey) < curyp) {
|
|
mouseison = numdisp - 1;
|
|
for (int i = 0; i < numdisp; ++i) {
|
|
if (_G(mousey) < dispyp[i]) {
|
|
mouseison = i - 1;
|
|
break;
|
|
}
|
|
}
|
|
if ((mouseison < 0) | (mouseison >= numdisp)) mouseison = -1;
|
|
}
|
|
|
|
if (parserInput != nullptr) {
|
|
int relativeMousey = _G(mousey);
|
|
if (usingCustomRendering)
|
|
relativeMousey -= dirtyy;
|
|
|
|
if ((relativeMousey > parserInput->Y) &&
|
|
(relativeMousey < parserInput->Y + parserInput->Height))
|
|
mouseison = DLG_OPTION_PARSER;
|
|
|
|
if (parserInput->IsActivated)
|
|
parserActivated = 1;
|
|
}
|
|
|
|
int mouseButtonPressed = NONE;
|
|
int mouseWheelTurn = 0;
|
|
if (run_service_mb_controls(mouseButtonPressed, mouseWheelTurn) && mouseButtonPressed >= 0 &&
|
|
!_GP(play).IsIgnoringInput()) {
|
|
if (mouseison < 0 && !new_custom_render) {
|
|
if (usingCustomRendering) {
|
|
_GP(runDialogOptionMouseClickHandlerFunc).params[0].SetDynamicObject(&ccDialogOptionsRendering, &ccDialogOptionsRendering);
|
|
_GP(runDialogOptionMouseClickHandlerFunc).params[1].SetInt32(mouseButtonPressed + 1);
|
|
run_function_on_non_blocking_thread(&_GP(runDialogOptionMouseClickHandlerFunc));
|
|
|
|
if (_GP(runDialogOptionMouseClickHandlerFunc).atLeastOneImplementationExists) {
|
|
Redraw();
|
|
return true; // continue running loop
|
|
}
|
|
}
|
|
return true; // continue running loop
|
|
}
|
|
if (mouseison == DLG_OPTION_PARSER) {
|
|
// they clicked the text box
|
|
parserActivated = 1;
|
|
} else if (new_custom_render) {
|
|
_GP(runDialogOptionMouseClickHandlerFunc).params[0].SetDynamicObject(&ccDialogOptionsRendering, &ccDialogOptionsRendering);
|
|
_GP(runDialogOptionMouseClickHandlerFunc).params[1].SetInt32(mouseButtonPressed + 1);
|
|
run_function_on_non_blocking_thread(&_GP(runDialogOptionMouseClickHandlerFunc));
|
|
} else if (usingCustomRendering) {
|
|
chose = mouseison;
|
|
return false; // end dialog options running loop
|
|
} else {
|
|
chose = disporder[mouseison];
|
|
return false; // end dialog options running loop
|
|
}
|
|
}
|
|
|
|
if (usingCustomRendering) {
|
|
if (mouseWheelTurn != 0) {
|
|
_GP(runDialogOptionMouseClickHandlerFunc).params[0].SetDynamicObject(&ccDialogOptionsRendering, &ccDialogOptionsRendering);
|
|
_GP(runDialogOptionMouseClickHandlerFunc).params[1].SetInt32((mouseWheelTurn < 0) ? 9 : 8);
|
|
run_function_on_non_blocking_thread(&_GP(runDialogOptionMouseClickHandlerFunc));
|
|
|
|
if (!new_custom_render) {
|
|
if (_GP(runDialogOptionMouseClickHandlerFunc).atLeastOneImplementationExists)
|
|
Redraw();
|
|
return true; // continue running loop
|
|
}
|
|
}
|
|
}
|
|
|
|
if (parserActivated) {
|
|
// They have selected a custom parser-based option
|
|
if (!parserInput->Text.IsEmpty()) {
|
|
chose = DLG_OPTION_PARSER;
|
|
return false; // end dialog options running loop
|
|
} else {
|
|
parserActivated = 0;
|
|
parserInput->IsActivated = 0;
|
|
}
|
|
}
|
|
if (mousewason != mouseison) {
|
|
//ags_domouse(DOMOUSE_DISABLE);
|
|
Redraw();
|
|
return true; // continue running loop
|
|
}
|
|
if (new_custom_render) {
|
|
if (ccDialogOptionsRendering.chosenOptionID >= 0) {
|
|
chose = ccDialogOptionsRendering.chosenOptionID;
|
|
ccDialogOptionsRendering.chosenOptionID = -1;
|
|
return false; // end dialog options running loop
|
|
}
|
|
if (ccDialogOptionsRendering.needRepaint) {
|
|
Redraw();
|
|
return true; // continue running loop
|
|
}
|
|
}
|
|
|
|
update_polled_stuff_if_runtime();
|
|
|
|
if (_GP(play).fast_forward == 0) {
|
|
WaitForNextFrame();
|
|
}
|
|
|
|
return true; // continue running loop
|
|
}
|
|
|
|
void DialogOptions::Close() {
|
|
ags_clear_input_buffer();
|
|
invalidate_screen();
|
|
|
|
if (parserActivated) {
|
|
strcpy(_GP(play).lastParserEntry, parserInput->Text);
|
|
ParseText(parserInput->Text);
|
|
chose = CHOSE_TEXTPARSER;
|
|
}
|
|
|
|
if (parserInput) {
|
|
delete parserInput;
|
|
parserInput = nullptr;
|
|
}
|
|
|
|
if (ddb != nullptr)
|
|
gfxDriver->DestroyDDB(ddb);
|
|
delete subBitmap;
|
|
|
|
set_mouse_cursor(curswas);
|
|
// In case it's the QFG4 style dialog, remove the black screen
|
|
_GP(play).in_conversation--;
|
|
remove_screen_overlay(OVER_COMPLETE);
|
|
|
|
delete tempScrn;
|
|
}
|
|
|
|
DialogOptions DlgOpt;
|
|
|
|
int show_dialog_options(int _dlgnum, int sayChosenOption, bool _runGameLoopsInBackground) {
|
|
DlgOpt.Prepare(_dlgnum, _runGameLoopsInBackground);
|
|
DlgOpt.Show();
|
|
DlgOpt.Close();
|
|
|
|
int dialog_choice = DlgOpt.chose;
|
|
if (dialog_choice != CHOSE_TEXTPARSER) {
|
|
DialogTopic *dialog_topic = DlgOpt.dtop;
|
|
int32_t &option_flags = dialog_topic->optionflags[dialog_choice];
|
|
const char *option_name = DlgOpt.dtop->optionnames[dialog_choice];
|
|
|
|
option_flags |= DFLG_HASBEENCHOSEN;
|
|
bool sayTheOption = false;
|
|
if (sayChosenOption == SAYCHOSEN_YES) {
|
|
sayTheOption = true;
|
|
} else if (sayChosenOption == SAYCHOSEN_USEFLAG) {
|
|
sayTheOption = ((option_flags & DFLG_NOREPEAT) == 0);
|
|
}
|
|
|
|
if (sayTheOption)
|
|
DisplaySpeech(get_translation(option_name), _GP(game).playercharacter);
|
|
}
|
|
|
|
return dialog_choice;
|
|
}
|
|
|
|
void do_conversation(int dlgnum) {
|
|
EndSkippingUntilCharStops();
|
|
|
|
// AGS 2.x always makes the mouse cursor visible when displaying a dialog.
|
|
if (loaded_game_file_version <= kGameVersion_272)
|
|
_GP(play).mouse_cursor_hidden = 0;
|
|
|
|
int dlgnum_was = dlgnum;
|
|
int previousTopics[MAX_TOPIC_HISTORY];
|
|
int numPrevTopics = 0;
|
|
DialogTopic *dtop = &dialog[dlgnum];
|
|
|
|
// run the startup script
|
|
int tocar = run_dialog_script(dtop, dlgnum, dtop->startupentrypoint, 0);
|
|
if ((tocar == RUN_DIALOG_STOP_DIALOG) ||
|
|
(tocar == RUN_DIALOG_GOTO_PREVIOUS)) {
|
|
// 'stop' or 'goto-previous' from first startup script
|
|
remove_screen_overlay(OVER_COMPLETE);
|
|
_GP(play).in_conversation--;
|
|
return;
|
|
} else if (tocar >= 0)
|
|
dlgnum = tocar;
|
|
|
|
while (dlgnum >= 0) {
|
|
if (dlgnum >= _GP(game).numdialog)
|
|
quit("!RunDialog: invalid dialog number specified");
|
|
|
|
dtop = &dialog[dlgnum];
|
|
|
|
if (dlgnum != dlgnum_was) {
|
|
// dialog topic changed, so play the startup
|
|
// script for the new topic
|
|
tocar = run_dialog_script(dtop, dlgnum, dtop->startupentrypoint, 0);
|
|
dlgnum_was = dlgnum;
|
|
if (tocar == RUN_DIALOG_GOTO_PREVIOUS) {
|
|
if (numPrevTopics < 1) {
|
|
// goto-previous on first topic -- end dialog
|
|
tocar = RUN_DIALOG_STOP_DIALOG;
|
|
} else {
|
|
tocar = previousTopics[numPrevTopics - 1];
|
|
numPrevTopics--;
|
|
}
|
|
}
|
|
if (tocar == RUN_DIALOG_STOP_DIALOG)
|
|
break;
|
|
else if (tocar >= 0) {
|
|
// save the old topic number in the history
|
|
if (numPrevTopics < MAX_TOPIC_HISTORY) {
|
|
previousTopics[numPrevTopics] = dlgnum;
|
|
numPrevTopics++;
|
|
}
|
|
dlgnum = tocar;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
int chose = show_dialog_options(dlgnum, SAYCHOSEN_USEFLAG, (_GP(game).options[OPT_RUNGAMEDLGOPTS] != 0));
|
|
if (SHOULD_QUIT)
|
|
return;
|
|
|
|
if (chose == CHOSE_TEXTPARSER) {
|
|
said_speech_line = 0;
|
|
|
|
tocar = run_dialog_request(dlgnum);
|
|
|
|
if (said_speech_line > 0) {
|
|
// fix the problem with the close-up face remaining on screen
|
|
DisableInterface();
|
|
UpdateGameOnce(); // redraw the screen to make sure it looks right
|
|
EnableInterface();
|
|
set_mouse_cursor(CURS_ARROW);
|
|
}
|
|
} else {
|
|
tocar = run_dialog_script(dtop, dlgnum, dtop->entrypoints[chose], chose + 1);
|
|
}
|
|
|
|
if (tocar == RUN_DIALOG_GOTO_PREVIOUS) {
|
|
if (numPrevTopics < 1) {
|
|
tocar = RUN_DIALOG_STOP_DIALOG;
|
|
} else {
|
|
tocar = previousTopics[numPrevTopics - 1];
|
|
numPrevTopics--;
|
|
}
|
|
}
|
|
if (tocar == RUN_DIALOG_STOP_DIALOG) break;
|
|
else if (tocar >= 0) {
|
|
// save the old topic number in the history
|
|
if (numPrevTopics < MAX_TOPIC_HISTORY) {
|
|
previousTopics[numPrevTopics] = dlgnum;
|
|
numPrevTopics++;
|
|
}
|
|
dlgnum = tocar;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// end dialog manager
|
|
|
|
|
|
//=============================================================================
|
|
//
|
|
// Script API Functions
|
|
//
|
|
//=============================================================================
|
|
|
|
|
|
|
|
// int (ScriptDialog *sd)
|
|
RuntimeScriptValue Sc_Dialog_GetID(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(ScriptDialog, Dialog_GetID);
|
|
}
|
|
|
|
// int (ScriptDialog *sd)
|
|
RuntimeScriptValue Sc_Dialog_GetOptionCount(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(ScriptDialog, Dialog_GetOptionCount);
|
|
}
|
|
|
|
// int (ScriptDialog *sd)
|
|
RuntimeScriptValue Sc_Dialog_GetShowTextParser(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(ScriptDialog, Dialog_GetShowTextParser);
|
|
}
|
|
|
|
// int (ScriptDialog *sd, int sayChosenOption)
|
|
RuntimeScriptValue Sc_Dialog_DisplayOptions(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT_PINT(ScriptDialog, Dialog_DisplayOptions);
|
|
}
|
|
|
|
// int (ScriptDialog *sd, int option)
|
|
RuntimeScriptValue Sc_Dialog_GetOptionState(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT_PINT(ScriptDialog, Dialog_GetOptionState);
|
|
}
|
|
|
|
// const char* (ScriptDialog *sd, int option)
|
|
RuntimeScriptValue Sc_Dialog_GetOptionText(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_CONST_OBJCALL_OBJ_PINT(ScriptDialog, const char, _GP(myScriptStringImpl), Dialog_GetOptionText);
|
|
}
|
|
|
|
// int (ScriptDialog *sd, int option)
|
|
RuntimeScriptValue Sc_Dialog_HasOptionBeenChosen(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT_PINT(ScriptDialog, Dialog_HasOptionBeenChosen);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Dialog_SetHasOptionBeenChosen(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT_PBOOL(ScriptDialog, Dialog_SetHasOptionBeenChosen);
|
|
}
|
|
|
|
// void (ScriptDialog *sd, int option, int newState)
|
|
RuntimeScriptValue Sc_Dialog_SetOptionState(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT2(ScriptDialog, Dialog_SetOptionState);
|
|
}
|
|
|
|
// void (ScriptDialog *sd)
|
|
RuntimeScriptValue Sc_Dialog_Start(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID(ScriptDialog, Dialog_Start);
|
|
}
|
|
|
|
void RegisterDialogAPI() {
|
|
ccAddExternalObjectFunction("Dialog::get_ID", Sc_Dialog_GetID);
|
|
ccAddExternalObjectFunction("Dialog::get_OptionCount", Sc_Dialog_GetOptionCount);
|
|
ccAddExternalObjectFunction("Dialog::get_ShowTextParser", Sc_Dialog_GetShowTextParser);
|
|
ccAddExternalObjectFunction("Dialog::DisplayOptions^1", Sc_Dialog_DisplayOptions);
|
|
ccAddExternalObjectFunction("Dialog::GetOptionState^1", Sc_Dialog_GetOptionState);
|
|
ccAddExternalObjectFunction("Dialog::GetOptionText^1", Sc_Dialog_GetOptionText);
|
|
ccAddExternalObjectFunction("Dialog::HasOptionBeenChosen^1", Sc_Dialog_HasOptionBeenChosen);
|
|
ccAddExternalObjectFunction("Dialog::SetHasOptionBeenChosen^2", Sc_Dialog_SetHasOptionBeenChosen);
|
|
ccAddExternalObjectFunction("Dialog::SetOptionState^2", Sc_Dialog_SetOptionState);
|
|
ccAddExternalObjectFunction("Dialog::Start^0", Sc_Dialog_Start);
|
|
|
|
/* ----------------------- Registering unsafe exports for plugins -----------------------*/
|
|
|
|
ccAddExternalFunctionForPlugin("Dialog::get_ID", (void *)Dialog_GetID);
|
|
ccAddExternalFunctionForPlugin("Dialog::get_OptionCount", (void *)Dialog_GetOptionCount);
|
|
ccAddExternalFunctionForPlugin("Dialog::get_ShowTextParser", (void *)Dialog_GetShowTextParser);
|
|
ccAddExternalFunctionForPlugin("Dialog::DisplayOptions^1", (void *)Dialog_DisplayOptions);
|
|
ccAddExternalFunctionForPlugin("Dialog::GetOptionState^1", (void *)Dialog_GetOptionState);
|
|
ccAddExternalFunctionForPlugin("Dialog::GetOptionText^1", (void *)Dialog_GetOptionText);
|
|
ccAddExternalFunctionForPlugin("Dialog::HasOptionBeenChosen^1", (void *)Dialog_HasOptionBeenChosen);
|
|
ccAddExternalFunctionForPlugin("Dialog::SetOptionState^2", (void *)Dialog_SetOptionState);
|
|
ccAddExternalFunctionForPlugin("Dialog::Start^0", (void *)Dialog_Start);
|
|
}
|
|
|
|
} // namespace AGS3
|