Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

103
engines/m4/adv_r/adv.h Normal file
View File

@@ -0,0 +1,103 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_H
#define M4_ADV_R_ADV_H
#include "common/algorithm.h"
#include "m4/adv_r/adv_hotspot.h"
#include "m4/m4_types.h"
namespace M4 {
#define MAX_SCENES 180
#define MAXRAILNODES 32
#define PATH_END 0xffff
#define MAX_PLYR_STRING_LEN 40
#define STR_FADEPAL "fade palette"
#define STR_RAILNODE "rail node"
#define STR_PATH_NODE "path node"
enum {
INSTALL_SOUND_DRIVERS = 1,
INSTALL_PLAYER_BEEN_INIT = 2,
INSTALL_RAIL_SYSTEM = 4,
INSTALL_INVENTORY_SYSTEM = 8,
INSTALL_INVERSE_PALETTE = 0x10,
INSTALL_MININUM = 0,
INSTALL_ALL = 0x1f
};
enum {
UNKNOWN_OBJECT = 997,
BACKPACK = 998,
NOWHERE = 999
};
#define GLOBAL_SCENE 999
enum KernelTriggerType {
KT_PARSE = 1,
KT_DAEMON, KT_PREPARSE, KT_EXPIRE, KT_LOOP
};
struct pathNode {
pathNode *next = nullptr;
byte nodeID = 0;
};
struct noWalkRect {
noWalkRect *next = nullptr;
noWalkRect *prev = nullptr;
int32 x1 = 0, y1 = 0, x2 = 0, y2 = 0;
int32 alternateWalkToNode = 0;
int32 walkAroundNode1 = 0;
int32 walkAroundNode2 = 0;
int32 walkAroundNode3 = 0;
int32 walkAroundNode4 = 0;
};
struct SceneDef {
char art_base[MAX_FILENAME_SIZE] = { 0 };
char picture_base[MAX_FILENAME_SIZE] = { 0 };
int32 num_hotspots = 0; // # of hotspots
HotSpotRec *hotspots = nullptr;
int32 num_parallax = 0;
HotSpotRec *parallax = nullptr;
int32 num_props = 0;
HotSpotRec *props = nullptr;
int32 front_y = 400, back_y = 100; // Player scaling baselines
int32 front_scale = 100, back_scale = 85; // Player scaling factors
int16 depth_table[16]; // Player sprite depth table
int32 numRailNodes = 0; // # of rails
SceneDef() {
Common::fill(depth_table, depth_table + 16, 0);
}
};
} // namespace M4
#endif

View File

@@ -0,0 +1,68 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/adv_background.h"
#include "m4/adv_r/adv_file.h"
#include "m4/vars.h"
namespace M4 {
void adv_freeCodes() {
if (_G(screenCodeBuff)) {
delete _G(screenCodeBuff);
_G(screenCodeBuff) = nullptr;
}
}
void adv_freeBackground() {
if (_G(game_bgBuff)) {
delete _G(game_bgBuff);
_G(game_bgBuff) = nullptr;
}
}
bool adv_restoreBackground() {
RGB8 myPalette[256];
SysFile sysFile(_G(currBackgroundFN));
if (load_background(&sysFile, &_G(game_bgBuff), myPalette)) {
sysFile.close();
return true;
} else {
return false;
}
}
bool adv_restoreCodes() {
SysFile sysFile(_G(currCodeFN));
_G(screenCodeBuff) = load_codes(&sysFile);
if (_G(screenCodeBuff)) {
sysFile.close();
return true;
} else {
return false;
}
return true;
}
} // End of namespace M4

View File

@@ -0,0 +1,37 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_BACKGROUND_H
#define M4_ADV_R_ADV_BACKGROUND_H
#include "m4/m4_types.h"
namespace M4 {
void adv_freeCodes();
void adv_freeBackground();
bool adv_restoreBackground();
bool adv_restoreCodes();
} // End of namespace M4
#endif

View File

@@ -0,0 +1,106 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/adv_been.h"
#include "m4/core/errors.h"
#include "m4/mem/memman.h"
#include "m4/vars.h"
namespace M4 {
#define TOTAL_SCENES 180
bool player_been_init(int16 num_scenes) {
assert(num_scenes == TOTAL_SCENES);
_G(scene_list).table = (int16 *)mem_alloc(sizeof(int16) * num_scenes, "been_scenes");
if (!_G(scene_list).table)
error_show(FL, 'OOM!', "player_been_init");
_G(scene_list).total_scenes = num_scenes;
player_reset_been();
return true;
}
void player_been_shutdown() {
mem_free(_G(scene_list).table);
}
void player_reset_been() {
Common::fill(_G(scene_list).table, _G(scene_list).table + TOTAL_SCENES, 180);
_G(scene_list).total_scenes = TOTAL_SCENES;
_G(scene_list).tail = 0;
}
void player_been_sync(Common::Serializer &s) {
// Handle number of scenes
int count = _G(scene_list).total_scenes;
s.syncAsUint32LE(count);
assert(s.isSaving() || count == _G(scene_list).total_scenes);
// Handle current tail
s.syncAsUint32LE(_G(scene_list).tail);
// Handle table
for (int i = 0; i < count; ++i)
s.syncAsSint16LE(_G(scene_list).table[i]);
}
bool player_been_here(int16 scene_num) {
for (int i = 0; i < _G(scene_list).tail; i++)
if (_G(scene_list).table[i] == scene_num)
return true;
return false;
}
bool player_enters_scene(int16 scene_num) {
if (player_been_here(scene_num))
return true;
_G(scene_list).table[_G(scene_list).tail] = scene_num;
++_G(scene_list).tail;
if (_G(scene_list).tail > _G(scene_list).total_scenes)
error_show(FL, 'SLTS');
return false;
}
void player_forgets_scene(int16 scene_num) {
// Check the list
for (int i = 0; i < _G(scene_list).tail; i++) {
// Found a match
if (_G(scene_list).table[i] == scene_num) {
// Close the table
for (int j = i; j < _G(scene_list).tail; j++) {
_G(scene_list).table[j] = _G(scene_list).table[j + 1];
}
// One less scene
--_G(scene_list).tail;
return;
}
}
}
} // End of namespace M4

View File

@@ -0,0 +1,81 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_BEEN_H
#define M4_ADV_R_ADV_BEEN_H
#include "common/serializer.h"
#include "common/stream.h"
#include "m4/m4_types.h"
namespace M4 {
struct Scene_list {
int32 total_scenes = 0;
int32 tail = 0;
int16 *table = nullptr;
};
/**
* Initializes player_been information, allocates memory for a list of scenes
*
* Takes the number of scenes to allocate space for
* @returns True if successfully allocated
*/
bool player_been_init(int16 num_scenes);
/**
* Shuts down player_been system. Deallocates some memory
*/
void player_been_shutdown();
/**
* Resets the player_been system
*/
void player_reset_been(void);
/**
* Saves/loads player_been information
*/
void player_been_sync(Common::Serializer &s);
/**
* Called whenever player enters a scene
* @param scene_num Takes the scene to be entered
* @returns True if the player has been there before, or false otherwise
*/
bool player_enters_scene(int16 scene_num);
/**
* Called if the apps programmer wants the player to forget about being in
* a room
*/
void player_forgets_scene(int16 scene_num);
/**
* Checks whether player has been in specified scene before
*/
bool player_been_here(int16 scene_num);
} // End of namespace M4
#endif

View File

@@ -0,0 +1,146 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/adv_chk.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/fileio/sys_file.h"
#include "m4/vars.h"
namespace M4 {
static HotSpotRec *read_hotspots(SysFile *fpdef, HotSpotRec *h, int32 num) {
int32 str_len;
char s[MAX_FILENAME_SIZE];
int32 x1, x2, y1, y2;
HotSpotRec *head = nullptr;
for (int32 i = 0; i < num; i++) {
x1 = fpdef->readSint32LE();
y1 = fpdef->readSint32LE();
x2 = fpdef->readSint32LE();
y2 = fpdef->readSint32LE();
h = hotspot_new(x1, y1, x2, y2);
if (!head)
head = h;
else
head = hotspot_add(head, h, false);
h->feet_x = fpdef->readSint32LE();
h->feet_y = fpdef->readSint32LE();
h->facing = fpdef->readSByte();
h->active = fpdef->readSByte();
h->cursor_number = fpdef->readSByte();
h->syntax = fpdef->readSByte();
h->vocabID = fpdef->readSint32LE();
h->verbID = fpdef->readSint32LE();
// -------
str_len = fpdef->readSint32LE();
if (str_len) {
if (!fpdef->read((byte *)s, str_len))
error_show(FL, 0, "Could not read vocab");
hotspot_newVocab(h, s);
}
str_len = fpdef->readSint32LE();
if (str_len) {
if (!fpdef->read((byte *)s, str_len))
error_show(FL, 0, "Could not read verb");
hotspot_newVerb(h, s);
}
str_len = fpdef->readSint32LE();
if (str_len) {
if (!fpdef->read((byte *)s, str_len))
error_show(FL, 0, "Could not read prep");
hotspot_newPrep(h, s);
}
str_len = fpdef->readSint32LE();
if (str_len) {
if (!fpdef->read((byte *)s, str_len))
error_show(FL, 0, "Could not read sprite");
hotspot_new_sprite(h, s);
}
h->hash = fpdef->readSint16LE();
}
return head;
}
static void load_def(SysFile *fpdef) {
int32 x, y;
char s[MAX_FILENAME_SIZE];
fpdef->read((byte *)s, MAX_FILENAME_SIZE);
Common::strlcpy(_G(myDef)->art_base, s, MAX_FILENAME_SIZE);
fpdef->read((byte *)s, MAX_FILENAME_SIZE);
Common::strlcpy(_G(myDef)->picture_base, s, MAX_FILENAME_SIZE);
_G(myDef)->num_hotspots = fpdef->readSint32LE();
_G(myDef)->num_parallax = fpdef->readSint32LE();
_G(myDef)->num_props = fpdef->readSint32LE();
_G(myDef)->front_y = fpdef->readSint32LE();
_G(myDef)->back_y = fpdef->readSint32LE();
_G(myDef)->front_scale = fpdef->readSint32LE();
_G(myDef)->back_scale = fpdef->readSint32LE();
for (int32 i = 0; i < 16; i++) {
_G(myDef)->depth_table[i] = fpdef->readSint16LE();
}
_G(myDef)->numRailNodes = fpdef->readSint32LE();
for (int32 i = 0; i < _G(myDef)->numRailNodes; i++) {
x = fpdef->readSint32LE();
y = fpdef->readSint32LE();
if (AddRailNode(x, y, nullptr, true) < 0)
error_show(FL, 0, "more than %d (defn. in intrrail.h) nodes", MAXRAILNODES);
}
_G(myDef)->hotspots = read_hotspots(fpdef, nullptr, _G(myDef)->num_hotspots);
_G(myDef)->parallax = read_hotspots(fpdef, nullptr, _G(myDef)->num_parallax);
_G(myDef)->props = read_hotspots(fpdef, nullptr, _G(myDef)->num_props);
}
int db_def_chk_read(int16 room_code, SceneDef *rdef) {
_G(myDef) = rdef;
_G(def_filename) = Common::String::format("%03d.chk", room_code);
SysFile fpdef(_G(def_filename));
load_def(&fpdef);
fpdef.close();
return -1; // everything happy code
}
} // End of namespace M4

View File

@@ -0,0 +1,40 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_CHK_H
#define M4_ADV_R_ADV_CHK_H
#include "m4/adv_r/adv.h"
namespace M4 {
/**
* Reads the .DEF file for the specified room into the "room" structure.
* @param room_code room number
* @param rdef Output def to populate
* @returns Returns 0 if successful, or -1 for error
*/
int db_def_chk_read(int16 room_code, SceneDef *rdef);
} // End of namespace M4
#endif

View File

@@ -0,0 +1,212 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/adv_control.h"
#include "m4/adv_r/adv_interface.h"
#include "m4/core/errors.h"
#include "m4/core/mouse.h"
#include "m4/gui/hotkeys.h"
#include "m4/gui/gui_vmng.h"
#include "m4/mem/memman.h"
#include "m4/wscript/ws_machine.h"
#include "m4/vars.h"
#include "m4/m4.h"
namespace M4 {
bool kernel_section_startup() {
_G(game).previous_section = _G(game).section_id;
_G(game).section_id = _G(game).new_section;
return true;
}
void player_set_commands_allowed(bool t_or_f) {
_G(set_commands_allowed_since_last_checked) = true;
_G(player).comm_allowed = t_or_f;
if (t_or_f) {
// OK to do something
mouse_set_sprite(kArrowCursor);
intr_cancel_sentence();
track_hotspots_refresh();
} else {
// Hour glass
_GI().showWaitCursor();
}
}
void game_pause(bool flag) {
if (flag) {
_G(kernel).pause = true;
pauseEngines();
} else {
_G(kernel).pause = false;
unpauseEngines();
}
}
void player_hotspot_walk_override(int32 x, int32 y, int32 facing, int32 trigger) {
_G(player).walk_x = x;
_G(player).walk_y = y;
_G(player).walk_facing = facing;
_G(player).walker_trigger = trigger;
_G(player).ready_to_walk = true;
_G(player).need_to_walk = true;
}
void player_hotspot_walk_override_just_face(int32 facing, int32 trigger) {
player_update_info(_G(my_walker), &_G(player_info));
player_hotspot_walk_override(_G(player_info).x, _G(player_info).y, facing, trigger);
}
void adv_kill_digi_between_rooms(bool true_or_false) {
_G(shut_down_digi_tracks_between_rooms) = true_or_false;
}
bool this_is_a_walkcode(int32 x, int32 y) {
if (!_G(screenCodeBuff))
return false;
Buffer *walkCodes;
byte *ptr;
bool result;
walkCodes = _G(screenCodeBuff)->get_buffer();
if (!walkCodes)
return false;
// Verify params
if (x < 0 || y < 0 || x >= walkCodes->w || y >= walkCodes->h)
return false;
ptr = gr_buffer_pointer(walkCodes, x, y);
result = ((*ptr) & 0x10) ? true : false;
_G(screenCodeBuff)->release();
return result;
}
int32 get_screen_depth(int32 x, int32 y) {
Buffer *walkCodes;
byte *ptr;
int32 myDepth;
if (!_G(screenCodeBuff))
return 0;
walkCodes = _G(screenCodeBuff)->get_buffer();
if (!walkCodes) {
return 0;
}
// Verify params
if (x < 0 || y < 0 || x >= walkCodes->w || y >= walkCodes->h) {
return -1;
}
ptr = gr_buffer_pointer(walkCodes, x, y);
myDepth = (*ptr) & 0x0f;
_G(screenCodeBuff)->release();
return myDepth;
}
int32 get_screen_color(int32 x, int32 y) {
Buffer *game_buff;
byte *ptr;
int32 myColor;
game_buff = _G(gameDrawBuff)->get_buffer();
if (!game_buff) {
return -1;
}
//verify params
if (x < 0 || y < 0 || x >= game_buff->w || y >= game_buff->h) {
return -1;
}
ptr = gr_buffer_pointer(game_buff, x, y);
myColor = *ptr;
_G(gameDrawBuff)->release();
return myColor;
}
void update_mouse_pos_dialog() {
int32 status;
ScreenContext *game_buff_ptr = vmng_screen_find(_G(gameDrawBuff), &status);
assert(game_buff_ptr);
if (_G(my_walker) != nullptr) {
if (!_G(my_walker)->myAnim8)
error_show(FL, 'W:-(');
player_get_info();
}
char tempStr1[MAX_STRING_LEN], tempStr2[MAX_STRING_LEN];
Common::sprintf_s(tempStr1, "%d From: %d", _G(game).room_id, _G(game).previous_room);
Dialog_Change_Item_Prompt(_G(mousePosDialog), tempStr1, nullptr, 1);
int32 xxx = _G(MouseState).CursorColumn;
int32 yyy = _G(MouseState).CursorRow;
int32 scrnDepth = get_screen_depth(xxx - game_buff_ptr->x1, yyy - game_buff_ptr->y1);
int32 palColor = get_screen_color(xxx - game_buff_ptr->x1, yyy - game_buff_ptr->y1);
if (this_is_a_walkcode(xxx - game_buff_ptr->x1, yyy - game_buff_ptr->y1)) {
Common::sprintf_s(tempStr1, "WC %d, %d PAL: %d", xxx, yyy, palColor);
Common::sprintf_s(tempStr2, "WC %d, %d D: %d", xxx - game_buff_ptr->x1, yyy - game_buff_ptr->y1, scrnDepth);
} else {
Common::sprintf_s(tempStr1, " %d, %d PAL: %d", xxx, yyy, palColor);
Common::sprintf_s(tempStr2, " %d, %d D: %d", xxx - game_buff_ptr->x1, yyy - game_buff_ptr->y1, scrnDepth);
}
Dialog_Change_Item_Prompt(_G(mousePosDialog), tempStr1, nullptr, 2);
Dialog_Change_Item_Prompt(_G(mousePosDialog), tempStr2, nullptr, 3);
if (this_is_a_walkcode(_G(player_info).x, _G(player_info).y)) {
Common::sprintf_s(tempStr1, "WC %d, %d", _G(player_info).x + game_buff_ptr->x1, _G(player_info).y + game_buff_ptr->y1);
Common::sprintf_s(tempStr2, "WC %d, %d", _G(player_info).x, _G(player_info).y);
} else {
Common::sprintf_s(tempStr1, " %d, %d", _G(player_info).x + game_buff_ptr->x1, _G(player_info).y + game_buff_ptr->y1);
Common::sprintf_s(tempStr2, " %d, %d", _G(player_info).x, _G(player_info).y);
}
Dialog_Change_Item_Prompt(_G(mousePosDialog), tempStr1, nullptr, 4);
Dialog_Change_Item_Prompt(_G(mousePosDialog), tempStr2, nullptr, 5);
Common::sprintf_s(tempStr1, "%d", _G(player_info).scale);
Dialog_Change_Item_Prompt(_G(mousePosDialog), tempStr1, nullptr, 6);
Common::sprintf_s(tempStr1, "%x", _G(player_info).depth);
Dialog_Change_Item_Prompt(_G(mousePosDialog), tempStr1, nullptr, 7);
Common::sprintf_s(tempStr1, "%d, %d", game_buff_ptr->x1, game_buff_ptr->y1);
Dialog_Change_Item_Prompt(_G(mousePosDialog), tempStr1, nullptr, 8);
Common::sprintf_s(tempStr1, "%d", _G(player_info).facing);
Dialog_Change_Item_Prompt(_G(mousePosDialog), tempStr1, nullptr, 10);
}
} // End of namespace M4

View File

@@ -0,0 +1,41 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_CONTROL_H
#define M4_ADV_R_ADV_CONTROL_H
#include "common/stream.h"
#include "m4/m4_types.h"
namespace M4 {
extern bool kernel_section_startup();
extern void player_set_commands_allowed(bool t_or_f);
extern void game_pause(bool flag);
extern bool this_is_a_walkcode(int32 x, int32 y);
extern int32 get_screen_depth(int32 x, int32 y);
extern int32 get_screen_color(int32 x, int32 y);
extern void update_mouse_pos_dialog();
} // End of namespace M4
#endif

View File

@@ -0,0 +1,406 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "common/savefile.h"
#include "common/util.h"
#include "m4/adv_r/adv_file.h"
#include "m4/adv_r/adv_chk.h"
#include "m4/adv_r/adv_walk.h"
#include "m4/adv_r/db_env.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/fileio/extensions.h"
#include "m4/fileio/info.h"
#include "m4/graphics/gr_pal.h"
#include "m4/gui/gui_buffer.h"
#include "m4/gui/gui_vmng.h"
#include "m4/platform/tile/tile_read.h"
#include "m4/m4.h"
namespace M4 {
static Common::String get_background_filename(const SceneDef *rdef);
static Common::String get_attribute_filename(const SceneDef *rdef);
static void recreate_animation_draw_screen(GrBuff **loadBuf);
static void troll_for_colors(RGB8 *newPal, uint8 minPalEntry, uint8 maxPalEntry);
void kernel_unload_room(SceneDef *rdef, GrBuff **code_data, GrBuff **loadBuffer) {
term_message("Unloading scene %d", _G(game).room_id);
if (_G(gameDrawBuff)) {
gui_buffer_deregister((Buffer *)_G(gameDrawBuff));
delete _G(gameDrawBuff);
_G(gameDrawBuff) = nullptr;
}
if (*code_data)
delete *code_data;
*code_data = nullptr;
if (*loadBuffer)
delete *loadBuffer;
*loadBuffer = nullptr;
if (!rdef)
return;
// Must we deallocate existing hot spots?
if (rdef->hotspots != nullptr) {
hotspot_delete_all(rdef->hotspots);
rdef->hotspots = nullptr;
}
rdef->num_hotspots = 0;
// Must we deallocate existing parallax?
if (rdef->parallax != nullptr) {
hotspot_delete_all(rdef->parallax);
rdef->parallax = nullptr;
}
rdef->num_parallax = 0;
// Must we deallocate existing props?
if (rdef->props != nullptr) {
hotspot_delete_all(rdef->props);
rdef->props = nullptr;
}
rdef->num_props = 0;
ClearRails();
}
bool kernel_load_room(int minPalEntry, int maxPalEntry, SceneDef *rdef, GrBuff **scr_orig_data, GrBuff **scr_orig) {
if (!scr_orig_data || !scr_orig) {
error_show(FL, 'BUF!', "load_picture_and_codes");
}
term_message("Reading scene %d", _G(game).new_room);
if (_G(game).previous_room != KERNEL_RESTORING_GAME) {
_G(game).previous_room = _G(game).room_id;
}
// Read DEF file
if (db_def_chk_read(_G(game).new_room, rdef) != -1) {
error_show(FL, 'DF:(', "trying to find %d.CHK", (uint32)_G(game).new_room);
}
set_walker_scaling(rdef);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Select background picture
_G(currBackgroundFN) = get_background_filename(rdef);
char *tempName = env_find(_G(currBackgroundFN));
if (tempName) {
// In normal rooms.db mode
_G(currBackgroundFN) = f_extension_new(tempName, "TT");
} else {
// In concat hag mode
_G(currBackgroundFN) = f_extension_new(_G(currBackgroundFN), "TT");
}
SysFile *pic_file = new SysFile(_G(currBackgroundFN));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Select attributes code file
_G(currCodeFN) = get_attribute_filename(rdef);
tempName = env_find(_G(currCodeFN));
if (tempName) {
// In normal rooms.db mode
_G(currCodeFN) = f_extension_new(tempName, "COD");
} else {
// In concat hag mode
_G(currCodeFN) = f_extension_new(_G(currCodeFN), "COD");
}
SysFile *code_file = new SysFile(_G(currCodeFN));
if (!code_file->exists()) {
delete code_file;
code_file = nullptr;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Load background picture
term_message("load background");
RGB8 newPal[256];
load_background(pic_file, scr_orig, newPal);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Load attributes code file
term_message("load codes");
*scr_orig_data = load_codes(code_file);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Prepare buffers for display
recreate_animation_draw_screen(scr_orig);
troll_for_colors(newPal, minPalEntry, maxPalEntry);
gr_pal_reset_ega_colors(&_G(master_palette)[0]);
RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y);
pic_file->close();
delete pic_file;
if (code_file) {
code_file->close();
delete code_file;
}
if (*scr_orig_data) {
Buffer *scr_orig_data_buffer = (**scr_orig_data).get_buffer();
RestoreEdgeList(scr_orig_data_buffer);
(**scr_orig_data).release();
} else
RestoreEdgeList(nullptr);
_G(game).room_id = _G(game).new_room;
return true;
}
bool kernel_load_variant(const char *variant) {
SceneDef &sceneDef = _G(currentSceneDef);
GrBuff *codeBuff = _G(screenCodeBuff);
Common::String filename;
if (!codeBuff)
return false;
if (_G(kernel).hag_mode) {
filename = f_extension_new(variant, "COD");
} else {
char *base = env_find(sceneDef.art_base);
char *dotPos = nullptr;
if (base)
dotPos = strchr(base, '.');
if (!dotPos)
return false;
filename = f_extension_new(base, "COD");
if (!f_info_exists(Common::Path(filename)))
return false;
}
SysFile code_file(filename);
if (!code_file.exists())
error("Failed to load variant %s", filename.c_str());
// TODO: This is just copied from the room loading code,
// rather than disassembling the reset of the original method.
// Need to determine whether this is correct or not,
// then modified to clean screenCodeBuff and the edges.
GrBuff *scr_orig_data = load_codes(&code_file);
code_file.close();
if (scr_orig_data) {
_G(screenCodeBuff)->release();
free _G(screenCodeBuff);
RestoreEdgeList(nullptr);
Buffer *scr_orig_data_buffer = scr_orig_data->get_buffer();
RestoreEdgeList(scr_orig_data_buffer);
_G(screenCodeBuff) = scr_orig_data;
}
return true;
}
GrBuff *load_codes(SysFile *code_file) {
// No this is not a cheat to allow bugs to live,
// if there is no code file, then we don't need a code buffer, either.
// it's perfectly acceptable, so there, NYAH!
if (!code_file)
return nullptr;
const int16 x_size = code_file->readSint16LE();
const int16 y_size = code_file->readSint16LE();
GrBuff *temp = new GrBuff(x_size, y_size);
Buffer *myBuff = temp->get_buffer();
byte *bufferHandle = myBuff->data;
for (int i = 0; i < y_size; i++) {
code_file->read(bufferHandle, x_size);
bufferHandle += myBuff->stride;
}
// Let the memory float
temp->release();
return temp;
}
bool load_background(SysFile *pic_file, GrBuff **loadBuffer, RGB8 *palette) {
int32 num_x_tiles, num_y_tiles, tile_x, tile_y, file_x, file_y;
int32 count = 0;
tt_read_header(pic_file, &file_x, &file_y,
&num_x_tiles, &num_y_tiles, &tile_x, &tile_y, palette);
*loadBuffer = new GrBuff(file_x, file_y);
if (!*loadBuffer)
error_show(FL, 'OOM!');
Buffer *theBuff = (**loadBuffer).get_buffer();
for (int i = 0; i < num_y_tiles; i++) {
for (int j = 0; j < num_x_tiles; j++) {
Buffer *out = tt_read(pic_file, count, tile_x, tile_y);
count++;
if (out && (out->data)) {
const int32 x_end = imath_min(file_x, (1 + j) * tile_x);
const int32 y_end = imath_min(file_y, (1 + i) * tile_y);
gr_buffer_rect_copy_2(out, theBuff, 0, 0, j * tile_x, i * tile_y,
x_end - (j * tile_x), y_end - (i * tile_y));
mem_free(out->data);
}
if (out)
mem_free(out);
}
}
(**loadBuffer).release();
return true;
}
static Common::SeekableReadStream *openForLoading(int slot) {
const Common::String slotName = g_engine->getSaveStateName(slot);
return g_system->getSavefileManager()->openForLoading(slotName);
}
bool kernel_save_game_exists(int32 slot) {
Common::SeekableReadStream *save = openForLoading(slot);
const bool result = save != nullptr;
delete save;
return result;
}
int kernel_save_game(int slot, const char *desc, int32 sizeofDesc, M4sprite *thumbNail, int32 sizeofThumbData) {
return g_engine->saveGameState(slot, desc, slot == 0).getCode() == Common::kNoError ? 0 : 1;
}
bool kernel_load_game(int slot) {
return g_engine->loadGameStateDoIt(slot).getCode() == Common::kNoError;
}
int32 extract_room_num(const Common::String &name) {
if ((name[0] == 'C' || name[0] == 'c') &&
(name[1] == 'O' || name[1] == 'o') &&
(name[2] == 'M' || name[2] == 'm'))
return _G(global_sound_room);
if (Common::isDigit(name[0]) && Common::isDigit(name[1]) && Common::isDigit(name[2])) {
return ((int32)(name[0] - '0')) * 100 + ((int32)(name[1] - '0')) * 10 + ((int32)(name[2] - '0'));
}
return _G(game).room_id;
}
static Common::String get_background_filename(const SceneDef *rdef) {
if (_G(art_base_override) != nullptr) {
return _G(art_base_override);
}
return rdef->art_base;
}
static Common::String get_attribute_filename(const SceneDef *rdef) {
if (_G(art_base_override) == nullptr || !_G(use_alternate_attribute_file)) {
return rdef->art_base;
}
return _G(art_base_override);
}
static void recreate_animation_draw_screen(GrBuff **loadBuf) {
// Remove previous animation draw screen
if (_G(gameDrawBuff)) {
gui_buffer_deregister((Buffer *)_G(gameDrawBuff));
delete _G(gameDrawBuff);
_G(gameDrawBuff) = nullptr;
_G(game_buff_ptr) = nullptr;
}
_G(gameDrawBuff) = new GrBuff((**loadBuf).w, (**loadBuf).h);
gui_GrBuff_register(_G(kernel).letter_box_x, _G(kernel).letter_box_y, _G(gameDrawBuff), SF_BACKGRND | SF_GET_ALL | SF_BLOCK_NONE, nullptr);
gui_buffer_activate((Buffer *)_G(gameDrawBuff));
vmng_screen_to_back((void *)_G(gameDrawBuff));
_G(game_buff_ptr) = vmng_screen_find(_G(gameDrawBuff), nullptr);
Buffer *theBuff = (**loadBuf).get_buffer();
Buffer *game_buff = (*_G(gameDrawBuff)).get_buffer();
gr_buffer_rect_copy_2(theBuff, game_buff, 0, 0, 0, 0,
imath_min((**loadBuf).w, game_buff->w), imath_min((**loadBuf).h, game_buff->h));
(**loadBuf).release();
(*_G(gameDrawBuff)).release();
}
static void troll_for_colors(RGB8 *newPal, uint8 minPalEntry, uint8 maxPalEntry) {
bool gotOne = false;
for (int16 pal_iter = minPalEntry; pal_iter <= maxPalEntry; pal_iter++) { // accept any colors that came with the background
if (gotOne || (newPal[pal_iter].r | newPal[pal_iter].g | newPal[pal_iter].b)) {
gotOne = true;
// colors are 6 bit...
_G(master_palette)[pal_iter].r = newPal[pal_iter].r << 2;
_G(master_palette)[pal_iter].g = newPal[pal_iter].g << 2;
_G(master_palette)[pal_iter].b = newPal[pal_iter].b << 2;
}
}
if (gotOne) {
gr_pal_interface(&_G(master_palette)[0]); // enforce interface colors
}
}
Common::String expand_name_2_RAW(const Common::String &name, int32 room_num) {
Common::String tempName = f_extension_new(name, "RAW");
if (!_G(kernel).hag_mode) {
if (room_num == -1)
room_num = extract_room_num(name);
return Common::String::format("%d\\%s", room_num, tempName.c_str());
}
return tempName;
}
Common::String expand_name_2_HMP(const Common::String &name, int32 room_num) {
return f_extension_new(name, "HMP");
}
} // End of namespace M4

View File

@@ -0,0 +1,62 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_FILE_H
#define M4_ADV_R_ADV_FILE_H
#include "common/stream.h"
#include "m4/adv_r/adv.h"
#include "m4/fileio/sys_file.h"
#include "m4/graphics/gr_buff.h"
#include "m4/gui/gui.h"
#include "m4/m4_types.h"
namespace M4 {
extern M4sprite *kernel_CreateThumbnail(int32 *spriteSize);
extern bool kernel_CreateSSFromGrBuff(GrBuff *myBuff, RGB8 *myPalette, bool completePal, const char *ssName);
extern bool kernel_load_room(int minPalEntry, int maxPalEntry, SceneDef *rdef, GrBuff **scr_orig_data, GrBuff **scr_orig);
extern void kernel_unload_room(SceneDef *rdef, GrBuff **code_data, GrBuff **loadBuffer);
extern int kernel_save_game(int slot, const char *desc, int32 sizeofDesc, M4sprite *thumbNail, int32 sizeofThumbData);
extern bool kernel_load_game(int slot);
extern bool kernel_save_game_exists(int32 slot);
extern int32 extract_room_num(const Common::String &name);
extern bool kernel_load_variant(const char *variant);
extern GrBuff *load_codes(SysFile *code_file);
extern bool load_background(SysFile *pic_file, GrBuff **loadBuffer, RGB8 *palette);
extern bool load_picture_and_codes(SysFile *pic_file, SysFile *code_file, GrBuff **loadBuf, GrBuff **code_data, uint8 minPalEntry, uint8 maxPalEntry);
extern bool kernel_load_code_variant(SceneDef *rdef, char *variant, GrBuff **codeData);
extern void kernel_current_background_name(char *result);
extern void kernel_current_codes_name(char *result);
extern void screen_capture(RGB8 *masterPalette);
extern Common::String expand_name_2_RAW(const Common::String &name, int32 room_num);
extern Common::String expand_name_2_HMP(const Common::String &name, int32 room_num);
} // End of namespace M4
#endif

View File

@@ -0,0 +1,53 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_GAME_H
#define M4_ADV_R_ADV_GAME_H
#include "common/serializer.h"
#include "m4/adv_r/adv.h"
#include "m4/m4_types.h"
namespace M4 {
struct GameControl {
int16 room_id = 0;
int16 new_room = 0;
int16 previous_section = 0;
int16 section_id = 0;
int16 new_section = 0;
int16 previous_room = 0;
bool camera_pan_instant = false;
/**
* Shortcut for setting new room and section
*/
void setRoom(int newRoom) {
new_room = newRoom;
new_section = newRoom / 100;
}
};
} // End of namespace M4
#endif

View File

@@ -0,0 +1,345 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/adv_hotspot.h"
#include "m4/core/cstring.h"
#include "m4/core/errors.h"
#include "m4/core/term.h"
#include "m4/gui/gui_vmng.h"
#include "m4/mem/mem.h"
#include "m4/mem/memman.h"
#include "m4/vars.h"
namespace M4 {
#define STR_HOT_SPOT "hot spot"
#define _MAXPOSINT 0x7fffffff
void HotSpotRec::clear() {
ul_x = ul_y = lr_x = lr_y = 0;
feet_x = feet_y = 0;
facing = 0;
active = false;
cursor_number = kArrowCursor;
syntax = 0;
vocabID = verbID = 0;
vocab = verb = prep = nullptr;
sprite = nullptr;
hash = 0;
next = nullptr;
}
void hotspot_new_sprite(HotSpotRec *h, const char *sprite) {
if (!h)
return;
if (h->sprite)
mem_free(h->sprite);
h->sprite = mem_strdup(sprite);
}
void hotspot_newVerb(HotSpotRec *h, const char *verb) {
if (!h)
return;
if (h->verb)
mem_free(h->verb);
h->verb = mem_strdup(verb);
}
void hotspot_newVocab(HotSpotRec *h, const char *vocab) {
if (!h)
return;
if (h->vocab)
mem_free(h->vocab);
h->vocab = mem_strdup(vocab);
}
void hotspot_newPrep(HotSpotRec *h, const char *prep) {
if (!h)
return;
if (h->prep)
mem_free(h->prep);
h->prep = mem_strdup(prep);
}
HotSpotRec *hotspot_new(int x1, int y1, int x2, int y2) {
HotSpotRec *newSpot = (HotSpotRec *)mem_alloc(sizeof(HotSpotRec), STR_HOT_SPOT);
if (!newSpot)
return newSpot;
newSpot->ul_x = x1;
newSpot->ul_y = y1;
newSpot->lr_x = x2;
newSpot->lr_y = y2;
newSpot->sprite = nullptr;
newSpot->vocab = nullptr;
newSpot->verb = nullptr;
newSpot->prep = nullptr;
newSpot->syntax = 0; // Unused field
newSpot->cursor_number = kArrowCursor;
newSpot->facing = 5;
newSpot->feet_x = 32767;
newSpot->feet_y = 32767;
newSpot->next = nullptr;
newSpot->active = true;
return newSpot;
}
HotSpotRec *hotspot_duplicate(HotSpotRec *dupMe) {
HotSpotRec *newSpot = (HotSpotRec *)mem_alloc(sizeof(HotSpotRec), STR_HOT_SPOT);
if (!newSpot)
return newSpot;
newSpot->ul_x = dupMe->ul_x;
newSpot->ul_y = dupMe->ul_y;
newSpot->lr_x = dupMe->lr_x;
newSpot->lr_y = dupMe->lr_y;
newSpot->sprite = mem_strdup(dupMe->sprite);
newSpot->vocab = mem_strdup(dupMe->vocab);
newSpot->verb = mem_strdup(dupMe->verb);
newSpot->prep = mem_strdup(dupMe->prep);
newSpot->syntax = dupMe->syntax;
newSpot->cursor_number = dupMe->cursor_number;
newSpot->facing = dupMe->facing;
newSpot->feet_x = dupMe->feet_x;
newSpot->feet_y = dupMe->feet_y;
newSpot->next = nullptr;
return newSpot;
}
static int32 hotspot_area(HotSpotRec *h) {
if (!h)
return _MAXPOSINT;
return (h->lr_x - h->ul_x) * (h->lr_y - h->ul_y);
}
HotSpotRec *hotspot_add(HotSpotRec *head, HotSpotRec *h, bool new_head) {
const int hArea = hotspot_area(h);
// Is head nullptr?
if (!head)
return h;
// Is the incoming spot the new head?
if (new_head || hArea < hotspot_area(head)) {
h->next = head;
return h;
}
HotSpotRec *i = head;
while (i) {
const int iArea = hotspot_area(i->next);
if (hArea < iArea) {
h->next = i->next;
i->next = h;
i = nullptr;
} else
i = i->next;
}
return head;
}
HotSpotRec *hotspot_add_dynamic(const char *verb, const char *noun,
int32 x1, int32 y1, int32 x2, int32 y2, int32 cursor,
bool new_head, int32 walkto_x, int32 walkto_y, int32 facing) {
int32 status;
ScreenContext *sc = vmng_screen_find(_G(gameDrawBuff), &status);
y2 = MIN(y2, sc->y2);
HotSpotRec *hotspot = hotspot_new(x1, y1, x2, y2);
if (!hotspot)
error("hotspot_new failed");
hotspot_newVocab(hotspot, noun);
hotspot_newVerb(hotspot, verb);
hotspot->feet_x = walkto_x;
hotspot->feet_y = walkto_y;
hotspot->cursor_number = cursor;
hotspot->facing = facing;
_G(currentSceneDef).hotspots = hotspot_add(_G(currentSceneDef).hotspots,
hotspot, new_head);
return hotspot;
}
void kill_hotspot_node(HotSpotRec *h) {
if (!h)
return;
if (h->vocab)
mem_free(h->vocab);
if (h->verb)
mem_free(h->verb);
if (h->sprite)
mem_free(h->sprite);
if (h->prep)
mem_free(h->prep);
mem_free(h);
}
HotSpotRec *hotspot_delete_record(HotSpotRec *head, HotSpotRec *h) {
HotSpotRec *i;
if (!h || !head)
return head;
// Are we deleting the head node?
if (head == h) {
head = h->next;
kill_hotspot_node(h);
return head; // This will of course be nullptr if the head was the only thing in the list.
}
// Find parent of current selection
for (i = head; i; i = i->next)
if (i->next == h)
break;
if (i)
i->next = h->next;
kill_hotspot_node(h);
return head;
}
HotSpotRec *hotspot_unlink(HotSpotRec *head, HotSpotRec *h) {
HotSpotRec *i;
if (!h || !head)
return head;
// Are we deleting the head node?
if (head == h) {
head = h->next;
return (head); // This will of course be nullptr if the head was the only thing in the list.
}
// Find parent of current selection
for (i = head; i; i = i->next)
if (i->next == h)
break;
if (i)
i->next = h->next;
return head;
}
void hotspot_delete_all(HotSpotRec *head) {
HotSpotRec *next;
for (HotSpotRec *i = head; i; i = next) {
next = i->next;
kill_hotspot_node(i);
}
}
HotSpotRec *hotspot_which(HotSpotRec *head, int x, int y) {
for (HotSpotRec *i = head; i; i = i->next)
if ((x >= i->ul_x) && (x <= i->lr_x) && (y >= i->ul_y) && (y <= i->lr_y) && i->active)
return i;
return nullptr;
}
HotSpotRec *hotspot_which(int x, int y) {
return hotspot_which(_G(currentSceneDef).hotspots, x, y);
}
void hotspot_set_active(HotSpotRec *head, const char *name, bool active_or_not) {
char name_str[MAX_FILENAME_SIZE];
bool hotspot_found = false;
cstrncpy(name_str, name, MAX_FILENAME_SIZE);
cstrupr(name_str);
for (HotSpotRec *i = head; i; i = i->next) {
if (i->vocab && !scumm_strnicmp(i->vocab, name_str, MAX_FILENAME_SIZE)) {
i->active = active_or_not;
hotspot_found = true;
}
}
if (hotspot_found != true)
term_message("hotspot '%s' not found!", name_str);
}
void hotspot_set_active(const char *name, bool active_or_not) {
hotspot_set_active(_G(currentSceneDef).hotspots, name, active_or_not);
}
void hotspot_set_active_xy(HotSpotRec *head, const char *name, int32 x, int32 y, bool active_or_not) {
char name_str[MAX_FILENAME_SIZE];
cstrncpy(name_str, name, MAX_FILENAME_SIZE);
cstrupr(name_str);
for (HotSpotRec *i = head; i; i = i->next)
if (!scumm_strnicmp(i->vocab, name_str, MAX_FILENAME_SIZE))
if ((x >= i->ul_x) && (x <= i->lr_x) && (y >= i->ul_y) && (y <= i->lr_y))
i->active = active_or_not;
}
void hotspot_set_active_xy(const char *name, int32 x, int32 y, bool active_or_not) {
hotspot_set_active_xy(_G(currentSceneDef).hotspots, name, x, y, active_or_not);
}
//-----------------------------------------------------------------------
static HotSpotRec *saved_hotspots = nullptr;
void hotspot_hide_all() {
if (saved_hotspots)
error_show(FL, 'HNST');
saved_hotspots = _G(currentSceneDef).hotspots;
_G(currentSceneDef).hotspots = nullptr;
}
void hotspot_restore_all() {
if (!saved_hotspots) {
error_show(FL, 'HNON');
}
if (_G(currentSceneDef).hotspots)
hotspot_delete_all(_G(currentSceneDef).hotspots);
_G(currentSceneDef).hotspots = saved_hotspots;
saved_hotspots = nullptr;
}
void hotspot_unhide_and_dump() {
if (saved_hotspots) {
hotspot_restore_all();
}
}
} // End of namespace M4

View File

@@ -0,0 +1,110 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_HOTSPOT_H
#define M4_ADV_R_ADV_HOTSPOT_H
#include "m4/m4_types.h"
namespace M4 {
struct HotSpotRec {
int32 ul_x, ul_y, lr_x, lr_y; // Hotspot screen coordinates
int32 feet_x, feet_y; // Walk-to target for player
int8 facing; // Direction player should face
bool active; // Flag if hotspot is active
byte cursor_number; // Mouse cursor number
byte syntax; // Word syntax
int32 vocabID, verbID; // ids of name and verb
char *vocab; // Vocabulary name of hotspot
char *verb; // Vocabulary default verb name
char *prep; // Preposition
char *sprite; // Sprite name
int16 hash; // woodscript sprite hash (runtime only)
HotSpotRec *next;
void clear();
};
HotSpotRec *hotspot_add_dynamic(
const char *verb, const char *noun,
int32 x1, int32 y1, int32 x2, int32 y2,
int32 cursor,
bool new_head = true,
int32 walkto_x = 32767, int32 walkto_y = 32767, int32 facing = 0);
/**
* Creates a new hot spot, doesn't add it to any list. Takes coords, sets everything
* else to default values.
* @returns The new hotspot
*/
HotSpotRec *hotspot_new(int x1, int y1, int x2, int y2);
/**
* Given a list and a hotspot to insert in that list, inserts the spot ordered by size,
* unless new_head is true, in which case the hotspot is the head regardless of size.
* @returns The new head of the list.
*/
HotSpotRec *hotspot_add(HotSpotRec *head, HotSpotRec *h, bool new_head);
HotSpotRec *hotspot_duplicate(HotSpotRec *dupMe);
/**
* Takes the head of a list of hot spots, and a pointer to a spot to delete.
* @returns The new head of the list.
*/
HotSpotRec *hotspot_delete_record(HotSpotRec *head, HotSpotRec *h);
/**
* Takes the head of a list of hot spots, and a pointer to a spot to unlink.
* @returns The new head of the list.
*/
HotSpotRec *hotspot_unlink(HotSpotRec *head, HotSpotRec *h);
void hotspot_delete_all(HotSpotRec *head);
void hotspot_set_active(HotSpotRec *head, const char *name, bool active_or_not);
void hotspot_set_active(const char *name, bool active_or_not);
void hotspot_set_active_xy(HotSpotRec *head, const char *name, int32 x, int32 y, bool active_or_not);
void hotspot_set_active_xy(const char *name, int32 x, int32 y, bool active_or_not);
#define kernel_flip_hotspot(aa,bb) (hotspot_set_active(currentSceneDef.hotspots,aa,bb))
#define kernel_flip_hotspot_xy(aa,bb,xx,yy) (hotspot_set_active_xy(currentSceneDef.hotspots,aa,xx,yy,bb))
#define kernel_flip_hotspot_loc(aa,bb,xx,yy) (hotspot_set_active_xy(currentSceneDef.hotspots,aa,xx,yy,bb))
void hotspot_new_sprite(HotSpotRec *h, const char *verb);
void hotspot_newVerb(HotSpotRec *h, const char *verb);
void hotspot_newVocab(HotSpotRec *h, const char *vocab);
void hotspot_newPrep(HotSpotRec *h, const char *prep);
/**
* Given a list of spots to check, and an x,y coordinate pair,
* @returns A pointer to the hotspot we're inside, or nullptr if there's nothing.
*/
HotSpotRec *hotspot_which(HotSpotRec *head, int x, int y);
HotSpotRec *hotspot_which(int x, int y);
void kill_hotspot_node(HotSpotRec *h);
void hotspot_restore_all();
void hotspot_hide_all();
void hotspot_unhide_and_dump();
} // End of namespace M4
#endif

View File

@@ -0,0 +1,87 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY {} without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/adv_interface.h"
#include "m4/gui/gui_buffer.h"
#include "m4/gui/gui_vmng.h"
#include "m4/vars.h"
namespace M4 {
bool Interface::init(int arrow, int wait, int look, int grab, int use) {
_arrow = arrow;
_wait = wait;
_look = look;
_grab = grab;
_use = use;
return true;
}
void Interface::showWaitCursor() {
mouse_set_sprite(_wait);
}
void Interface::show() {
if (!_shown) {
gui_GrBuff_register(_x1, _y1, _G(gameInterfaceBuff),
SF_DRIFTER | SF_GET_KEY | SF_GET_MOUSE | SF_IMMOVABLE,
intr_EventHandler);
_shown = true;
}
}
void Interface::hide() {
if (_shown) {
vmng_screen_hide(_G(gameInterfaceBuff));
_visible = false;
}
}
void interface_hide() {
_GI().hide();
}
void interface_show() {
_GI().show();
}
void track_hotspots_refresh() {
_GI().track_hotspots_refresh();
}
bool intr_EventHandler(void *bufferPtr, int32 eventType, int32 event, int32 x, int32 y, bool *z) {
return _GI().eventHandler(bufferPtr, eventType, event, x, y, z);
}
void intr_cancel_sentence() {
_GI().cancel_sentence();
}
void intr_freshen_sentence() {
_GI().freshen_sentence();
}
void intr_freshen_sentence(int cursor) {
mouse_set_sprite(cursor);
_GI().freshen_sentence();
}
} // End of namespace M4

View File

@@ -0,0 +1,88 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_INTERFACE_H
#define M4_ADV_R_ADV_INTERFACE_H
#include "m4/m4_types.h"
namespace M4 {
#define INTERFACE_SPRITES 22
class Interface {
public:
bool _shown = false;
bool _visible = false;
int _x1 = 0, _y1 = 0, _x2 = 0, _y2 = 0;
int _arrow = 0;
int _wait = 0;
int _look = 0;
int _grab = 0;
int _use = 0;
public:
virtual ~Interface() {
}
virtual bool init(int arrow, int wait, int look, int grab, int use);
virtual void cancel_sentence() = 0;
virtual void freshen_sentence() = 0;
virtual bool set_interface_palette(RGB8 *myPalette) = 0;
virtual bool eventHandler(void *bufferPtr, int32 eventType, int32 event, int32 x, int32 y, bool *z) = 0;
virtual void track_hotspots_refresh() = 0;
/**
* Show the interface
*/
virtual void show();
/**
* Hide the interface
*/
void hide();
/**
* Show the wait cursor
*/
void showWaitCursor();
};
void interface_hide();
void interface_show();
void track_hotspots_refresh();
bool intr_EventHandler(void *bufferPtr, int32 eventType, int32 event, int32 x, int32 y, bool *z);
void intr_cancel_sentence();
void intr_freshen_sentence();
void intr_freshen_sentence(int cursor);
} // End of namespace M4
#endif

View File

@@ -0,0 +1,248 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/str.h"
#include "m4/adv_r/adv_inv.h"
#include "m4/core/cstring.h"
#include "m4/core/errors.h"
#include "m4/mem/mem.h"
#include "m4/vars.h"
namespace M4 {
#define MAX_NAME_LENGTH 40
static char *inv_get_name(const Common::String &itemName);
InventoryBase::~InventoryBase() {
for (int i = 0; i < _tail; i++) {
mem_free(_objects[i]->name);
mem_free(_objects[i]->verbs);
mem_free_to_stash((void *)_objects[i], _G(inv_obj_mem_type));
}
}
void InventoryBase::syncGame(Common::Serializer &s) {
char invName[MAX_NAME_LENGTH];
uint32 inv_size = _tail * (MAX_NAME_LENGTH + sizeof(uint32));
s.syncAsUint32LE(inv_size);
if (s.isLoading()) {
assert((inv_size % (MAX_NAME_LENGTH + sizeof(uint32))) == 0);
_tail = inv_size / (MAX_NAME_LENGTH + sizeof(uint32));
}
for (int32 i = 0; i < _tail; ++i) {
char *objName = _G(inventory)->_objects[i]->name;
uint32 scene = _G(inventory)->_objects[i]->scene;
if (s.isLoading()) {
s.syncBytes((byte *)invName, MAX_NAME_LENGTH);
char *item = inv_get_name(invName);
assert(item);
s.syncAsUint32LE(scene);
inv_put_thing_in(item, scene);
} else {
Common::strcpy_s(invName, MAX_NAME_LENGTH, objName);
s.syncBytes((byte *)invName, MAX_NAME_LENGTH);
s.syncAsUint32LE(scene);
}
}
}
bool inv_init(int32 num_objects) {
term_message("Fluffing up the backpack", nullptr);
_G(inventory)->_objects.resize(num_objects);
if (!mem_register_stash_type(&_G(inv_obj_mem_type), sizeof(InvObj), num_objects, "obj"))
error_show(FL, 'OOM!', "fail to mem_register_stash_type for inv_obj");
for (int i = 0; i < num_objects; i++) {
_G(inventory)->_objects[i] = (InvObj *)mem_get_from_stash(_G(inv_obj_mem_type), "obj");
if (!_G(inventory)->_objects[i])
error_show(FL, 'OOM!', "%d bytes", (int32)sizeof(InvObj));
}
_G(inventory)->_tail = 0;
return true;
}
bool inv_register_thing(const Common::String &itemName, const Common::String &itemVerbs,
int32 scene, int32 cel, int32 cursor) {
char *s_name = mem_strdup(itemName.c_str());
char *s_verbs = mem_strdup(itemVerbs.c_str());
_G(inventory)->_objects[_G(inventory)->_tail]->name = nullptr;
_G(inventory)->_objects[_G(inventory)->_tail]->verbs = nullptr;
if (s_name) {
cstrupr(s_name);
_G(inventory)->_objects[_G(inventory)->_tail]->name = s_name;
}
if (s_verbs) {
cstrupr(s_verbs);
_G(inventory)->_objects[_G(inventory)->_tail]->verbs = s_verbs;
}
_G(inventory)->_objects[_G(inventory)->_tail]->scene = scene;
_G(inventory)->_objects[_G(inventory)->_tail]->cel = cel;
_G(inventory)->_objects[_G(inventory)->_tail]->cursor = cursor;
_G(inventory)->_tail++;
if (scene == BACKPACK) {
_G(inventory)->add(s_name, s_verbs, cel, cursor);
}
return true;
}
//-------------------------------------------------------------------
int32 inv_where_is(const Common::String &itemName) {
Common::String name = itemName;
name.toUppercase();
for (int i = 0; i < _G(inventory)->_tail; i++) {
if (_G(inventory)->_objects[i]->name) {
if (name.equals(_G(inventory)->_objects[i]->name)) {
return _G(inventory)->_objects[i]->scene;
}
}
}
return UNKNOWN_OBJECT;
}
bool inv_player_has(const Common::String &itemName) {
return (inv_where_is(itemName) == BACKPACK);
}
bool inv_put_thing_in(const Common::String &itemName, int32 scene) {
Common::String name = itemName;
name.toUppercase();
for (int i = 0; i < _G(inventory)->_tail; i++) {
if (_G(inventory)->_objects[i]->name) {
if (name.equals(_G(inventory)->_objects[i]->name)) {
// Remove object from backpack?
if (_G(inventory)->_objects[i]->scene == BACKPACK && scene != BACKPACK) {
_G(inventory)->remove(name);
}
_G(inventory)->_objects[i]->scene = scene;
// Put object in backpack?
if (scene == BACKPACK) {
_G(inventory)->add(name, _G(inventory)->_objects[i]->verbs, _G(inventory)->_objects[i]->cel, _G(inventory)->_objects[i]->cursor);
}
return true;
}
}
}
return false;
}
int32 inv_get_cursor(const Common::String &itemName) {
Common::String name = itemName;
name.toUppercase();
for (int i = 0; i < _G(inventory)->_tail; i++) {
if (_G(inventory)->_objects[i]->name) {
if (name.equals(_G(inventory)->_objects[i]->name)) {
return _G(inventory)->_objects[i]->cursor;
}
}
}
return UNKNOWN_OBJECT;
}
int32 inv_get_cel(const Common::String &itemName) {
Common::String name = itemName;
name.toUppercase();
for (int i = 0; i < _G(inventory)->_tail; i++) {
if (_G(inventory)->_objects[i]->name) {
if (name.equals(_G(inventory)->_objects[i]->name)) {
return _G(inventory)->_objects[i]->cel;
}
}
}
return UNKNOWN_OBJECT;
}
const char *inv_get_verbs(const Common::String &itemName) {
Common::String name = itemName;
name.toUppercase();
for (int i = 0; i < _G(inventory)->_tail; i++) {
if (_G(inventory)->_objects[i]->name) {
if (name.equals(_G(inventory)->_objects[i]->name)) {
return _G(inventory)->_objects[i]->verbs;
}
}
}
return nullptr;
}
// This is provided so that when restoring a game from a save file,
// we store a pointer to the registered name, not to an unmanaged
// memory pointer.
static char *inv_get_name(const Common::String &itemName) {
Common::String name = itemName;
name.toUppercase();
for (int i = 0; i < _G(inventory)->_tail; i++) {
if (_G(inventory)->_objects[i]->name) {
if (name.equals(_G(inventory)->_objects[i]->name)) {
return _G(inventory)->_objects[i]->name;
}
}
}
return nullptr;
}
void inv_give_to_player(const Common::String &itemName) {
inv_put_thing_in(itemName, BACKPACK);
}
void inv_move_object(const Common::String &itemName, int32 scene) {
inv_put_thing_in(itemName, scene);
}
bool inv_object_is_here(const Common::String &itemName) {
return (inv_where_is(itemName) == _G(game).room_id);
}
bool inv_object_in_scene(const Common::String &itemName, int32 scene) {
return (inv_where_is(itemName) == scene);
}
} // End of namespace M4

View File

@@ -0,0 +1,82 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_INV_H
#define M4_ADV_R_ADV_INV_H
#include "common/array.h"
#include "common/serializer.h"
namespace M4 {
struct InvObj {
char *name = nullptr;
char *verbs = nullptr;
int32 scene = 0, cel = 0, cursor = 0;
};
struct InventoryBase {
Common::Array<InvObj *> _objects;
int32 _tail = 0;
InventoryBase() {}
virtual ~InventoryBase();
void syncGame(Common::Serializer &s);
virtual void add(const Common::String &name, const Common::String &verb, int32 sprite, int32 cursor) = 0;
virtual void set_scroll(int32 scroll) = 0;
virtual void remove(const Common::String &name) = 0;
};
/**
* Init the system, preferably in game_systems_initialize
*/
bool inv_init(int32 num_objects);
/**
* Register things during init of the game
* @param itemName Name of the object as it should appear as a sentence is built
* @param itemVerbs Verbs should have this format: verbs = "slit,peel,fricasee,examine"
* There can be any number of verbs in the string.
* @param scene The place for the thing to appear initially (BACKPACK is one place)
* @param cel Index into the inventory sprite series for use when displaying inventory
* @param cursor Cel index into the cursor sprite series when the player is "holding" a thing
*/
bool inv_register_thing(const Common::String &itemName, const Common::String &itemVerbs, int32 scene, int32 cel, int32 cursor);
int32 inv_where_is(const Common::String &itemName);
bool inv_player_has(const Common::String &itemName);
bool inv_put_thing_in(const Common::String &itemName, int32 scene);
int32 inv_get_cursor(const Common::String &itemName);
int32 inv_get_cel(const Common::String &itemName);
const char *inv_get_verbs(const Common::String &itemName);
void inv_give_to_player(const Common::String &itemName);
void inv_move_object(const Common::String &itemName, int32 scene);
bool inv_object_is_here(const Common::String &itemName);
bool inv_object_in_scene(const Common::String &itemName, int32 scene);
void inv_sync_game(Common::Serializer &s);
} // End of namespace M4
#endif

View File

@@ -0,0 +1,246 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/adv_player.h"
#include "m4/adv_r/adv_walk.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/gui/gui_vmng_core.h"
#include "m4/vars.h"
namespace M4 {
void Player::syncGame(Common::Serializer &s) {
s.skip(12);
s.syncBytes((byte *)verb, MAX_PLYR_STRING_LEN);
s.syncBytes((byte *)noun, MAX_PLYR_STRING_LEN);
s.syncBytes((byte *)object, MAX_PLYR_STRING_LEN);
s.syncBytes((byte *)prep, MAX_PLYR_STRING_LEN);
s.skip(64);
s.syncAsSint16LE(walker_type);
s.syncAsSint16LE(shadow_type);
s.skip(4);
s.syncAsByte(need_to_walk);
s.syncAsByte(ready_to_walk);
s.syncAsByte(waiting_for_walk);
s.syncAsByte(comm_allowed);
s.syncAsUint32LE(command_ready);
s.syncAsByte(walker_visible);
s.skip(1);
s.syncAsByte(been_here_before);
s.syncAsByte(walker_reload_palette);
s.syncAsByte(disable_hyperwalk);
s.syncAsByte(walker_loads_first);
s.syncAsByte(walker_in_this_scene);
s.syncAsSint32LE(walker_trigger);
s.syncAsSint32LE(walk_x);
s.syncAsSint32LE(walk_y);
s.syncAsSint32LE(walk_facing);
s.syncAsSint32LE(click_x);
s.syncAsSint32LE(click_y);
}
void Player::resetWalk() {
need_to_walk = false;
ready_to_walk = true;
waiting_for_walk = false;
}
void PlayerInfo::syncGame(Common::Serializer &s) {
s.syncAsSint32LE(x);
s.syncAsSint32LE(y);
s.syncAsSint32LE(facing);
s.syncAsSint32LE(scale);
s.syncAsSint32LE(depth);
s.syncAsSint32LE(camera_x);
s.syncAsSint32LE(camera_y);
}
bool player_said(const char *w0, const char *w1, const char *w2) {
const char *ptrs[3] = { w0, w1, w2 };
for (int i = 0; i < 3; i++) {
if (ptrs[i])
if ((scumm_strnicmp(_G(player).noun, ptrs[i], MAX_PLYR_STRING_LEN))
&& (scumm_strnicmp(_G(player).object, ptrs[i], MAX_PLYR_STRING_LEN))
&& (scumm_strnicmp(_G(player).verb, ptrs[i], MAX_PLYR_STRING_LEN)))
return false;
}
return true;
}
bool player_said_any(const char *w0, const char *w1, const char *w2,
const char *w3, const char *w4, const char *w5, const char *w6,
const char *w7, const char *w8, const char *w9) {
const char *ptrs[10] = { w0, w1, w2, w3, w4, w5, w6, w7, w8, w9 };
for (int i = 0; i < 10; i++) {
if (ptrs[i]) {
if (!scumm_strnicmp(_G(player).noun, ptrs[i], MAX_PLYR_STRING_LEN))
return true;
if (!scumm_strnicmp(_G(player).object, ptrs[i], MAX_PLYR_STRING_LEN))
return true;
if (!scumm_strnicmp(_G(player).verb, ptrs[i], MAX_PLYR_STRING_LEN))
return true;
}
}
return false;
}
void player_inform_walker_new_scale(int32 frontY, int32 backY, int32 frontS, int32 backS) {
_G(globals)[GLB_MIN_Y] = backY << 16;
_G(globals)[GLB_MAX_Y] = frontY << 16;
_G(globals)[GLB_MIN_SCALE] = FixedDiv(backS << 16, 100 << 16);
_G(globals)[GLB_MAX_SCALE] = FixedDiv(frontS << 16, 100 << 16);
if (_G(globals)[GLB_MIN_Y] == _G(globals)[GLB_MAX_Y])
_G(globals)[GLB_SCALER] = 0;
else
_G(globals)[GLB_SCALER] = FixedDiv(_G(globals)[GLB_MAX_SCALE] - _G(globals)[GLB_MIN_SCALE], _G(globals)[GLB_MAX_Y] - _G(globals)[GLB_MIN_Y]);
}
// This routine must also load shadow animations
bool player_load_series(const char *walkerName, const char *shadowName, bool load_palette) {
int i;
int32 thatRoomCode;
char assetPath[MAX_FILENAME_SIZE];
char *tempPtr;
// Load walker
db_rmlst_get_asset_room_path(walkerName, assetPath, &thatRoomCode);
tempPtr = strrchr(assetPath, '.');
if (!tempPtr)
return false;
tempPtr--;
if ((*tempPtr < '0') || (*tempPtr > '9'))
return false;
for (i = 1; i <= 5; i++) {
*tempPtr = (char)((int32)'0' + i);
AddWSAssetCELS(assetPath, i - 1, load_palette ? _G(master_palette) : nullptr);
}
// Load walker shadow
db_rmlst_get_asset_room_path(shadowName, assetPath, &thatRoomCode);
tempPtr = strrchr(assetPath, '.');
if (!tempPtr)
return false;
tempPtr--;
if ((*tempPtr < '0') || (*tempPtr > '9'))
return false;
for (i = 1; i <= 5; i++) {
*tempPtr = (char)((int32)'0' + i);
AddWSAssetCELS(assetPath, i + 4, nullptr);
}
sendWSMessage(0, 0, nullptr, 6, nullptr, 1); // Hash 6 is the shadow machine
return true;
}
void player_first_walk(int32 x1, int32 y1, int32 f1, int32 x2, int32 y2, int32 f2, bool /*enable_commands_at_destination*/) {
ws_demand_location(x1, y1, f1);
ws_walk(x2, y2, nullptr, -1, f2);
}
void player_set_defaults() {
_G(player).walker_visible = false;
_G(player).disable_hyperwalk = false;
_G(player).walker_loads_first = true;
_G(player).walker_reload_palette = true;
_G(player).walker_in_this_scene = true;
}
bool player_commands_allowed() {
return _G(player).comm_allowed;
}
PlayerInfo *player_update_info(machine *myWalker, PlayerInfo *player_info) {
if (!myWalker)
return nullptr;
ws_get_walker_info(myWalker, &player_info->x, &player_info->y,
&player_info->scale, &player_info->depth, &player_info->facing);
int32 status;
ScreenContext *game_buff_ptr = vmng_screen_find(_G(gameDrawBuff), &status);
player_info->camera_x = game_buff_ptr->x1;
player_info->camera_y = game_buff_ptr->y1;
return player_info;
}
PlayerInfo *player_update_info() {
return player_update_info(_G(my_walker), &_G(player_info));
}
void player_set_facing_hotspot(int trigger) {
player_set_facing_at(_G(player).click_x, _G(player).click_y, trigger);
}
void player_set_facing_at(int x, int y, int trigger) {
player_hotspot_walk_override_just_face(calc_facing(x, y), trigger);
}
int calc_facing(int x, int y) {
player_update_info(_G(my_walker), &_G(player_info));
if (!x) {
return -_G(player_info).y < -y;
} else {
double slope = (double)(y - _G(player_info).y) / (double)(x - _G(player_info).x);
term_message("click (%d,%d) player (%d,%d) slope = %f",
x, -y, _G(player_info).x, -_G(player_info).y);
if (_G(player_info).x < x) {
if (slope >= 1.25)
return 1;
else if (slope >= 0.1)
return 2;
else if (slope >= -0.1)
return 3;
else if (slope >= -0.4)
return 4;
else
return 5;
} else {
if (slope >= 0.4)
return 7;
else if (slope >= 0.1)
return 8;
else if (slope >= -0.1)
return 9;
else if (slope >= -1.25)
return 10;
else
return 11;
}
}
}
} // End of namespace M4

View File

@@ -0,0 +1,127 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_PLAYER_H
#define M4_ADV_R_ADV_PLAYER_H
#include "common/serializer.h"
#include "m4/m4_types.h"
#include "m4/wscript/ws_machine.h"
namespace M4 {
#define MAX_PLYR_STRING_LEN 40
struct Player {
int32 x = 0, y = 0; // Player's current screen location
int32 facing = 0; // Player's current directional facing
char verb[MAX_PLYR_STRING_LEN] = { 0 };
char noun[MAX_PLYR_STRING_LEN] = { 0 };
char prep[MAX_PLYR_STRING_LEN] = { 0 };
char object[MAX_PLYR_STRING_LEN] = { 0 };
char ws_asset_name[32] = { 0 }; // Name of the walker sprite series holder
char ws_shadow_name[32] = { 0 }; // Name of the walker sprite series shadow holder
int16 walker_type = 0; // Type of walker (ripley, mei_chin, safari, etc.)
int16 shadow_type = 0; // Type of walker (ripley shadow, candleman shadow, etc.)
// If he walks off edge, this holds the number of the room he goes to
int32 walk_off_edge_to_room = 0; // Player should walk off edge unless told otherwise
// If False player won't walk - totally aborts a walk
bool need_to_walk = false; // Player needs to walk somewhere
// If False player won't walk yet - walk suspended until TRUE
bool ready_to_walk = false; // Player is ready to perform that walk
bool waiting_for_walk = false;
// Default True for every room
// Flag if accepting player input
bool comm_allowed = false;
// Means a parser command is ready. When apps programmer finishes
// parsing command, the programmer must set this to FALSE. If this
// doesn't get set, the command falls through to the error handling code.
int32 command_ready = 0;
// Default True, if set to FALSE walker disappears instantly, and vv
bool walker_visible = false; // Flag if player's sprite is visible
// If True, then apps programmer must display look messages
bool look_around = false; // Flag for special "look around" command
// Tested by the apps programmer to check if player has been here before
bool been_here_before = false;
// Walker sprites dumped on switching scenes if TRUE in room preload code
bool walker_reload_palette = false;
bool disable_hyperwalk = false;
bool walker_loads_first = false;
bool walker_in_this_scene = false;
int32 walker_trigger = 0;
int32 walk_x = 0, walk_y = 0; // Where to walk to when player.ready_to_walk
int32 walk_facing = 0;
int32 click_x = 0, click_y = 0;
void syncGame(Common::Serializer &s);
void resetWalk();
};
struct PlayerInfo {
int32 x = 0, y = 0, facing = 0;
int32 scale = 0, depth = 0;
int32 camera_x = 0, camera_y = 0;
void syncGame(Common::Serializer &s);
};
bool player_said(const char *w0, const char *w1 = nullptr, const char *w2 = nullptr);
bool player_said_any(const char *w0, const char *w1 = nullptr, const char *w2 = nullptr,
const char *w3 = nullptr, const char *w4 = nullptr, const char *w5 = nullptr, const char *w6 = nullptr,
const char *w7 = nullptr, const char *w8 = nullptr, const char *w9 = nullptr);
void player_inform_walker_new_scale(int32 frontY, int32 backY, int32 frontS, int32 backS);
bool player_load_series(const char *walkerName, const char *shadowName, bool load_palette);
void player_first_walk(int32 x1, int32 y1, int32 f1, int32 x2, int32 y2, int32 f2, bool enable_commands_at_destination);
void player_set_defaults();
void player_hotspot_walk_override(int32 x, int32 y, int32 facing = -1, int32 trigger = -1);
void player_hotspot_walk_override_just_face(int32 facing, int32 trigger = -1);
bool player_commands_allowed();
PlayerInfo *player_update_info(machine *myWalker, PlayerInfo *player_info);
PlayerInfo *player_update_info();
void adv_kill_digi_between_rooms(bool true_or_false);
void player_set_facing_hotspot(int trigger = -1);
void player_set_facing_at(int x, int y, int trigger = -1);
int calc_facing(int x, int y);
} // End of namespace M4
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,71 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_RAILS_H
#define M4_ADV_R_ADV_RAILS_H
#include "m4/m4_types.h"
#include "m4/adv_r/adv.h"
#include "m4/wscript/ws_machine.h"
namespace M4 {
struct Rails_Globals {
railNode **myNodes = nullptr;
int16 *myEdges = nullptr;
railNode **stackBottom = nullptr, **stackTop = nullptr;
noWalkRect *noWalkRectList = nullptr;
int32 memtypePATHN = 0;
};
bool InitRails();
void rail_system_shutdown();
void ClearRails();
noWalkRect *intr_add_no_walk_rect(int32 x1, int32 y1, int32 x2, int32 y2, int32 altX, int32 altY, Buffer *walkCodes);
noWalkRect *intr_add_no_walk_rect(int32 x1, int32 y1, int32 x2, int32 y2, int32 altX, int32 altY);
void intr_move_no_walk_rect(noWalkRect *myRect, int32 new_x1, int32 new_y1,
int32 new_x2, int32 new_y2, int32 new_altX, int32 new_altY, Buffer *walkCodes);
void intr_remove_no_walk_rect(noWalkRect *myRect, Buffer *walkCodes);
void intr_remove_no_walk_rect(noWalkRect *myRect);
void CreateEdge(int32 node1, int32 node2, Buffer *walkCodes);
void RestoreNodeEdges(int32 nodeID, Buffer *walkCodes);
void RestoreEdgeList(Buffer *walkCodes);
int32 AddRailNode(int32 x, int32 y, Buffer *walkCodes, bool restoreEdges);
void MoveRailNode(int32 nodeID, int32 x, int32 y, Buffer *walkCodes, bool restoreEdges);
bool RemoveRailNode(int32 nodeID, Buffer *walkCodes, bool restoreEdges);
bool RailNodeExists(int32 nodeID, int32 *nodeX, int32 *nodeY);
int16 GetEdgeLength(int32 node1, int32 node2);
bool GetShortestPath(int32 origID, int32 destID, railNode **shortPath);
railNode *CreateCustomPath(int coord, ...);
void DisposePath(railNode *pathStart);
bool intr_LineCrossesRect(int32 line_x1, int32 line_y1, int32 line_x2, int32 line_y2,
int32 rect_x1, int32 rect_y1, int32 rect_x2, int32 rect_y2);
bool intr_LinesCross(int32 line1_x1, int32 line1_y1, int32 line1_x2, int32 line1_y2,
int32 line2_x1, int32 line2_y1, int32 line2_x2, int32 line2_y2);
bool intr_PathCrossesLine(int32 startX, int32 startY, railNode *pathStart,
int32 line_x1, int32 line_y1, int32 line_x2, int32 line_y2);
} // End of namespace M4
#endif

View File

@@ -0,0 +1,136 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/adv_scale.h"
#include "m4/core/errors.h"
#include "m4/graphics/gr_pal.h"
#include "m4/graphics/gr_line.h"
#include "m4/gui/gui_vmng.h"
#include "m4/vars.h"
namespace M4 {
#define LABEL_OFFSET 8
#define _GS(X) _G(scale)._##X
static void scale_editor_clear_rects() {
_GS(old_front) = _GS(old_back) = -1;
_GS(myback) = _GS(myfront) = _GS(mybs) = _GS(myfs) = -1;
}
static void scale_editor_init() {
scale_editor_clear_rects();
}
static void scale_editor_undraw() {
Buffer *scr_orig = _G(game_bgBuff)->get_buffer();
Buffer *game_buff = _G(gameDrawBuff)->get_buffer();
int32 status;
ScreenContext *game_buff_ptr = vmng_screen_find(_G(gameDrawBuff), &status);
if (_GS(old_back) != -1) {
gr_buffer_rect_copy_2(scr_orig, game_buff, 0, _GS(old_back) - LABEL_OFFSET, 0,
_GS(old_back) - LABEL_OFFSET, scr_orig->w, LABEL_OFFSET + 1);
RestoreScreensInContext(0, _GS(old_back) - LABEL_OFFSET, scr_orig->w, _GS(old_back) + 1, game_buff_ptr);
_GS(old_back) = -1;
}
if (_GS(old_front) != -1) {
gr_buffer_rect_copy_2(scr_orig, game_buff, 0, _GS(old_front) - LABEL_OFFSET, 0,
_GS(old_front) - LABEL_OFFSET, scr_orig->w, LABEL_OFFSET + 1);
RestoreScreensInContext(0, _GS(old_front) - LABEL_OFFSET, scr_orig->w, _GS(old_front) + 1, game_buff_ptr);
_GS(old_front) = -1;
}
_G(game_bgBuff)->release();
_G(gameDrawBuff)->release();
scale_editor_clear_rects();
}
void scale_editor_draw() {
bool bail = true;
// Have we drawn before?
if (_GS(myback) == -1 && _GS(myfront) == -1)
bail = false;
// Has back or front y changed?
if (bail && _GS(myback) != _G(currentSceneDef).back_y && _GS(myfront) != _G(currentSceneDef).front_y)
bail = false;
// Has back or front scale changed?
if (bail && _GS(mybs) != _G(currentSceneDef).back_scale && _GS(myfs) != _G(currentSceneDef).front_scale)
bail = false;
// No changes, don't draw.
if (bail)
return;
scale_editor_undraw();
Buffer *game_buff = _G(gameDrawBuff)->get_buffer();
int32 status;
ScreenContext *game_buff_ptr = vmng_screen_find(_G(gameDrawBuff), &status);
Buffer *scr_orig = _G(game_bgBuff)->get_buffer();
gr_color_set(__YELLOW);
_GS(myback) = _GS(old_back) = _G(currentSceneDef).back_y;
_GS(myfront) = _GS(old_front) = _G(currentSceneDef).front_y;
_GS(mybs) = _G(currentSceneDef).back_scale;
_GS(myfs) = _G(currentSceneDef).front_scale;
gr_hline(game_buff, 0, scr_orig->w, _GS(old_back));
gr_hline(game_buff, 0, scr_orig->w, _GS(old_front));
char string[20];
gr_font_set_color(__WHITE);
gr_font_set(_G(font_tiny));
Common::sprintf_s(string, "Front: %d, %d", _GS(old_front), _G(currentSceneDef).front_scale);
int x;
for (x = 10; x < scr_orig->w - 220; x += 400)
gr_font_write(game_buff, string, x, _GS(old_front) - LABEL_OFFSET, 0, 0);
Common::sprintf_s(string, "Back: %d, %d", _GS(old_back), _G(currentSceneDef).back_scale);
for (x = 110; x < scr_orig->w - 320; x += 400)
gr_font_write(game_buff, string, x, _GS(old_back) - LABEL_OFFSET, 0, 0);
RestoreScreensInContext(0, _GS(old_back) - LABEL_OFFSET, scr_orig->w, _GS(old_back) + 1, game_buff_ptr);
RestoreScreensInContext(0, _GS(old_front) - LABEL_OFFSET, scr_orig->w, _GS(old_front) + 1, game_buff_ptr);
_G(game_bgBuff)->release();
_G(gameDrawBuff)->release();
}
void scale_editor_cancel() {
scale_editor_undraw();
_G(editors_in_use) &= ~kScaleEditor;
}
void scale_editor_toggle() {
if (_G(editors_in_use) & kScaleEditor)
scale_editor_cancel();
else {
_G(editors_in_use) |= kScaleEditor;
scale_editor_init();
}
}
} // End of namespace M4

View File

@@ -0,0 +1,47 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_SCALE_H
#define M4_ADV_R_ADV_SCALE_H
#include "m4/m4_types.h"
namespace M4 {
constexpr int kScaleEditor = 1;
struct ADVScale_Globals {
int32 _old_front = -1;
int32 _old_back = -1;
int _myback = -1;
int _myfront = -1;
int _mybs = -1;
int _myfs = -1;
};
void scale_editor_draw();
void scale_editor_cancel();
void scale_editor_toggle();
} // End of namespace M4
#endif

View File

@@ -0,0 +1,187 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/adv_trigger.h"
#include "m4/core/errors.h"
#include "m4/vars.h"
#include "m4/m4.h"
#include "m4/platform/timer.h"
namespace M4 {
#define _GT(X) _G(triggers)._##X
int32 kernel_trigger_create(int32 trigger_num) {
if (trigger_num < 0)
return (trigger_num);
if (trigger_num > 0xffff) { // If room changed, this is an invalid trigger
error_show(FL, 'BADT', "bad trigger. %d > 0xffff", trigger_num);
}
const int32 new_trigger = trigger_num + (_G(game).room_id << 16) + (_G(kernel).trigger_mode << 28);
return new_trigger;
}
bool kernel_trigger_dispatch_now(int32 trigger_num) {
if (g_engine->getGameType() == GType_Riddle)
return kernel_trigger_dispatchx(trigger_num);
return kernel_trigger_dispatchx(kernel_trigger_create(trigger_num));
}
void cisco_dispatch_triggers() {
for (int i = 0; i < _GT(q_end); ++i) {
kernel_trigger_dispatchx(_GT(sound_trigger_q)[i]);
}
_GT(q_end) = 0;
}
void cisco_clear_triggers() {
_GT(q_end) = _GT(q_start) = 0;
}
bool kernel_trigger_dispatchx(int32 trigger_num) {
if (_G(between_rooms))
return true;
const KernelTriggerType old_trigger_mode = _G(kernel).trigger_mode;
const int32 old_trigger = _G(kernel).trigger;
bool result = false;
if (trigger_num < 0)
return false;
if (((trigger_num >> 16) & 0xfff) != _G(game).room_id) { // if room changed, this is an invalid trigger
term_message("orphan scene trigger:mode: %d, scene: %d, trigger: %d",
trigger_num >> 28, (trigger_num >> 16) & 0xffff, trigger_num & 0xffff);
return false;
}
_G(kernel).trigger = trigger_num & 0xffff; // If no command in Q, must be here because of code
switch (trigger_num >> 28) {
case KT_PREPARSE:
if (_G(kernel).trigger >= 32000)
break;
_G(kernel).trigger_mode = KT_PREPARSE;
g_engine->room_pre_parser();
result = true;
break;
case KT_PARSE:
if (_G(kernel).trigger >= 32000)
break;
_G(kernel).trigger_mode = KT_PARSE;
_G(player).command_ready = true;
g_engine->room_parser();
if (_G(player).command_ready) {
g_engine->global_parser();
}
result = true;
break;
case KT_DAEMON:
g_engine->game_daemon_code();
result = true;
break;
default:
term_message("orphan mode trigger: mode: %d, scene: %d, trigger: %d",
trigger_num >> 28, (trigger_num >> 16) & 0xffff, trigger_num & 0xffff);
result = false;
break;
}
_G(kernel).trigger_mode = old_trigger_mode;
_G(kernel).trigger = old_trigger;
return result;
}
static void timer_callback(frac16 myMessage, struct machine *sender) {
kernel_trigger_dispatchx(myMessage);
}
void kernel_timing_trigger(int32 ticks, int16 trigger, const char *name) {
if (ticks <= 0) {
// Trigger immediately
kernel_trigger_dispatchx(kernel_trigger_create(trigger));
return;
}
_G(globals)[GLB_TEMP_1] = ticks << 16;
_G(globals)[GLB_TEMP_2] = kernel_trigger_create(trigger);
if (name) {
const Common::String machName = Common::String::format("timer - %s", name);
TriggerMachineByHash(2, nullptr, -1, -1, timer_callback, false, machName.c_str());
} else {
TriggerMachineByHash(2, nullptr, -1, -1, timer_callback, false, "timer trigger");
}
}
void kernel_timing_trigger(int32 ticks, int16 trigger,
KernelTriggerType preMode, KernelTriggerType postMode) {
_G(kernel).trigger_mode = preMode;
kernel_timing_trigger(ticks, trigger, nullptr);
_G(kernel).trigger_mode = postMode;
}
void kernel_timing_trigger_daemon(int32 ticks, int16 trigger) {
const KernelTriggerType oldMode = _G(kernel).trigger_mode;
_G(kernel).trigger_mode = KT_DAEMON;
kernel_timing_trigger(ticks, trigger, nullptr);
_G(kernel).trigger_mode = oldMode;
}
void kernel_service_timing_trigger_q() {
// Dispatch pending timing triggers
int32 iter = 0;
const int32 now = timer_read_60();
while (iter < _GT(time_q_end) && _GT(time_q)[iter] <= now)
{
kernel_trigger_dispatchx(_GT(time_trigger_q)[iter]);
++iter;
}
if (!iter)
return;
// Remove dispatched triggers from the q
const int32 total = iter;
int32 dispatched = iter;
iter = 0;
while (dispatched < _GT(time_q_end)) {
_GT(time_q)[iter] = _GT(time_q)[dispatched];
_GT(time_trigger_q)[iter] = _GT(time_trigger_q)[dispatched];
++iter;
++dispatched;
}
_GT(time_q_end) -= total;
}
} // End of namespace M4

View File

@@ -0,0 +1,76 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_TRIGGER_H
#define M4_ADV_R_ADV_TRIGGER_H
#include "m4/m4_types.h"
#include "m4/adv_r/adv.h"
namespace M4 {
constexpr int NO_TRIGGER = -1;
constexpr int TENTH_SECOND = 6;
constexpr int MAX_TIMERS = 32;
struct Triggers {
int32 _time_q[MAX_TIMERS];
int32 _time_trigger_q[MAX_TIMERS];
int32 _time_q_end = 0;
int32 _sound_trigger_q[MAX_TIMERS];
int32 _q_start = 0;
int32 _q_end = 0;
};
/**
* Create a new trigger
* Trigger usage:
* [31-28][27-16][15-0]
* | | |
* | | +--- trigger_num (0 - 0xffff)
* | +--- room_id (0 - 4096) (0 - 0xfff)
* +--- trigger_mode (0-16)
*/
int32 kernel_trigger_create(int32 trigger_num);
int32 kernel_trigger_create_mode(int32 trigger_num, int32 desired_mode);
/**
* Dispatches a trigger.
* @returns Returns true if the trigger was handled. If the trigger is for
* a different room that current room_id, returns false. If no trigger_mode was
* attached to the trigger, returns false
*/
bool kernel_trigger_dispatchx(int32 trigger_num);
bool kernel_trigger_dispatch_now(int32 trigger_num);
void kernel_timing_trigger(int32 ticks, int16 trigger, const char *name = nullptr);
void kernel_timing_trigger_daemon(int32 ticks, int16 trigger);
void kernel_timing_trigger(int32 ticks, int16 trigger,
KernelTriggerType preMode, KernelTriggerType postMode);
void cisco_dispatch_triggers();
void cisco_clear_triggers();
void kernel_service_timing_trigger_q();
} // End of namespace M4
#endif

View File

@@ -0,0 +1,509 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/adv_walk.h"
#include "m4/adv_r/adv_trigger.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/graphics/gr_series.h"
#include "m4/wscript/wst_regs.h"
#include "m4/vars.h"
namespace M4 {
void set_walker_scaling(SceneDef *rdef) {
_G(globals)[GLB_MIN_Y] = rdef->back_y << 16;
_G(globals)[GLB_MAX_Y] = rdef->front_y << 16;
_G(globals)[GLB_MIN_SCALE] = FixedDiv(rdef->back_scale << 16, 100 << 16);
_G(globals)[GLB_MAX_SCALE] = FixedDiv(rdef->front_scale << 16, 100 << 16);
if (_G(globals)[GLB_MIN_Y] == _G(globals)[GLB_MAX_Y])
_G(globals)[GLB_SCALER] = 0;
else
_G(globals)[GLB_SCALER] = FixedDiv(_G(globals)[GLB_MAX_SCALE] - _G(globals)[GLB_MIN_SCALE], _G(globals)[GLB_MAX_Y] - _G(globals)[GLB_MIN_Y]);
}
static void ws_walkto_node(machine *myWalker, railNode *destNode, bool firstTime) {
// Parameter verification
if (!myWalker) {
error_show(FL, 'W:-(');
return;
}
if (!destNode) {
error_show(FL, 'WNDN');
return;
}
// Calculate the destination values x, y, s
const frac16 x = destNode->x << 16;
const frac16 y = destNode->y << 16;
const frac16 s = _G(globals)[GLB_MIN_SCALE] + FixedMul(y - _G(globals)[GLB_MIN_Y], _G(globals)[GLB_SCALER]);
// Plug in the destination x, y, and s
_G(globals)[GLB_TEMP_1] = x;
_G(globals)[GLB_TEMP_2] = y;
_G(globals)[GLB_TEMP_3] = s;
// Final direction (GLB_TEMP_4) and trigger (GLB_TEMP_5) are set in ws_walk() below
if (firstTime) {
if (_G(completeWalk)) {
_G(globals)[GLB_TEMP_6] = 0x10000; // so the feet will come together
} else {
_G(globals)[GLB_TEMP_6] = 0; // so the walker will freeze when he reaches the last node
}
sendWSMessage(STARTWALK << 16, 0, myWalker, 0, nullptr, 1);
} else {
sendWSMessage(WALKSEQ << 16, 0, myWalker, 0, nullptr, 1);
}
}
bool walker_has_walk_finished(machine *sender) {
// Parameter verification
if ((!sender) || (!sender->myAnim8)) {
error_show(FL, 'W:-(');
return false;
}
// Remove the node we just arrived at from the sender's walkPath
if (sender->walkPath) {
railNode *tempNode = sender->walkPath;
sender->walkPath = sender->walkPath->shortPath;
mem_free((void *)tempNode);
}
// If no more nodes to traverse (a canadian word), check if he's standing.
// If not standing, let him finish standing, then when he finishes standing
if (!sender->walkPath) {
return true;
} else {
// Else there are more nodes, so keep walking
ws_walkto_node(sender, sender->walkPath, false);
return false;
}
}
/**
* Called by player_walk
*/
void ws_walk(machine *myWalker, int32 x, int32 y, GrBuff **, int16 trigger, int32 finalFacing, bool complete_walk) {
int8 directions[14] = { 0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 9 };
int32 currNodeID, destNodeID;
if (!myWalker || !myWalker->myAnim8)
error_show(FL, 'W:-(');
// Get walker's current location
const int32 currX = myWalker->myAnim8->myRegs[IDX_X] >> 16;
const int32 currY = myWalker->myAnim8->myRegs[IDX_Y] >> 16;
// Add the walker's current location and the destination to the rail nodes...
Buffer *walkerCodes = nullptr;
if (_G(screenCodeBuff))
walkerCodes = _G(screenCodeBuff)->get_buffer();
if ((currNodeID = AddRailNode(currX, currY, walkerCodes, true)) < 0) {
error_show(FL, 'WCAN', "Walker's curr posn: %d %d", currX, currY);
}
if ((destNodeID = AddRailNode(x, y, walkerCodes, true)) < 0) {
error_show(FL, 'WCAN', "Trying to walk to: %d %d", x, y);
}
// Dispose of the current path myWalker is following
if (myWalker->walkPath) {
DisposePath(myWalker->walkPath);
}
// Find the shortest path between currNodeID, and destNodeID
const bool result = GetShortestPath(currNodeID, destNodeID, &(myWalker->walkPath));
// Now that a path has been found, remove the two extra added nodes
RemoveRailNode(currNodeID, walkerCodes, true);
RemoveRailNode(destNodeID, walkerCodes, true);
if (_G(screenCodeBuff))
_G(screenCodeBuff)->release();
// Check the success of GetShortestPath
if (!result) {
term_message("Player: Can't walk there!!!");
_G(player).waiting_for_walk = false;
return;
}
// If the result was true, but no path was returned, we are already there
if (!myWalker->walkPath) {
_G(player).need_to_walk = false;
//we can only turn to face the final direction
ws_turn_to_face(myWalker, finalFacing, trigger);
} else {
// Otherwise we have a path to follow, so get going...
// Verify that finalFacing is valid or set -1
if (finalFacing > 0 && finalFacing < 13) {
_G(globals)[GLB_TEMP_4] = directions[finalFacing] << 16;
} else {
_G(globals)[GLB_TEMP_4] = (frac16)-1 & ~0xffff;
}
// Set the trigger to be returned when the walk is finished
_G(globals)[GLB_TEMP_5] = kernel_trigger_create(trigger);
_G(completeWalk) = complete_walk;
ws_walkto_node(myWalker, myWalker->walkPath, true);
}
if (_G(hyperwalk))
adv_hyperwalk_to_final_destination(nullptr, nullptr);
}
bool adv_walker_path_exists(machine *myWalker, int32 x, int32 y) {
int32 currNodeID, destNodeID;
if (!myWalker || !myWalker->myAnim8) {
error_show(FL, 'W:-(');
return false;
}
// Get walker's current location
const int32 currX = myWalker->myAnim8->myRegs[IDX_X] >> 16;
const int32 currY = myWalker->myAnim8->myRegs[IDX_Y] >> 16;
// Add the walker's current location and the destination to the rail nodes...
Buffer *walkerCodes = nullptr;
if (_G(screenCodeBuff)) {
walkerCodes = _G(screenCodeBuff)->get_buffer();
}
if ((currNodeID = AddRailNode(currX, currY, walkerCodes, true)) < 0) {
error_show(FL, 'WCAN', "Walker's curr posn: %d %d", currX, currY);
}
if ((destNodeID = AddRailNode(x, y, walkerCodes, true)) < 0) {
error_show(FL, 'WCAN', "Trying to walk to: %d %d", x, y);
}
// Dispose of the current path myWalker is following
if (myWalker->walkPath) {
DisposePath(myWalker->walkPath);
}
// Find the shortest path between currNodeID, and destNodeID
const bool result = GetShortestPath(currNodeID, destNodeID, &(myWalker->walkPath));
// Now that a path has been attempted, remove the two extra added nodes
RemoveRailNode(currNodeID, walkerCodes, true);
RemoveRailNode(destNodeID, walkerCodes, true);
if (_G(screenCodeBuff))
_G(screenCodeBuff)->release();
return result;
}
void ws_custom_walk(machine *myWalker, int32 finalFacing, int32 trigger, bool complete_walk) {
const int8 directions[14] = { 0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 9 };
// Verify parameters
if ((!myWalker) || (!myWalker->walkPath)) {
return;
}
// Verify that finalFacing is valid or set -1
if (finalFacing > 0 && finalFacing < 13) {
_G(globals)[GLB_TEMP_4] = directions[finalFacing] << 16;
} else {
_G(globals)[GLB_TEMP_4] = (frac16)-1 & ~0xffff;
}
// Set the trigger to be returned when the walk is finished
_G(globals)[GLB_TEMP_5] = kernel_trigger_create(trigger);
// Begin the walk...
_G(completeWalk) = complete_walk;
ws_walkto_node(myWalker, myWalker->walkPath, true);
}
void ws_demand_facing(machine *myWalker, int32 facing) {
const int8 directions[13] = { 0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 9 };
if ((!myWalker) || (!myWalker->myAnim8)) {
term_message("demand facing, but no walker");
return;
}
if (facing > 0 && facing < 13) {
_G(globals)[GLB_TEMP_4] = directions[facing] << 16;
sendWSMessage(DEMAND_FACING << 16, 0, myWalker, 0, nullptr, 1);
}
}
void ws_demand_location(machine *myWalker, int32 x, int32 y, int facing) {
if (!myWalker || !myWalker->myAnim8) {
term_message("demand locn, no walker");
return;
}
frac16 s = _G(globals)[GLB_MIN_SCALE] + FixedMul((y << 16) - _G(globals)[GLB_MIN_Y], _G(globals)[GLB_SCALER]);
_G(globals)[GLB_TEMP_1] = x << 16;
_G(globals)[GLB_TEMP_2] = y << 16;
_G(globals)[GLB_TEMP_3] = s;
sendWSMessage(DEMAND_LOCATION << 16, 0, myWalker, 0, nullptr, 1);
if (facing != -1)
ws_demand_facing(myWalker, facing);
}
static void ws_demand_location_and_facing(machine *myWalker, int32 x, int32 y, int32 facing) {
if ((!myWalker) || (!myWalker->myAnim8)) {
term_message("demand f & l, no walker");
return;
}
frac16 s = _G(globals)[GLB_MIN_SCALE] + FixedMul((y << 16) - _G(globals)[GLB_MIN_Y], _G(globals)[GLB_SCALER]);
_G(globals)[GLB_TEMP_1] = x << 16;
_G(globals)[GLB_TEMP_2] = y << 16;
_G(globals)[GLB_TEMP_3] = s;
if (facing > 0 && facing < 13)
// WORKAROUND: The original's hyperwalk didn't work. By doing
// the facing set separately, this is fixed
ws_demand_facing(facing);
sendWSMessage(DEMAND_LOCATION << 16, 0, myWalker, 0, nullptr, 1);
_G(player).waiting_for_walk = false; // lets parse code get called when there is no facing set (from scenedit)
}
void ws_turn_to_face(machine *myWalker, int32 facing, int32 trigger) {
int8 directions[13] = { 0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 9 };
if (!myWalker || !myWalker->myAnim8) {
error_show(FL, 'W:-(', "demand facing: %d", facing);
return;
}
// Verify that facing is valid or set -1
if (facing > 0 && facing < 13) {
_G(globals)[GLB_TEMP_4] = directions[facing] << 16;
} else {
_G(globals)[GLB_TEMP_4] = (frac16)-1 & ~0xffff;
}
// Set the trigger to be returned when the walk is finished
_G(globals)[GLB_TEMP_5] = kernel_trigger_create(trigger);
// Make sure the _G(completeWalk) flag is set
_G(globals)[GLB_TEMP_6] = 0x10000;
sendWSMessage(TURN_TO_FACE << 16, 0, myWalker, 0, nullptr, 1);
}
void ws_demand_location(int32 x, int32 y, int facing) {
ws_demand_location(_G(my_walker), x, y, facing);
}
void ws_hide_walker(machine *myWalker) {
if (!myWalker) {
error_show(FL, 'W:-(');
return;
}
_G(player).walker_visible = false;
sendWSMessage(PLAYER_HIDE << 16, 0, myWalker, 0, nullptr, 1);
}
void ws_unhide_walker(machine *myWalker) {
if (!myWalker) {
error_show(FL, 'W:-(');
return;
}
_G(player).walker_visible = true;
sendWSMessage(PLAYER_UNHIDE << 16, 0, myWalker, 0, nullptr, 1);
}
void ws_demand_facing(int32 newFacing) {
ws_demand_facing(_G(my_walker), newFacing);
}
void ws_turn_to_face(int32 facing, int32 trigger) {
ws_turn_to_face(_G(my_walker), facing, trigger);
}
void ws_hide_walker() {
ws_hide_walker(_G(my_walker));
}
void ws_unhide_walker() {
ws_unhide_walker(_G(my_walker));
}
void ws_walk(int32 x, int32 y, GrBuff **buffer, int16 trigger, int32 finalFacing, bool complete_walk) {
ws_walk(_G(my_walker), x, y, buffer, trigger, finalFacing, complete_walk);
}
void ws_get_walker_info(machine *myWalker, int32 *x, int32 *y, int32 *s, int32 *layer, int32 *facing) {
const int8 facings[10] = { 1, 2, 3, 4, 5, 7, 8, 9, 10, 11 };
if (!myWalker || !myWalker->myAnim8) {
error_show(FL, 'W:-(');
return;
}
Anim8 *myAnim8 = myWalker->myAnim8;
if (x) {
*x = myAnim8->myRegs[IDX_X] >> 16;
}
if (y) {
*y = myAnim8->myRegs[IDX_Y] >> 16;
}
if (s) {
*s = MulSF16(100 << 16, myAnim8->myRegs[IDX_S]) >> 16;
}
if (layer) {
*layer = myAnim8->myRegs[IDX_LAYER] >> 16;
}
if (facing) {
int index = myAnim8->myRegs[IDX_CELS_HASH] >> 24;
// WORKAROUND: At the very least, Mei Chen in Riddle room 201
// produces a negative index value. This ensures indexes are valid
if (index < 0 || index > 9)
index = 0;
if (myAnim8->myRegs[IDX_W] < 0)
index = 9 - index;
*facing = facings[index];
}
}
bool ws_walk_init_system() {
// Initialize walker
_G(globals)[GLB_MIN_Y] = _G(currentSceneDef).back_y << 16;
_G(globals)[GLB_MAX_Y] = _G(currentSceneDef).front_y << 16;
_G(globals)[GLB_MIN_SCALE] = FixedDiv(_G(currentSceneDef).back_scale << 16, 100 << 16);
_G(globals)[GLB_MAX_SCALE] = FixedDiv(_G(currentSceneDef).front_scale << 16, 100 << 16);
if (_G(globals)[GLB_MIN_Y] == _G(globals)[GLB_MAX_Y]) {
_G(globals)[GLB_SCALER] = 0;
} else {
_G(globals)[GLB_SCALER] = FixedDiv(_G(globals)[GLB_MAX_SCALE] - _G(globals)[GLB_MIN_SCALE], _G(globals)[GLB_MAX_Y] - _G(globals)[GLB_MIN_Y]);
}
_G(my_walker) = _GW().walk_initialize_walker();
if (!_G(my_walker)) {
error_show(FL, 'W:-(');
return false;
}
return true;
}
bool ws_walk_load_series(const int16 *dir_array, const char *name_array[], bool shadow_flag, bool load_palette) {
int32 i = 0;
while (dir_array[i] >= 0) {
const int32 result = AddWSAssetCELS(name_array[i], dir_array[i],
(load_palette && !shadow_flag) ? _G(master_palette) : nullptr);
if (result < 0)
error_show(FL, 'W:-(');
i++;
}
return true;
}
bool ws_walk_load_walker_series(const int16 *dir_array, const char *name_array[], bool load_palette) {
return (ws_walk_load_series(dir_array, name_array, false, load_palette));
}
bool ws_walk_load_shadow_series(const int16 *dir_array, const char *name_array[]) {
return (ws_walk_load_series(dir_array, name_array, true, false));
}
void ws_walk_dump_series(int16 num_directions, int16 start_hash) {
for (int32 i = 0; i < num_directions; i++) {
series_unload(start_hash++);
}
}
void adv_get_walker_destination(machine *my_walker, int32 *x, int32 *y, int32 *final_facing) {
int8 directions[11] = { 1, 2, 3, 4, 5, 7, 8, 9, 10, 11 };
// If there is no walker, or the walker is not on a walk path, return
if (!my_walker || !my_walker->walkPath) {
*x = 0;
*y = 0;
*final_facing = 0;
return;
}
// Find the end of the path
railNode *current_node = my_walker->walkPath;
while (current_node->shortPath) {
current_node = current_node->shortPath;
}
// Set the destination coords
*x = current_node->x;
*y = current_node->y;
// Get final facing from l.v.6 = myRegs[6 + IDX_COUNT]
int32 face = my_walker->myAnim8->myRegs[6 + IDX_COUNT] >> 16;
// FIXME: Riddle room 608 Twelvetrees cutscene has face -1. Not sure if happens in original
if (face == -1)
face = 0;
*final_facing = directions[face];
}
void adv_hyperwalk_to_final_destination(void *, void *) {
int32 x, y;
int32 facing;
_G(i_just_hyperwalked) = true;
// Make sure we have a walker, that it can walk in this scene, and that we can hyperwalk
if ((!_G(my_walker)) || (!_G(player).walker_in_this_scene) || _G(player).disable_hyperwalk) {
return;
}
// If the walker is not currently walking anywhere, return
if (!_G(my_walker)->walkPath) {
return;
}
//get the final direction and facing
adv_get_walker_destination(_G(my_walker), &x, &y, &facing);
// Nuke the rail node path
DisposePath(_G(my_walker)->walkPath);
_G(my_walker)->walkPath = nullptr;
// This will make player goto x,y,facing. when that happens, trigger will return
ws_demand_location_and_facing(_G(my_walker), x, y, facing);
}
} // End of namespace M4

View File

@@ -0,0 +1,99 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_ADV_WALK_H
#define M4_ADV_R_ADV_WALK_H
#include "m4/m4_types.h"
#include "m4/adv_r/adv.h"
#include "m4/wscript/ws_machine.h"
namespace M4 {
class Walker {
public:
virtual ~Walker() {}
virtual bool walk_load_walker_and_shadow_series() = 0;
virtual machine *walk_initialize_walker() = 0;
};
void set_walker_scaling(SceneDef *rdef);
/**
* Called every time s/he hits a node
*/
bool walker_has_walk_finished(machine *sender);
void ws_demand_location(machine *myWalker, int32 x, int32 y, int facing = -1);
void ws_demand_facing(machine *myWalker, int32 newFacing);
void ws_turn_to_face(machine *myWalker, int32 facing, int32 trigger = -1);
void ws_hide_walker(machine *myWalker);
void ws_unhide_walker(machine *myWalker);
void ws_walk(machine *myWalker, int32 x, int32 y, GrBuff **, int16 trigger, int32 finalFacing, bool complete_walk = true);
void ws_demand_location(int32 x, int32 y, int facing = -1);
void ws_demand_facing(int32 newFacing);
void ws_turn_to_face(int32 facing, int32 trigger = -1);
void ws_hide_walker();
void ws_unhide_walker();
void ws_walk(int32 x, int32 y, GrBuff **buffer, int16 trigger, int32 finalFacing = -1, bool complete_walk = true);
void ws_get_walker_info(machine *myWalker, int32 *x, int32 *y, int32 *s, int32 *layer, int32 *facing);
bool ws_walk_init_system();
bool ws_walk_load_series(const int16 *dir_array, const char *name_array[], bool shadow_flag, bool load_palette);
bool ws_walk_load_walker_series(const int16 *dir_array, const char *name_array[], bool load_palette = false);
bool ws_walk_load_shadow_series(const int16 *dir_array, const char *name_array[]);
void ws_walk_dump_series(int16 num_directions, int16 start_hash);
#define ws_walk_dump_walker_series(xx, yy) (ws_walk_dump_series (xx, yy))
#define ws_walk_dump_shadow_series(xx, yy) (ws_walk_dump_series (xx, yy))
#define player_walk(xx, yy, ff, tt) (ws_walk(_G(my_walker), xx, yy, nullptr, tt, ff, true))
#define player_walk_no_finish(xx, yy, ff, tt) (ws_walk(_G(my_walker), xx, yy, nullptr, tt, ff, FALSE))
#define player_demand_facing(dd) (ws_demand_facing(_G(my_walker), dd))
#define player_demand_location(xx, yy) (ws_demand_location(_G(my_walker), xx, yy))
#define player_turn_to_face(dd, tt) (ws_turn_to_face(_G(my_walker), dd, tt))
#define player_hide() (ws_hide_walker(_G(my_walker)))
#define player_unhide() (ws_unhide_walker(_G(my_walker)))
#define player_get_info() (player_update_info(_G(my_walker), &_G(player_info)))
// New walking stuff
void ws_custom_walk(machine *myWalker, int32 finalFacing, int32 trigger, bool complete_walk = true);
#define adv_walker_custom_walk(ww, ff, tt) (ws_custom_walk(ww, ff, tt, true))
#define adv_walker_custom_walk_no_finish(ww, ff, tt) (ws_custom_walk(ww, ff, tt, FALSE))
#define adv_walker_walk(ww, xx, yy, ff, tt) (ws_walk(ww, xx, yy, nullptr, tt, ff, true))
#define adv_walker_walk_no_finish(ww, xx, yy, ff, tt) (ws_walk(ww, xx, yy, nullptr, tt, ff, FALSE))
#define adv_walker_face(ww, dd) (ws_demand_facing(ww, dd))
#define adv_walker_turn_to_face(ww, dd, tt) (ws_turn_to_face(ww, dd, tt))
#define adv_walker_move(ww, xx, yy) (ws_demand_location(ww, xx, yy))
#define adv_walker_hide(ww) (ws_hide_walker(ww))
#define adv_walker_unhide(ww) (ws_unhide_walker(ww))
bool adv_walker_path_exists(machine *myWalker, int32 x, int32 y);
void adv_hyperwalk_to_final_destination(void *, void *);
} // End of namespace M4
#endif

View File

@@ -0,0 +1,629 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/textconsole.h"
#include "m4/adv_r/chunk_ops.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/vars.h"
namespace M4 {
int32 conv_ops_text_strlen(char *s) {
int32 len = strlen(s) + 1; // Added +1 for null char.
if ((len % 4) == 0)
return len;
len += 4 - (len % 4);
return len;
}
void conv_ops_unknown_chunk(int32 tag, const char *s) {
char *tag_name = (char *)&tag;
error_show(FL, 'PARS', "'%s' What type is this chunk: %c%c%c%c ?", s, tag_name[3], tag_name[2], tag_name[1], tag_name[0]);
}
/*
* Find an entry and auto-advance the pointer past it.
*/
char *conv_ops_get_entry(int32 i, int32 *next, int32 *tag, Conv *c) {
int32 num_blocks;
int32 j = 0, k = 0;
lnode_chunk *L;
node_chunk *N;
text_chunk *T;
w_reply_chunk *W;
conv_chunk *CC;
if ((i + c->myCNode) > c->chunkSize) {
error_show(FL, 'PARS', "Conv pointer skipped past chunk EOF. Please check script and make sure HAG is up to date");
}
char *outChunk = &(c->conv[c->myCNode]);
*tag = *(int32 *)&outChunk[i];
if (_GC(swap))
*tag = convert_intel32(*tag);
switch (*tag) {
case C_ASGN_CHUNK:
k = sizeof(c_assign_chunk);
break;
case ASGN_CHUNK:
k = sizeof(assign_chunk);
break;
case CONV_CHUNK:
CC = (conv_chunk *)&outChunk[i];
j = CC->size;
break;
case DECL_CHUNK:
k = sizeof(decl_chunk);
break;
case FALL_CHUNK:
k = sizeof(fall_chunk);
break;
case LNODE_CHUNK:
L = (lnode_chunk *)&outChunk[i];
k = sizeof(lnode_chunk);
if (_GC(swap)) {
j = convert_intel32(L->num_entries) * sizeof(int32);
j = convert_intel32(j);
} else {
j = L->num_entries * sizeof(int32);
}
break;
case NODE_CHUNK:
N = (node_chunk *)&outChunk[i];
k = sizeof(node_chunk);
if (_GC(swap)) {
j = convert_intel32(N->num_entries) * sizeof(int32);
j = convert_intel32(j);
} else {
j = N->num_entries * sizeof(int32); //was +=
}
break;
case ENTRY_CHUNK:
k = sizeof(entry_chunk);
break;
case TEXT_CHUNK:
case MESSAGE_CHUNK:
T = (text_chunk *)&outChunk[i];
num_blocks = T->size;
k = sizeof(text_chunk);
j = num_blocks;
break;
case REPLY_CHUNK:
k = sizeof(reply_chunk);
break;
case WEIGHT_REPLY_CHUNK:
case WEIGHT_PREPLY_CHUNK:
W = (w_reply_chunk *)&outChunk[i];
k = sizeof(w_reply_chunk);
if (_GC(swap)) {
j = convert_intel32(W->num_replies) * (2 * sizeof(int32)); //was +=
j = convert_intel32(j);
} else {
j = W->num_replies * (2 * sizeof(int32)); //was +=
}
break;
case COND_REPLY_CHUNK:
k = sizeof(c_reply_chunk);
break;
case COND_EXIT_GOTO_CHUNK:
case COND_GOTO_CHUNK:
k = sizeof(c_goto_chunk);
break;
case GOTO_CHUNK:
case EXIT_GOTO_CHUNK:
k = sizeof(goto_chunk);
break;
case HIDE_CHUNK:
case UHID_CHUNK:
case DSTR_CHUNK:
k = sizeof(misc_chunk);
break;
case CHDE_CHUNK:
case CUHD_CHUNK:
case CDST_CHUNK:
k = sizeof(c_misc_chunk);
break;
default:
error_show(FL, 'PARS', "Tag: %d (%x) Node: %d (%x hex)", *tag, *tag, c->myCNode, c->myCNode);
break;
}
if (_GC(swap))
j = convert_intel32(j);
j += k;
j += i;
*next = j;
return &outChunk[i];
}
static void swap_assign(assign_chunk *a) {
a->tag = convert_intel32(a->tag);
a->index = convert_intel32(a->index);
a->op = convert_intel32(a->op);
a->opnd1 = convert_intel32(a->opnd1);
}
assign_chunk *get_asgn(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (assign_chunk *)&s[cSize];
}
static void swap_c_asgn(c_assign_chunk *c) {
c->tag = convert_intel32(c->tag);
c->c_op_l = convert_intel32(c->c_op_l);
c->c_op = convert_intel32(c->c_op);
c->c_op_r = convert_intel32(c->c_op_r);
c->index = convert_intel32(c->index);
c->op = convert_intel32(c->op);
c->opnd1 = convert_intel32(c->opnd1);
}
c_assign_chunk *get_c_asgn(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (c_assign_chunk *)&s[cSize];
}
static void swap_conv(conv_chunk *c) {
c->tag = convert_intel32(c->tag);
c->size = convert_intel32(c->size);
}
conv_chunk *get_conv(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (conv_chunk *)&s[cSize];
}
static void swap_decl(decl_chunk *d) {
d->tag = convert_intel32(d->tag);
d->val = convert_intel32(d->val);
d->flags = convert_intel32(d->flags);
}
decl_chunk *get_decl(Conv *c, int32 cSize) {
return (decl_chunk *)&c->conv[cSize];
}
static void swap_fall(fall_chunk *l) {
l->tag = convert_intel32(l->tag);
l->val = convert_intel32(l->val);
l->index = convert_intel32(l->index);
}
fall_chunk *get_fall(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (fall_chunk *)&s[cSize];
}
static void swap_lnode(lnode_chunk *l) {
l->tag = convert_intel32(l->tag);
l->hash = convert_intel32(l->hash);
l->size = convert_intel32(l->size);
l->entry_num = convert_intel32(l->entry_num);
l->num_entries = convert_intel32(l->num_entries);
int32 *L = (int32 *)l;
L += 5;
for (int i = 0; i < l->num_entries; i++) {
L[i] = convert_intel32(L[i]);
}
}
lnode_chunk *get_lnode(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (lnode_chunk *)&s[cSize];
}
static void swap_node(node_chunk *n) {
n->tag = convert_intel32(n->tag);
n->hash = convert_intel32(n->hash);
n->size = convert_intel32(n->size);
n->num_entries = convert_intel32(n->num_entries);
int32 *L = (int32 *)(n + 1);
for (int i = 0; i < n->num_entries; i++) {
L[i] = convert_intel32(L[i]);
}
}
node_chunk *get_node(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (node_chunk *)&s[cSize];
}
static void swap_entry(entry_chunk *e) {
e->tag = convert_intel32(e->tag);
e->size = convert_intel32(e->size);
e->status = convert_intel32(e->status);
}
entry_chunk *get_entry(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (entry_chunk *)&s[cSize];
}
entry_chunk *get_hash_entry(Conv *c, int32 cSize) {
char *s = &(c->conv[0]);
return (entry_chunk *)&s[cSize];
}
static void swap_text(text_chunk *t) {
t->tag = convert_intel32(t->tag);
t->size = convert_intel32(t->size);
}
text_chunk *get_text(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (text_chunk *)&s[cSize];
}
static void swap_mesg(mesg_chunk *m) {
m->tag = convert_intel32(m->tag);
m->size = convert_intel32(m->size);
}
mesg_chunk *get_mesg(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (mesg_chunk *)&s[cSize];
}
static void swap_reply(reply_chunk *r) {
r->tag = convert_intel32(r->tag);
r->index = convert_intel32(r->index);
}
reply_chunk *get_reply(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (reply_chunk *)&s[cSize];
}
static void swap_c_reply(c_reply_chunk *c) {
c->tag = convert_intel32(c->tag);
c->op_l = convert_intel32(c->op_l);
c->op = convert_intel32(c->op);
c->op_r = convert_intel32(c->op_r);
c->index = convert_intel32(c->index);
}
c_reply_chunk *get_c_reply(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (c_reply_chunk *)&s[cSize];
}
static void swap_w_reply(w_reply_chunk *c) {
c->tag = convert_intel32(c->tag);
c->num_replies = convert_intel32(c->num_replies);
}
w_reply_chunk *get_w_reply(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (w_reply_chunk *)&s[cSize];
}
static void swap_w_entry(w_entry_chunk *w) {
w->weight = convert_intel32(w->weight);
w->index = convert_intel32(w->index);
}
w_entry_chunk *get_w_entry(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (w_entry_chunk *)&s[cSize];
}
static void swap_goto(goto_chunk *g) {
g->tag = convert_intel32(g->tag);
g->index = convert_intel32(g->index);
}
goto_chunk *get_goto(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (goto_chunk *)&s[cSize];
}
static void swap_c_goto(c_goto_chunk *c) {
c->tag = convert_intel32(c->tag);
c->opnd1 = convert_intel32(c->opnd1);
c->op = convert_intel32(c->op);
c->opnd2 = convert_intel32(c->opnd2);
c->index = convert_intel32(c->index);
}
c_goto_chunk *get_c_goto(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (c_goto_chunk *)&s[cSize];
}
static void swap_misc(misc_chunk *m) {
m->tag = convert_intel32(m->tag);
m->index = convert_intel32(m->index);
}
misc_chunk *get_misc(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (misc_chunk *)&s[cSize];
}
static void swap_c_misc(c_misc_chunk *c) {
c->tag = convert_intel32(c->tag);
c->c_op_l = convert_intel32(c->c_op_l);
c->c_op = convert_intel32(c->c_op);
c->c_op_r = convert_intel32(c->c_op_r);
c->index = convert_intel32(c->index);
}
c_misc_chunk *get_c_misc(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
return (c_misc_chunk *)&s[cSize];
}
int32 get_long(Conv *c, int32 cSize) {
char *s = &(c->conv[c->myCNode]);
int32 *l = (int32 *)&s[cSize];
return *l;
}
char *get_string(Conv *c, int32 cSize) {
return &c->conv[cSize];
}
int conv_ops_cond_successful(int32 l_op, int32 op, int32 r_op) {
switch (op) {
case PERCENT:
return l_op % r_op;
case GE:
return l_op >= r_op;
case LE:
return l_op <= r_op;
case GT:
return l_op > r_op;
case LT:
return l_op < r_op;
case NE:
case CNE:
return !(l_op == r_op);
case IS_ASSIGNED:
return l_op == r_op;
case ANDAND:
return l_op && r_op;
case OROR:
return l_op || r_op;
}
return 0;
}
int32 conv_ops_process_asgn(int32 val, int32 oprtr, int32 opnd) {
switch (oprtr) {
case PPLUS:
val += opnd;
break;
case MINUS:
val -= opnd;
break;
case TIMES:
val *= opnd;
break;
case DIVIDE:
val /= opnd;
break;
case IS_ASSIGNED:
val = opnd;
break;
default:
error_show(FL, 'PARS', "Operator must be a +,-,*,/,=, Please check tag type: %d in token header file", oprtr);
break;
}
return val;
}
void conv_swap_words(Conv *c) {
int32 ent = 0, tag = 0, next;
if (!c)
return;
c->myCNode = 0;
const int32 ent_old = c->myCNode;
c->myCNode = 0;
_GC(swap) = true;
while (ent < c->chunkSize) {
conv_ops_get_entry(ent, &next, &tag, c);
switch (tag) {
case C_ASGN_CHUNK: {
c_assign_chunk *c_asgn = get_c_asgn(c, ent);
swap_c_asgn(c_asgn);
}
break;
case ASGN_CHUNK: {
assign_chunk *asgn = get_asgn(c, ent);
swap_assign(asgn);
}
break;
case HIDE_CHUNK:
case DSTR_CHUNK:
case UHID_CHUNK: {
misc_chunk *misc = get_misc(c, ent);
swap_misc(misc);
}
break;
case CHDE_CHUNK:
case CUHD_CHUNK:
case CDST_CHUNK: {
c_misc_chunk *c_misc = get_c_misc(c, ent);
swap_c_misc(c_misc);
}
break;
case CONV_CHUNK: {
conv_chunk *conv = get_conv(c, ent);
swap_conv(conv);
}
break;
case DECL_CHUNK: {
decl_chunk *decl = get_decl(c, ent);
swap_decl(decl);
}
break;
case FALL_CHUNK: {
fall_chunk *fall = get_fall(c, ent);
swap_fall(fall);
}
break;
case LNODE_CHUNK: {
lnode_chunk *lnode = get_lnode(c, ent);
swap_lnode(lnode);
}
break;
case NODE_CHUNK: {
node_chunk *node = get_node(c, ent);
swap_node(node);
}
break;
case ENTRY_CHUNK: {
entry_chunk *entry = get_entry(c, ent);
swap_entry(entry);
}
break;
case TEXT_CHUNK: {
text_chunk *text = get_text(c, ent);
swap_text(text);
}
break;
case REPLY_CHUNK: {
reply_chunk *reply = get_reply(c, ent);
swap_reply(reply);
}
break;
case WEIGHT_REPLY_CHUNK:
case WEIGHT_PREPLY_CHUNK: {
w_reply_chunk *w_reply = get_w_reply(c, ent);
swap_w_reply(w_reply);
int32 tempEnt = ent + sizeof(w_reply_chunk);
for (int x = 0; x < w_reply->num_replies; x++) {
w_entry_chunk *w_entry = get_w_entry(c, tempEnt);
swap_w_entry(w_entry);
tempEnt += sizeof(w_entry_chunk);
}
}
break;
case COND_REPLY_CHUNK: {
c_reply_chunk *c_reply = get_c_reply(c, ent);
swap_c_reply(c_reply);
}
break;
case MESSAGE_CHUNK: {
mesg_chunk *mesg = get_mesg(c, ent);
swap_mesg(mesg);
}
break;
case GOTO_CHUNK:
case EXIT_GOTO_CHUNK: {
goto_chunk *go = get_goto(c, ent);
swap_goto(go);
}
break;
case COND_GOTO_CHUNK:
case COND_EXIT_GOTO_CHUNK: {
c_goto_chunk *c_goto = get_c_goto(c, ent);
swap_c_goto(c_goto);
}
break;
default:
break;
}
ent = next;
}
_GC(swap) = false;
c->myCNode = ent_old;
}
} // End of namespace M4

View File

@@ -0,0 +1,94 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_CHUNK_OPS_H
#define M4_ADV_R_CHUNK_OPS_H
#include "m4/m4_types.h"
#include "m4/adv_r/conv.h"
namespace M4 {
#define PPLUS 402
#define MINUS 403
#define TIMES 404
#define PERCENT 405
#define POWER 406
#define IS_ASSIGNED 407
#define PERIOD 410
#define DOLLAR 411
#define LT 412
#define GT 413
#define L_BRAK 416
#define R_BRAK 417
#define DIVIDE 418
#define NOT 419
#define LE 420
#define GE 421
#define NE 422
#define PE 423
#define ME 424
#define TE 425
#define DE 426
#define AE 427
#define OE 428
#define EQ 429
#define AND 430
#define OR 431
#define TILDA 432
#define DBL_QUOTE 433
#define ANDAND 444
#define OROR 445
#define DOT 446
#define CNE 448
conv_chunk *get_conv(Conv *c, int32 cSize);
entry_chunk *get_entry(Conv *c, int32 cSize);
char *conv_ops_get_entry(int32 i, int32 *next, int32 *tag, Conv *c);
void conv_ops_unknown_chunk(int32 tag, const char *s);
decl_chunk *get_decl(Conv *c, int32 cSize);
char *get_string(Conv *c, int32 cSize);
text_chunk *get_text(Conv *c, int32 cSize);
int32 conv_ops_text_strlen(char *s);
c_assign_chunk *get_c_asgn(Conv *c, int32 cSize);
int conv_ops_cond_successful(int32 l_op, int32 op, int32 r_op);
int32 conv_ops_process_asgn(int32 val, int32 oprtr, int32 opnd);
assign_chunk *get_asgn(Conv *c, int32 cSize);
misc_chunk *get_misc(Conv *c, int32 cSize);
entry_chunk *get_hash_entry(Conv *c, int32 cSize);
c_misc_chunk *get_c_misc(Conv *c, int32 cSize);
c_goto_chunk *get_c_goto(Conv *c, int32 cSize);
goto_chunk *get_goto(Conv *c, int32 cSize);
reply_chunk *get_reply(Conv *c, int32 cSize);
lnode_chunk *get_lnode(Conv *c, int32 cSize);
node_chunk *get_node(Conv *c, int32 cSize);
fall_chunk *get_fall(Conv *c, int32 cSize);
int32 get_long(Conv *c, int32 cSize);
c_reply_chunk *get_c_reply(Conv *c, int32 cSize);
w_reply_chunk *get_w_reply(Conv *c, int32 cSize);
w_entry_chunk *get_w_entry(Conv *c, int32 cSize);
} // End of namespace M4
#endif

956
engines/m4/adv_r/conv.cpp Normal file
View File

@@ -0,0 +1,956 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/conv.h"
#include "m4/adv_r/adv_control.h"
#include "m4/adv_r/chunk_ops.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/gui/gui_univ.h"
#include "m4/gui/gui_vmng.h"
#include "m4/vars.h"
#include "m4/m4.h"
namespace M4 {
// ---------------
// Set entry stats,
// Get next node pointer
// Process declarations
// Get message text
static void conv_exec_entry(int32 offset, Conv *c) {
int32 i = offset;
int32 tag, next;
goto_chunk *go;
c_goto_chunk *c_goto;
decl_chunk *decl;
assign_chunk *asgn;
c_assign_chunk *c_asgn;
misc_chunk *misc;
c_misc_chunk *c_misc;
int32 l_op;
int32 r_op;
entry_chunk *entry = get_entry(c, i);
const int32 entry_count = entry->size;
entry->status = conv_toggle_flags(entry);
i += sizeof(entry_chunk);
while (i < offset + entry_count) {
conv_ops_get_entry(i, &next, &tag, c);
switch (tag) {
case TEXT_CHUNK:
case MESSAGE_CHUNK:
case ENTRY_CHUNK:
case FALL_CHUNK:
break;
case C_ASGN_CHUNK:
c_asgn = get_c_asgn(c, i);
decl = get_decl(c, c_asgn->c_op_l);
l_op = conv_get_decl_val(c, decl);
if (conv_ops_cond_successful(l_op, c_asgn->c_op, c_asgn->c_op_r)) {
decl = get_decl(c, c_asgn->index);
conv_set_decl_val(c, decl, conv_ops_process_asgn(conv_get_decl_val(c, decl), c_asgn->op, c_asgn->opnd1));
}
break;
case ASGN_CHUNK:
asgn = get_asgn(c, i);
decl = get_decl(c, asgn->index);
conv_set_decl_val(c, decl, conv_ops_process_asgn(conv_get_decl_val(c, decl), asgn->op, asgn->opnd1));
break;
case HIDE_CHUNK:
misc = get_misc(c, i);
entry = get_hash_entry(c, misc->index);
if (!(entry->status & DESTROYED))
entry->status |= HIDDEN;
break;
case CHDE_CHUNK:
c_misc = get_c_misc(c, i);
entry = get_hash_entry(c, c_misc->index);
decl = get_decl(c, c_misc->c_op_l);
l_op = conv_get_decl_val(c, decl);
if (conv_ops_cond_successful(l_op, c_misc->c_op, c_misc->c_op_r)) {
if (!(entry->status & DESTROYED))
entry->status |= HIDDEN;
}
break;
case UHID_CHUNK:
misc = get_misc(c, i);
entry = get_hash_entry(c, misc->index);
if (!(entry->status & DESTROYED)) {
entry->status &= 0xfffffffb; // Mask HIDDEN bit
entry->status |= 0x00000001;
}
break;
case CUHD_CHUNK:
c_misc = get_c_misc(c, i);
entry = get_hash_entry(c, c_misc->index);
decl = get_decl(c, c_misc->c_op_l);
l_op = conv_get_decl_val(c, decl);
if (conv_ops_cond_successful(l_op, c_misc->c_op, c_misc->c_op_r)) {
if (!(entry->status & DESTROYED)) {
entry->status &= 0xfffffffb;
entry->status |= 0x00000001;
}
}
break;
case DSTR_CHUNK:
misc = get_misc(c, i);
entry = get_hash_entry(c, misc->index);
entry->status |= DESTROYED;
break;
case CDST_CHUNK:
c_misc = get_c_misc(c, i);
entry = get_hash_entry(c, c_misc->index);
decl = get_decl(c, c_misc->c_op_l);
l_op = conv_get_decl_val(c, decl);
if (conv_ops_cond_successful(l_op, c_misc->c_op, c_misc->c_op_r))
entry->status |= DESTROYED;
break;
case COND_GOTO_CHUNK:
c_goto = get_c_goto(c, i);
decl = get_decl(c, c_goto->opnd1);
l_op = conv_get_decl_val(c, decl);
r_op = c_goto->opnd2; //val
if (conv_ops_cond_successful(l_op, c_goto->op, r_op)) {
c->myCNode = c_goto->index;
return;
}
break;
case COND_EXIT_GOTO_CHUNK:
c_goto = get_c_goto(c, i);
decl = get_decl(c, c_goto->opnd1);
l_op = conv_get_decl_val(c, decl);
r_op = c_goto->opnd2; //val
if (conv_ops_cond_successful(l_op, c_goto->op, r_op)) {
if (c_goto->index != CONV_QUIT) { //was go->index
c->myCNode = c_goto->index; //was go->index
c->exit_now = CONV_BAIL;
} else {
c->exit_now = CONV_QUIT;
c->myCNode = CONV_QUIT;
}
return;
}
break;
case EXIT_GOTO_CHUNK:
go = get_goto(c, i);
if (go->index != CONV_QUIT) {
c->myCNode = go->index;
c->exit_now = CONV_BAIL;
} else {
c->exit_now = CONV_QUIT;
c->myCNode = CONV_QUIT;
}
return;
case GOTO_CHUNK:
go = get_goto(c, i);
c->myCNode = go->index;
return;
// Replies are non-player responses
case REPLY_CHUNK:
case COND_REPLY_CHUNK:
case WEIGHT_REPLY_CHUNK:
case WEIGHT_PREPLY_CHUNK:
break;
default:
conv_ops_unknown_chunk(tag, "conv_exec_entry");
break;
}
i = next;
}
}
static int conv_get_mesg(int32 offset, int32 is_valid, Conv *c) {
int32 i = offset;
int32 x, y, s_offset = 0, cSize;
int32 tag, next;
int32 text_len;
int sum, result = 0;
decl_chunk *decl;
reply_chunk *reply;
c_reply_chunk *c_reply;
w_reply_chunk *w_reply;
w_entry_chunk *w_entry;
int32 l_op;
int32 r_op;
entry_chunk *entry = get_entry(c, i);
const int32 entry_count = entry->size;
i += sizeof(entry_chunk);
while (i < offset + entry_count) {
conv_ops_get_entry(i, &next, &tag, c);
switch (tag) {
case TEXT_CHUNK:
case MESSAGE_CHUNK:
case ENTRY_CHUNK:
case FALL_CHUNK:
case C_ASGN_CHUNK:
case ASGN_CHUNK:
case HIDE_CHUNK:
case CHDE_CHUNK:
case UHID_CHUNK:
case CUHD_CHUNK:
case DSTR_CHUNK:
case CDST_CHUNK:
case COND_GOTO_CHUNK:
case COND_EXIT_GOTO_CHUNK:
case EXIT_GOTO_CHUNK:
case GOTO_CHUNK:
break;
case REPLY_CHUNK:
reply = get_reply(c, i);
if (is_valid) {
result = 1;
_G(cdd).player_non_player = 0;
if (!strcmp(_G(cdd).mesg, "")) {
text_len = conv_ops_text_strlen(get_string(c, reply->index + sizeof(mesg_chunk)));
Common::strcpy_s(_G(cdd).mesg, get_string(c, reply->index + sizeof(mesg_chunk) + text_len));
_G(cdd).mesg_snd_file = get_string(c, reply->index + sizeof(mesg_chunk));
} else {
Common::strcat_s(_G(cdd).mesg, " ");
text_len = conv_ops_text_strlen(get_string(c, reply->index + sizeof(mesg_chunk)));
Common::strcat_s(_G(cdd).mesg, get_string(c, reply->index + sizeof(mesg_chunk) + text_len));
_G(cdd).mesg_snd_file = get_string(c, reply->index + sizeof(mesg_chunk));
}
}
break;
case COND_REPLY_CHUNK:
c_reply = get_c_reply(c, i);
decl = get_decl(c, c_reply->op_l);
l_op = conv_get_decl_val(c, decl);
r_op = c_reply->op_r; //val
if (is_valid && conv_ops_cond_successful(l_op, c_reply->op, r_op)) {
result = 1;
_G(cdd).player_non_player = 0;
if (!strcmp(_G(cdd).mesg, "")) {
text_len = conv_ops_text_strlen(get_string(c, c_reply->index + sizeof(mesg_chunk)));
Common::strcpy_s(_G(cdd).mesg, get_string(c, c_reply->index + sizeof(mesg_chunk) + text_len));
_G(cdd).mesg_snd_file = get_string(c, c_reply->index + sizeof(mesg_chunk));
} else {
Common::strcat_s(_G(cdd).mesg, " ");
text_len = conv_ops_text_strlen(get_string(c, c_reply->index + sizeof(mesg_chunk)));
Common::strcat_s(_G(cdd).mesg, get_string(c, c_reply->index + sizeof(mesg_chunk) + text_len));
_G(cdd).mesg_snd_file = get_string(c, c_reply->index + sizeof(mesg_chunk));
}
}
break;
case WEIGHT_REPLY_CHUNK:
cSize = i;
w_reply = get_w_reply(c, i);
cSize += sizeof(w_reply_chunk);
sum = 0;
for (x = 0; x < w_reply->num_replies; x++) {
w_entry = get_w_entry(c, cSize);
sum += w_entry->weight;
cSize += sizeof(w_entry_chunk);
}
y = g_engine->getRandomNumber(sum - 1) + 1;
cSize = i;
w_reply = get_w_reply(c, i);
cSize += sizeof(w_reply_chunk);
sum = 0;
for (x = 0; (x < w_reply->num_replies) && (sum < y); x++) {
w_entry = get_w_entry(c, cSize);
s_offset = w_entry->index;
sum += w_entry->weight;
cSize += sizeof(w_entry_chunk);
}
if (is_valid) {
result = 1;
_G(cdd).player_non_player = 0;
if (!strcmp(_G(cdd).mesg, "")) {
text_len = conv_ops_text_strlen(get_string(c, s_offset + sizeof(mesg_chunk)));
Common::strcpy_s(_G(cdd).mesg, get_string(c, s_offset + sizeof(mesg_chunk) + text_len));
_G(cdd).mesg_snd_file = get_string(c, s_offset + sizeof(mesg_chunk));
} else {
Common::strcat_s(_G(cdd).mesg, " ");
text_len = conv_ops_text_strlen(get_string(c, s_offset + sizeof(mesg_chunk)));
Common::strcat_s(_G(cdd).mesg, get_string(c, s_offset + sizeof(mesg_chunk) + text_len));
_G(cdd).mesg_snd_file = get_string(c, s_offset + sizeof(mesg_chunk));
}
}
break;
case WEIGHT_PREPLY_CHUNK:
cSize = i;
w_reply = get_w_reply(c, i);
cSize += sizeof(w_reply_chunk);
sum = 0;
for (x = 0; x < w_reply->num_replies; x++) {
w_entry = get_w_entry(c, cSize);
sum += w_entry->weight;
cSize += sizeof(w_entry_chunk);
}
y = g_engine->getRandomNumber(sum - 1) + 1;
cSize = i;
w_reply = get_w_reply(c, i);
cSize += sizeof(w_reply_chunk);
sum = 0;
for (x = 0; (x < w_reply->num_replies) && (sum < y); x++) {
w_entry = get_w_entry(c, cSize);
s_offset = w_entry->index;
sum += w_entry->weight;
cSize += sizeof(w_entry_chunk);
}
if (is_valid) {
result = 1;
_G(cdd).player_non_player = 1;
if (!strcmp(_G(cdd).mesg, "")) {
text_len = conv_ops_text_strlen(get_string(c, s_offset + sizeof(mesg_chunk)));
Common::strcpy_s(_G(cdd).mesg, get_string(c, s_offset + sizeof(mesg_chunk) + text_len));
_G(cdd).mesg_snd_file = get_string(c, s_offset + sizeof(mesg_chunk));
} else {
Common::strcat_s(_G(cdd).mesg, " ");
text_len = conv_ops_text_strlen(get_string(c, s_offset + sizeof(mesg_chunk)));
Common::strcat_s(_G(cdd).mesg, get_string(c, s_offset + sizeof(mesg_chunk) + text_len));
_G(cdd).mesg_snd_file = get_string(c, s_offset + sizeof(mesg_chunk));
}
}
break;
default:
conv_ops_unknown_chunk(tag, "conv_get_mesg");
break;
}
i = next;
}
return result;
}
static void find_true_ent(int entry_num, Conv *c) {
int32 ent = 0, n = 0;
int32 next = 0, tag = 0, num_ents = 0;
int i;
// Start by getting the current NODE or LNODE
node_chunk *node;
lnode_chunk *lnode;
conv_ops_get_entry(ent, &next, &tag, c);
switch (tag) {
case LNODE_CHUNK:
lnode = get_lnode(c, ent);
ent += sizeof(lnode_chunk);
num_ents = lnode->num_entries;
entry_num = lnode->entry_num;
c->node_hash = lnode->hash;
break;
case NODE_CHUNK:
node = get_node(c, ent);
ent += sizeof(node_chunk);
num_ents = node->num_entries;
c->node_hash = node->hash;
break;
default:
break;
}
// ent will now be pointing at an ENTRY or FALLTHROUGH
const int32 sub_ent = next;
conv_ops_get_entry(sub_ent, &next, &tag, c);
if (tag == FALL_CHUNK) {
// We either want to jump to a new node
// or skip to the first offset.
fall_chunk *fall = get_fall(c, sub_ent);
assert(fall);
//do this to skip the fall chunk and all will be fine.
ent += sizeof(int32); //was get_long, sizeof( fall_chunk )
}
_GC(ent) = 0;
// Not only i<entry_num, check to see entry->num_entries
for (i = 0, n = 0; n < num_ents; n++) {
const int32 offset = get_long(c, ent);
entry_chunk *entry = get_entry(c, ent + offset);
if (i == entry_num)
break;
if ((entry->status & 0x00000003) && ok_status(entry)) //was 1
i++;
_GC(ent)++;
ent += sizeof(int32);
}
}
// Simplify me now that all changes have been made.
static int conv_get_node_text(Conv *c) {
lnode_chunk *lnode = nullptr;
node_chunk *node;
entry_chunk *entry = nullptr;
fall_chunk *fall = nullptr;
int32 ent = 0, offset = 0, tag, next, num_ents = 0;
int i = 0, num_vis = 0, result = 0;
_G(cdd).num_txt_ents = 0;
_GC(width) = 0; _GC(height) = 0;
// conv _get_node_text will either get a NODE or LNODE
conv_ops_get_entry(offset, &next, &tag, c);
offset = 0; //not needed.?
_GC(n_t_e) = 0;
switch (tag) {
case LNODE_CHUNK:
lnode = get_lnode(c, offset);
offset += sizeof(lnode_chunk);
num_ents = lnode->num_entries;
c->node_hash = lnode->hash;
break;
case NODE_CHUNK:
node = get_node(c, offset);
offset += sizeof(node_chunk);
num_ents = node->num_entries;
c->node_hash = node->hash;
break;
default:
// handle error.
break;
}
switch (tag) {
case LNODE_CHUNK:
// was in bounds.
// lnode->entry_num starts at 0. in the chunk.
if (lnode->entry_num >= lnode->num_entries)
lnode->entry_num = 0;
for (i = 0; (i <= lnode->entry_num) && (i < num_ents); i++) {
ent = get_long(c, offset);
entry = get_entry(c, offset + ent);
offset += sizeof(int32);
}
offset -= sizeof(int32);
// Set sound file name instead.?
if ((entry->status != 0) && (num_ents != 0) && ok_status(entry)) {
//here here here.
if (conv_get_text(offset + ent, entry->size, c)) {
result = 1;
_G(cdd).num_txt_ents = 0;
_G(cdd).mesg_snd_file = _G(cdd).snd_files[0];
_G(cdd).text[0] = nullptr;
_G(cdd).snd_files[0] = nullptr;
_G(cdd).player_non_player = 1;
c->c_entry_num = lnode->entry_num;
}
num_vis++;
}
lnode->entry_num++;
_GC(ent) = lnode->entry_num;
break;
case NODE_CHUNK:
for (i = 0; i < num_ents; i++) {
ent = get_long(c, offset);
entry = get_entry(c, offset + ent);
if (entry->tag != FALL_CHUNK) {
if ((entry->status != 0) && ok_status(entry)) {
if (conv_get_text(offset + ent, entry->size, c)) {
result = 1;
_G(cdd).player_non_player = 1;
}
num_vis++;
_GC(n_t_e)++;
}
} else {
fall = get_fall(c, offset + ent);
}
offset += sizeof(int32);
}
if (fall) {
if (num_vis <= fall->val) {
_GC(n_t_e) = 0;
c->myCNode = fall->index;
c->mode = CONV_GET_TEXT_MODE;
result = 0;
}
}
break;
default:
break;
}
return result;
}
void conv_shutdown() {
if (conv_get_handle())
conv_unload(conv_get_handle());
if (_GC(myTextScrn))
TextScrn_Destroy(_GC(myTextScrn));
_GC(myTextScrn) = nullptr;
}
static void conv_start(Conv *c) {
int32 ok = 1, ent = 0, tag = 0, next;
decl_chunk *decl;
switch (c->exit_now) {
case CONV_OK:
// Potential prob. when entering while loop.
break;
// Goto_exit encountered.
// a conversation state.
case CONV_BAIL:
return;
// Goodbye forever.
case CONV_QUIT:
return;
//new conv. no restore file on hard disk.
case CONV_NEW:
c->exit_now = CONV_OK;
c->myCNode = 0;
break;
default:
break;
}
while ((ent < c->chunkSize) && ok) {
conv_ops_get_entry(ent, &next, &tag, c); //ok if c->myCNode = 0
switch (tag) {
case LNODE_CHUNK:
case NODE_CHUNK:
ok = 0;
break;
case DECL_CHUNK:
decl = get_decl(c, ent);
assert(decl);
break;
default:
break;
}
if (ok)
ent = next;
}
c->myCNode = ent;
// if we exit, the current node is set, the next node is null
}
static int conv_next_node(Conv *c) {
if (c->myCNode == -1)
return 0;
switch (c->exit_now) {
case CONV_OK:
return 1;
case CONV_QUIT:
case CONV_BAIL:
return 0;
case CONV_NEW:
conv_start(c); // Should go in conv_load.
return 1;
default:
break;
}
return 1;
}
static int conv_process_entry(int entry_num, Conv *c, int mode) {
node_chunk *node;
lnode_chunk *lnode;
int32 offset = 0, ent = 0, is_valid = 0, n = 0;
int32 next = 0, tag = 0, num_ents = 0;
int i = 0;
int result = 1;
// Start by getting the current NODE or LNODE
conv_ops_get_entry(ent, &next, &tag, c);
switch (tag) {
case LNODE_CHUNK:
lnode = get_lnode(c, ent);
ent += sizeof(lnode_chunk);
num_ents = lnode->num_entries;
entry_num = lnode->entry_num;
c->node_hash = lnode->hash;
break;
case NODE_CHUNK:
node = get_node(c, ent);
ent += sizeof(node_chunk);
num_ents = node->num_entries;
c->node_hash = node->hash;
break;
default:
break;
}
// ent will now be pointing at an ENTRY or FALLTHROUGH
const int32 sub_ent = next;
conv_ops_get_entry(sub_ent, &next, &tag, c);
if (tag == FALL_CHUNK) {
// We either want to jump to a new node
// or skip to the first offset.
fall_chunk *fall = get_fall(c, sub_ent);
assert(fall);
// Do this to skip the fall chunk and all will be fine.
ent += sizeof(int32); //was get_long, sizeof( fall_chunk )
n++; //don't increment i.
}
// Not only i<entry_num, check to see entry->num_entries
while ((i < entry_num) && (n < num_ents)) {
offset = get_long(c, ent);
entry_chunk *entry = get_entry(c, ent + offset);
if ((entry->status != 0) && ok_status(entry)) {
i++;
is_valid = 1;
}
ent += sizeof(int32);
n++;
}
ent -= sizeof(int32);
if (is_valid) {
switch (mode) {
case CONV_GET_MESG_MODE:
result = conv_get_mesg(ent + offset, is_valid, c);
break;
case CONV_UPDATE_MODE:
conv_exec_entry(ent + offset, c);
break;
default:
break;
}
}
return result;
}
static void textBoxInit();
static int conv_run(Conv *c) {
if (!c)
return 0;
int ok = 1;
if (conv_next_node(c)) {
switch (c->exit_now) {
case CONV_NEW:
case CONV_QUIT:
case CONV_BAIL:
break;
case CONV_OK:
while (ok && conv_next_node(c)) {
switch (c->mode) {
case CONV_GET_TEXT_MODE:
cdd_init();
c->mode = CONV_GET_MESG_MODE;
if (conv_get_node_text(c)) {
ok = 0;
if (_G(cdd).num_txt_ents) { //node
mouse_unlock_sprite();
mouse_lock_sprite(0);
textBoxInit();
} else { //linear node.
conv_set_event(-1);
Common::strcpy_s(_G(player).verb, _GC(conv_name)); // was verb.
c->c_entry_num = 1;
}
}
break;
case CONV_GET_MESG_MODE:
cdd_init();
if (conv_process_entry(c->c_entry_num, c, CONV_GET_MESG_MODE)) {
mouse_unlock_sprite();
mouse_lock_sprite(5);
conv_set_event(-1);
Common::strcpy_s(_G(player).verb, _GC(conv_name));
ok = 0;
}
c->mode = CONV_UPDATE_MODE;
break;
case CONV_UPDATE_MODE:
conv_process_entry(c->c_entry_num, c, CONV_UPDATE_MODE);
c->mode = CONV_GET_TEXT_MODE;
break;
default:
break;
}
}
break;
default:
break;
}
}
if (!conv_next_node(c))
conv_unload(c);
return 0;
}
static void convtestCallback(void *a, void *) {
mouse_unlock_sprite();
mouse_lock_sprite(5);
player_set_commands_allowed(false);
TextItem *i = (TextItem *)a;
Conv *c = conv_get_handle();
if (!c)
return;
c->c_entry_num = i->tag;
c->mode = CONV_GET_MESG_MODE;
TextScrn_Destroy(_GC(myTextScrn));
_GC(myTextScrn) = nullptr;
find_true_ent(c->c_entry_num, c);
_G(cdd).mesg_snd_file = _G(cdd).snd_files[c->c_entry_num - 1];
_G(cdd).player_non_player = 1;
Common::strcpy_s(_G(player).verb, _GC(conv_name));
_G(player).command_ready = true;
conv_set_event(-1); // Must have or conv freezes.
}
void set_dlg_rect() {
int32 status;
ScreenContext *game_buff_ptr = vmng_screen_find(_G(gameDrawBuff), &status);
if (!game_buff_ptr)
error_show(FL, 'BUF!');
const int32 screen_x_center = VIDEO_W / 2;
const int32 screen_y_center = (game_buff_ptr->y2 - game_buff_ptr->y1) / 2;
const int32 screen_x_size = VIDEO_W;
const int32 screen_y_size = (game_buff_ptr->y2 - game_buff_ptr->y1);
_GC(height) = gr_font_get_height() + _GC(conv_font_spacing_v); // Must have....
_GC(width) += 2 * _GC(conv_font_spacing_h);
const int32 sizeX = _GC(width);
const int32 sizeY = _G(cdd).num_txt_ents * (_GC(height)) + _GC(conv_font_spacing_v);
switch (_GC(glob_x)) {
case DLG_CENTER_H:
_GC(r_x1) = screen_x_center - (sizeX / 2);
break;
case DLG_FLUSH_LEFT:
_GC(r_x1) = 0;
break;
case DLG_FLUSH_RIGHT:
_GC(r_x1) = screen_x_size - sizeX;
break;
default:
_GC(r_x1) = _GC(glob_x);
_GC(r_x1) += game_buff_ptr->x1;
break;
}
switch (_GC(glob_y)) {
case DLG_CENTER_V:
_GC(r_y1) = screen_y_center - (sizeY / 2);
break;
case DLG_FLUSH_TOP:
_GC(r_y1) = 0;
break;
case DLG_FLUSH_BOTTOM:
_GC(r_y1) = screen_y_size - sizeY + game_buff_ptr->y1 - 10;
break;
default:
_GC(r_y1) = _GC(glob_y);
_GC(r_y1) += game_buff_ptr->y1;
break;
}
if (_GC(r_x1) < 0)
_GC(r_x1) = 0;
if (_GC(r_y1) < 0)
_GC(r_y1) = 0;
_GC(r_y2) = _GC(r_y1) + sizeY - 1;
_GC(r_x2) = _GC(r_x1) + sizeX - 1;
_GC(r_x2) = imath_min(VIDEO_W, _GC(r_x2));
_GC(r_y2) = imath_min(VIDEO_H, _GC(r_y2));
}
static void textBoxInit() {
player_set_commands_allowed(true);
mouse_set_sprite(0);
gr_font_set(_G(font_conv));
conv_get_node_text(conv_get_handle());
set_dlg_rect();
_GC(myTextScrn) = TextScrn_Create(_GC(r_x1), _GC(r_y1), _GC(r_x2), _GC(r_y2), _GC(conv_shading),
6 | SF_GET_MOUSE | SF_IMMOVABLE | SF_BLOCK_MOUSE,
_GC(conv_normal_colour), _GC(conv_hilite_colour), _GC(conv_normal_colour_alt1),
_GC(conv_hilite_colour_alt1), _GC(conv_normal_colour_alt2),
_GC(conv_hilite_colour_alt2));
for (int32 i = 0; i < _G(cdd).num_txt_ents; i++) {
TextScrn_Add_TextItem(_GC(myTextScrn), _GC(conv_font_spacing_h),
(i * _GC(height)) + _GC(conv_font_spacing_v), i + 1, TS_GIVEN,
_G(cdd).text[i], convtestCallback);
}
TextScrn_Activate(_GC(myTextScrn));
}
void conv_get_dlg_coords(int32 *x1, int32 *y1, int32 *x2, int32 *y2) {
*x1 = _GC(r_x1);
*y1 = _GC(r_y1);
*x2 = _GC(r_x2);
*y2 = _GC(r_y2);
}
void conv_set_dlg_coords(int32 x1, int32 y1, int32 x2, int32 y2) {
_GC(r_x1) = x1;
_GC(r_y1) = y1;
_GC(r_x2) = x2;
_GC(r_y2) = y2;
}
void conv_go(Conv *c) {
conv_run(c);
}
} // End of namespace M4

346
engines/m4/adv_r/conv.h Normal file
View File

@@ -0,0 +1,346 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_CONV_H
#define M4_ADV_R_CONV_H
#include "common/array.h"
#include "m4/m4_types.h"
namespace M4 {
#define _GC(X) _G(conversations).X
#define CONV_WAIT_FOR_INPUT 1
#define CONV_HALT_FOREVER 2
#define CONV_HALT 3
#define CONV_INPUT_OK 4
#define CONV_DO_NOTHING 5
#define CONV_PLAYER_TALKING 1
#define CONV_NON_PLAYER_TALKING 0
#define DLG_FLUSH_LEFT -1
#define DLG_FLUSH_RIGHT -2
#define DLG_FLUSH_TOP -3
#define DLG_FLUSH_BOTTOM -4
#define DLG_CENTER_H -5
#define DLG_CENTER_V -6
//from: prochunk.h
#define C_ASGN_CHUNK ((int32) ('C' << 24) | ('A' << 16) | ('S' << 8) | 'N')
#define ASGN_CHUNK ((int32) ('A' << 24) | ('S' << 16) | ('G' << 8) | 'N')
#define HIDE_CHUNK ((int32) ('H' << 24) | ('I' << 16) | ('D' << 8) | 'E')
#define UHID_CHUNK ((int32) ('U' << 24) | ('H' << 16) | ('I' << 8) | 'D')
#define DSTR_CHUNK ((int32) ('D' << 24) | ('S' << 16) | ('T' << 8) | 'R')
#define CHDE_CHUNK ((int32) ('C' << 24) | ('H' << 16) | ('D' << 8) | 'E')
#define CUHD_CHUNK ((int32) ('C' << 24) | ('U' << 16) | ('H' << 8) | 'D')
#define CDST_CHUNK ((int32) ('D' << 24) | ('D' << 16) | ('T' << 8) | 'S')
#define CONV_CHUNK ((int32) ('C' << 24) | ('O' << 16) | ('N' << 8) | 'V')
#define DECL_CHUNK ((int32) ('D' << 24) | ('E' << 16) | ('C' << 8) | 'L')
#define FALL_CHUNK ((int32) ('F' << 24) | ('A' << 16) | ('L' << 8) | 'L')
#define LNODE_CHUNK ((int32) ('L' << 24) | ('N' << 16) | ('O' << 8) | 'D')
#define NODE_CHUNK ((int32) ('N' << 24) | ('O' << 16) | ('D' << 8) | 'E')
#define ENTRY_CHUNK ((int32) ('E' << 24) | ('T' << 16) | ('R' << 8) | 'Y')
#define TEXT_CHUNK ((int32) ('T' << 24) | ('E' << 16) | ('X' << 8) | 'T')
//reply
#define REPLY_CHUNK ((int32) ('R' << 24) | ('P' << 16) | ('L' << 8) | 'Y')
#define WEIGHT_REPLY_CHUNK ((int32) ('W' << 24) | ('R' << 16) | ('P' << 8) | 'L')
#define WEIGHT_PREPLY_CHUNK ((int32) ('W' << 24) | ('P' << 16) | ('R' << 8) | 'L')
#define COND_REPLY_CHUNK ((int32) ('C' << 24) | ('R' << 16) | ('P' << 8) | 'L')
#define MESSAGE_CHUNK ((int32) ('M' << 24) | ('E' << 16) | ('S' << 8) | 'G')
// goto
#define GOTO_CHUNK ((int32) ('G' << 24) | ('O' << 16) | ('T' << 8) | 'O')
#define EXIT_GOTO_CHUNK ((int32) ('E' << 24) | ('X' << 16) | ('I' << 8) | 'T')
#define COND_GOTO_CHUNK ((int32) ('C' << 24) | ('C' << 16) | ('G' << 8) | 'O')
#define COND_EXIT_GOTO_CHUNK ((int32) ('C' << 24) | ('E' << 16) | ('G' << 8) | 'O')
struct Conv {
int32 chunkSize = 0;
char *conv = nullptr;
int32 myCNode = 0;
int32 exit_now = 0;
int32 node_hash = 0;
int32 mode = 0;
int32 c_entry_num = 0;
Common::Array<int32 *> _pointers;
};
struct ConvDisplayData {
char *text[16];
char *snd_files[16];
char mesg[1024];
char *mesg_snd_file;
int num_txt_ents;
int player_non_player;
int player_choice;
};
#include "common/pack-start.h" // START STRUCT PACKING
struct conv_chunk {
int32 tag;
int32 size;
} PACKED_STRUCT;
struct decl_chunk {
int32 tag;
int32 val;
int32 flags;
int32 addrIndex; // Index into Conv::_pointers array
} PACKED_STRUCT;
struct fall_chunk {
int32 tag;
int32 val;
int32 index;
} PACKED_STRUCT;
struct node_chunk {
int32 tag;
int32 hash;
int32 size;
int32 num_entries;
} PACKED_STRUCT;
struct lnode_chunk {
int32 tag;
int32 hash;
int32 size;
int32 entry_num;
int32 num_entries;
} PACKED_STRUCT;
struct entry_chunk {
int32 tag;
int32 size;
int32 status;
} PACKED_STRUCT;
struct text_chunk {
int32 tag;
int32 size;
} PACKED_STRUCT;
struct mesg_chunk {
int32 tag;
int32 size;
} PACKED_STRUCT;
struct reply_chunk {
int32 tag;
int32 index; // Where the message is located.
} PACKED_STRUCT;
struct c_reply_chunk {
int32 tag;
int32 op_l;
int32 op;
int32 op_r;
int32 index; // Where the message is located.
} PACKED_STRUCT;
struct w_reply_chunk {
int32 tag;
int32 num_replies;
} PACKED_STRUCT;
struct w_entry_chunk {
int32 weight;
int32 index; // Where the message is located.
} PACKED_STRUCT;
struct goto_chunk {
int32 tag;
int32 index; // Where the node is located.
} PACKED_STRUCT;
struct c_goto_chunk {
int32 tag;
int32 opnd1; // Where the decl is located.
int32 op;
int32 opnd2; // Integer value.
int32 index; // Where the node is located.
} PACKED_STRUCT;
struct misc_chunk {
int32 tag;
int32 index; // Where the entry is located.
} PACKED_STRUCT;
struct c_misc_chunk {
int32 tag;
int32 c_op_l; // Where the decl is located.
int32 c_op;
int32 c_op_r; // Integer value.
int32 index; // Where the entry is located.
} PACKED_STRUCT;
struct assign_chunk {
int32 tag;
int32 index; // Where the decl is located.
int32 op;
int32 opnd1; // Integer value.
} PACKED_STRUCT;
struct c_assign_chunk {
int32 tag;
int32 c_op_l; // Where the decl is located.
int32 c_op;
int32 c_op_r; // Integer value.
int32 index; // Where the decl is located.
int32 op;
int32 opnd1; // Integer value.
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
/**
"xxxxxxxx" means the size you have calculated a conversation box to be,
x x given the sentences that are in it, and whatever border
x x space you may have in it. The boxes in Figure A may be
xxxxxxxx where the user wants to place them. Obviously, the user
won't try to put them out in hyperspace, but may do this
if he wants the box to be flush with a corner (to grow
up, down, left, or right.) Figure B is the re-calculated
coordinates of the boxes in order to get them onto the
background. The new boxes should not be in the interface
or in the letterboxed areas at the top and bottom.
xxxxxxxxx
x (d) x
x x Figure A xxxxxxxxxxxx
0,0 xxxxxxxxx x (b) x
x x
x x
(letterbox at top) xxxxxxxxxxxx
(background)
xxxxxxxxxxxxx
x (c) x
x x
x x
x x
x x
xxxxxxxxxxxxxxxxxx x x
x x xxxxxxxxxxxxx
x x (interface)
x x
x x
x x
x x (letterbox at bottom)
x x
x x
x (a) x 640,479
xxxxxxxxxxxxxxxxxx
Figure B
0,0
xxxxxxxx xxxxxxxxxxx
x (d) x x (b) x
x x x x
xxxxxxxx x x
xxxxxxxxxxx
xxxxxxxxxxxxxxxxxx
x x
x x
x x xxxxxxxxxxxxx
x x x (c) x
x x x x
x x x x
x x x x
x (a) x x x
xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxx
640,479
If someone says to draw conversation box (a) at the location in Figure 1,
then have it automatically re-position itself into where it is in Figure 2.
The extra space around the newly re-positioned box should be about 10 pixels
wide and/or tall. Make the spacing visually look identical. In other
words, if the height of the border is 10 pixels, the width on the side of
the new conversation box may need to be 15. You may have to experiment
with this. In even other words, you should correct for the aspect ration.
The same thing should work for boxes (b), (c), and (d).
*/
void set_dlg_rect();
void conv_go(Conv *c);
} // End of namespace M4
#endif

View File

@@ -0,0 +1,837 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/conv_io.h"
#include "m4/adv_r/adv_control.h"
#include "m4/adv_r/conv.h"
#include "m4/adv_r/chunk_ops.h"
#include "m4/adv_r/db_env.h"
#include "m4/core/errors.h"
#include "m4/vars.h"
#include "m4/m4.h"
namespace M4 {
#define NAME_SIZE (g_engine->getGameType() == GType_Riddle ? 12 : 8)
#define HIDDEN 0x00000004
#define DESTROYED 0x00000008
#define INITIAL 1
#define PERSISTENT 2
#define CONV_OK 0
#define CONV_QUIT -1
#define CONV_NEW -2
#define CONV_BAIL -3
#define CONV_UNKNOWN_MODE 0
#define CONV_GET_TEXT_MODE 1
#define CONV_SET_TEXT_MODE 2
#define CONV_GET_MESG_MODE 3
#define CONV_UPDATE_MODE 4
#define DECL_POINTER 1
void Converstation_Globals::syncGame(Common::Serializer &s) {
if (s.isLoading())
conv_reset_all();
// Handle size
uint32 count = convSave.size();
s.syncAsUint32LE(count);
if (s.isLoading())
convSave.resize(count);
// Sync buffer contents
if (count)
s.syncBytes(&convSave[0], count);
}
void Converstation_Globals::conv_reset_all() {
convSave.clear();
}
/*------------------------------------------------------------------------*/
void cdd_init() {
for (int i = 0; i < 16; i++) {
_G(cdd).text[i] = nullptr;
_G(cdd).snd_files[i] = nullptr;
}
_G(cdd).num_txt_ents = 0;
Common::strcpy_s(_G(cdd).mesg, "");
_G(cdd).mesg_snd_file = nullptr;
}
Conv *conv_get_handle(void) {
return _GC(globConv);
}
void conv_set_handle(Conv *c) {
_GC(globConv) = c;
}
void conv_resume(Conv *c) {
conv_go(c);
}
void conv_resume() {
conv_resume(conv_get_handle());
}
int conv_is_event_ready() {
return _GC(event_ready);
}
void conv_set_event(int e) {
_GC(event) = e;
_GC(event_ready) = 1;
}
int conv_get_event() {
_GC(event_ready) = 0;
return _GC(event);
}
void conv_play(Conv *c) {
conv_go(c);
}
void conv_play() {
conv_play(conv_get_handle());
}
int32 conv_current_node() {
if (conv_get_handle())
return conv_get_handle()->node_hash;
return 0;
}
int32 conv_current_entry() {
return _GC(ent) - 1;
}
void conv_reset(const char *filename) {
_GC(restore_conv) = 0;
Conv *c = conv_load(filename, 1, 1, -1, false);
conv_unload(c);
}
void conv_reset_all() {
_G(conversations).conv_reset_all();
}
const char *conv_sound_to_play() {
return _G(cdd).mesg_snd_file;
}
int32 conv_whos_talking() {
return _G(cdd).player_non_player;
}
int ok_status(entry_chunk *entry) {
if (entry->status & DESTROYED)
return 0;
if (entry->status & HIDDEN)
return 0;
return 1;
}
int conv_toggle_flags(entry_chunk *entry) {
if (ok_status(entry))
return (entry->status & 0x0000000e); //mask off INITIAL bit.
return entry->status;
}
int32 conv_get_decl_val(Conv *c, decl_chunk *decl) {
if (decl->flags == DECL_POINTER)
return *c->_pointers[decl->addrIndex];
return decl->val;
}
void conv_set_decl_val(Conv *c, decl_chunk *decl, int32 val) {
if (decl->flags == DECL_POINTER) {
decl->val = val;
*c->_pointers[decl->addrIndex] = val;
} else {
decl->val = val;
}
}
void conv_export_value(Conv *c, int32 val, int index) {
int32 tag = 0, next;
int i = 0;
if (!c)
return;
const int32 ent_old = c->myCNode;
int32 ent = 0;
c->myCNode = 0;
while (ent < c->chunkSize) {
conv_ops_get_entry(ent, &next, &tag, c);
if (tag == DECL_CHUNK) {
if (i == index) {
decl_chunk *decl = get_decl(c, ent);
conv_set_decl_val(c, decl, val);
}
i++;
}
ent = next;
}
c->myCNode = ent_old;
}
void conv_export_value_curr(int32 val, int index) {
conv_export_value(conv_get_handle(), val, index);
}
void conv_export_pointer(Conv *c, int32 *val, int index) {
int32 tag = 0, next;
int i = 0;
if (!c)
return;
const int32 ent_old = c->myCNode;
int32 ent = 0;
c->myCNode = 0;
while (ent < c->chunkSize) {
conv_ops_get_entry(ent, &next, &tag, c);
switch (tag) {
case DECL_CHUNK:
if (i == index) {
decl_chunk *decl = get_decl(c, ent);
c->_pointers.push_back(val);
decl->addrIndex = c->_pointers.size() - 1;
decl->flags = DECL_POINTER;
}
i++;
break;
default:
break;
}
ent = next;
}
c->myCNode = ent_old;
}
void conv_export_pointer_curr(int32 *val, int index) {
conv_export_pointer(conv_get_handle(), val, index);
}
void conv_init(Conv *c) {
switch (c->exit_now) {
case CONV_OK:
case CONV_QUIT:
break;
case CONV_BAIL:
case CONV_NEW:
if (c->myCNode != CONV_QUIT) {
c->exit_now = CONV_NEW; //conv hasn't been run before. only done here once.
c->myCNode = 0;
}
break;
default:
break;
}
}
static int32 find_state(const char *s, char *c, int file_size) {
char name[13];
int32 size = 0, offset = 0;
while (offset < file_size) {
Common::strcpy_s(name, &c[offset]);
if (!scumm_stricmp(name, s)) {
offset += NAME_SIZE * sizeof(char);
goto handled;
}
offset += NAME_SIZE * sizeof(char);
if (offset < file_size) {
memcpy(&size, &c[offset], sizeof(int32));
}
offset += size + sizeof(int32);
}
offset = -1;
handled:
return offset;
}
void find_and_set_conv_name(Conv *c) {
int32 ent = 0, tag = 0, next = 0;
c->myCNode = 0;
while (ent < c->chunkSize) {
conv_ops_get_entry(ent, &next, &tag, c);
if (tag == CONV_CHUNK) {
conv_chunk *conv = get_conv(c, ent);
assert(conv);
Common::strcpy_s(_GC(conv_name), get_string(c, c->myCNode + ent + sizeof(conv_chunk)));
}
ent = next;
}
}
static void conv_save_state(Conv *c) {
//-------------------------------------------------------------------------------
// Calculate amt_to_write by counting up the size of DECL_CHUNKs.
// the number of ENTRY_CHUNKs affects the amt_to_write
// also extract fname from the CONV_CHUNK
int32 amt_to_write = 3 * sizeof(int32); // Header size
int32 ent = 0;
int32 next, tag; // receive conv_ops_get_entry results
const int32 myCNode = c->myCNode;
char fname[13];
memset(fname, 0, 13);
int32 num_decls = 0;
int32 num_entries = 0;
c->myCNode = 0;
while (ent < c->chunkSize) {
conv_chunk *conv;
conv_ops_get_entry(ent, &next, &tag, c);
switch (tag) {
case CONV_CHUNK:
conv = get_conv(c, ent);
assert(conv);
Common::strcpy_s(fname, get_string(c, c->myCNode + ent + sizeof(conv_chunk)));
break;
case DECL_CHUNK:
num_decls++;
amt_to_write += sizeof(int32);
break;
case ENTRY_CHUNK:
num_entries++;
break;
default:
break;
}
ent = next;
}
amt_to_write += (num_entries / 8) * sizeof(int32);
if ((num_entries % 8) != 0)
amt_to_write += sizeof(int32); // Pad the sucker
//-------------------------------------------------------------------------------
// if consave data exists, read it in
int32 file_size = 0;
int32 offset;
char *conv_save_buff;
bool overwrite_file = false;
if (!_GC(convSave).empty()) {
file_size = _GC(convSave).size();
conv_save_buff = (char *)mem_alloc(file_size, "conv save buff");
if (!conv_save_buff)
error_show(FL, 'OOM!');
Common::copy(&_GC(convSave)[0], &_GC(convSave)[0] + file_size, &conv_save_buff[0]);
//----------------------------------------------------------------------------
// If this conversation already in conv data, overwrite it,
// otherwise chuck out the buffer, and create a new buffer which is just
// big enough to hold the new save data.
offset = find_state(fname, conv_save_buff, file_size);
if (offset != -1) {
overwrite_file = true;
/* int32 prev_size = */ READ_LE_UINT32(&conv_save_buff[offset]);
/* prev_size += NAME_SIZE + sizeof(int32);*/
offset += sizeof(int32); // Skip header. (name + size)
} else {
// Append
offset = 0;
mem_free(conv_save_buff);
conv_save_buff = (char *)mem_alloc(amt_to_write + NAME_SIZE + sizeof(int32), "conv save buff");
if (!conv_save_buff)
error_show(FL, 'OOM!');
memcpy(&conv_save_buff[offset], fname, NAME_SIZE * sizeof(char));
offset += NAME_SIZE * sizeof(char);
WRITE_LE_UINT32(&conv_save_buff[offset], amt_to_write);
offset += sizeof(int32);
}
} else {
//----------------------------------------------------------------------------
// Conv save dat didn't exist, so we set things up for a create here.
offset = 0;
conv_save_buff = (char *)mem_alloc(amt_to_write + NAME_SIZE + sizeof(int32), "conv save buff");
if (!conv_save_buff)
error_show(FL, 'OOM!');
memcpy(&conv_save_buff[offset], fname, NAME_SIZE * sizeof(char));
offset += NAME_SIZE * sizeof(char);
WRITE_LE_UINT32(&conv_save_buff[offset], amt_to_write);
offset += sizeof(int32);
}
//----------------------------------------------------------------------------
// finish filling in conv_save_buff data with num of entries etc.
WRITE_LE_INT32(&conv_save_buff[offset], myCNode);
offset += sizeof(int32);
WRITE_LE_UINT32(&conv_save_buff[offset], num_decls);
offset += sizeof(int32);
WRITE_LE_UINT32(&conv_save_buff[offset], num_entries);
offset += sizeof(int32);
int32 size = 3 * sizeof(int32);
// fill in all the entries themselves
int32 e_flags = 0;
short flag_index = 0;
ent = 0;
c->myCNode = 0;
int32 val;
entry_chunk *entry;
while (ent < c->chunkSize) {
conv_ops_get_entry(ent, &next, &tag, c);
decl_chunk *decl;
switch (tag) {
case DECL_CHUNK:
decl = get_decl(c, ent);
val = conv_get_decl_val(c, decl);
WRITE_LE_UINT32(&conv_save_buff[offset], val);
offset += sizeof(int32);
size += sizeof(int32);
break;
case LNODE_CHUNK:
case NODE_CHUNK:
break;
case ENTRY_CHUNK:
entry = get_entry(c, ent);
if (flag_index == 32) {
flag_index = 0;
WRITE_LE_UINT32(&conv_save_buff[offset], e_flags);
offset += sizeof(int32);
size += sizeof(int32);
e_flags = 0;
}
e_flags |= ((entry->status & 0x0000000f) << flag_index);
flag_index += 4;
break;
default:
break;
}
ent = next;
}
// Copy the flags
if (flag_index != 0) {
WRITE_LE_UINT32(&conv_save_buff[offset], e_flags);
// offset += sizeof(int32);
size += sizeof(int32);
}
if (amt_to_write != size)
error_show(FL, 'CNVS', "save_state: error! size written != size (%d %d)", amt_to_write, size);
// Finally, write out the conversation data
if (overwrite_file == true) {
_GC(convSave).resize(file_size);
Common::copy(conv_save_buff, conv_save_buff + file_size, &_GC(convSave)[0]);
} else {
// Append conversation
const size_t oldSize = _GC(convSave).size();
file_size = amt_to_write + NAME_SIZE + sizeof(int32);
_GC(convSave).resize(_GC(convSave).size() + file_size);
Common::copy(conv_save_buff, conv_save_buff + file_size, &_GC(convSave)[oldSize]);
}
mem_free(conv_save_buff);
}
static Conv *conv_restore_state(Conv *c) {
int32 tag, next;
short flag_index = 0;
int32 val;
int32 e_flags = 0;
int32 myCNode;
char fname[13];
int file_size;
int32 ent;
c->myCNode = 0;
find_and_set_conv_name(c);
Common::strcpy_s(fname, _GC(conv_name));
if (_GC(convSave).empty())
file_size = -1;
else
file_size = _GC(convSave).size();
if (file_size <= 0) {
conv_init(c);
return c;
}
char *conv_save_buff = (char *)mem_alloc(file_size, "conv save buff");
if (!conv_save_buff)
error_show(FL, 'OOM!');
// ------------------
Common::copy(&_GC(convSave)[0], &_GC(convSave)[0] + file_size, &conv_save_buff[0]);
int32 offset = find_state(fname, conv_save_buff, file_size);
if (offset == -1)
goto i_am_so_done;
// Skip header.
offset += sizeof(int32);
myCNode = READ_LE_INT32(&conv_save_buff[offset]);
offset += sizeof(int32);
/*int num_decls = */READ_LE_UINT32(&conv_save_buff[offset]);
offset += sizeof(int32);
/*int num_entries = */READ_LE_UINT32(&conv_save_buff[offset]);
offset += sizeof(int32);
ent = 0; c->myCNode = 0;
while (ent < c->chunkSize) {
conv_ops_get_entry(ent, &next, &tag, c);
if (tag == DECL_CHUNK) {
val = READ_LE_UINT32(&conv_save_buff[offset]);
offset += sizeof(int32);
decl_chunk *decl = get_decl(c, ent);
conv_set_decl_val(c, decl, val);
}
ent = next;
}
ent = 0;
c->myCNode = 0;
while (ent < c->chunkSize) {
conv_ops_get_entry(ent, &next, &tag, c);
if (tag == ENTRY_CHUNK) {
entry_chunk *entry = get_entry(c, ent);
if (flag_index == 32) {
flag_index = 0;
//flag_num++;
}
if (flag_index == 0) {
e_flags = READ_LE_UINT32(&conv_save_buff[offset]);
offset += sizeof(int32);
}
val = (e_flags >> flag_index) & 0x0000000f;
entry->status = val;
flag_index += 4;
}
ent = next;
}
c->myCNode = myCNode;
if (c->myCNode == CONV_QUIT) {
c->exit_now = CONV_QUIT;
conv_unload(c);
c = nullptr;
} else
c->exit_now = CONV_OK;
i_am_so_done:
mem_free(conv_save_buff);
return c;
}
void conv_set_font_spacing(int32 h, int32 v) {
_GC(conv_font_spacing_h) = h;
_GC(conv_font_spacing_v) = v;
}
void conv_set_text_colours(int32 norm_colour, int32 norm_colour_alt1, int32 norm_colour_alt2,
int32 hi_colour, int32 hi_colour_alt1, int32 hi_colour_alt2) {
_GC(conv_normal_colour) = norm_colour;
_GC(conv_normal_colour_alt1) = norm_colour_alt1;
_GC(conv_normal_colour_alt2) = norm_colour_alt2;
_GC(conv_hilite_colour) = hi_colour;
_GC(conv_hilite_colour_alt1) = hi_colour_alt1;
_GC(conv_hilite_colour_alt2) = hi_colour_alt2;
}
void conv_set_text_colour(int32 norm_colour, int32 hi_colour) {
conv_set_text_colours(norm_colour, norm_colour, norm_colour, hi_colour, hi_colour, hi_colour);
}
void conv_set_default_hv(int32 h, int32 v) {
_GC(conv_default_h) = h;
_GC(conv_default_v) = v;
}
void conv_set_default_text_colour(int32 norm_colour, int32 hi_colour) {
conv_set_text_colours(norm_colour, norm_colour, norm_colour, hi_colour, hi_colour, hi_colour);
_GC(conv_default_normal_colour) = norm_colour;
_GC(conv_default_hilite_colour) = hi_colour;
}
void conv_set_shading(int32 shade) {
_GC(conv_shading) = shade;
}
void conv_set_box_xy(int32 x, int32 y) {
_GC(glob_x) = x;
_GC(glob_y) = y;
}
static void conv_set_disp_default(void) {
_GC(conv_font_spacing_h) = _GC(conv_default_h);
_GC(conv_font_spacing_v) = _GC(conv_default_v);
_GC(conv_normal_colour) = _GC(conv_default_normal_colour);
_GC(conv_hilite_colour) = _GC(conv_default_hilite_colour);
_GC(conv_shading) = 75;
}
Conv *conv_load(const char *filename, int x1, int y1, int32 myTrigger, bool want_box) {
char fullPathname[MAX_FILENAME_SIZE];
term_message("conv_load");
// Remember if player commands are on before we start the conversation
_GC(playerCommAllowed) = _G(player).comm_allowed;
_GC(interface_was_visible) = INTERFACE_VISIBLE;
term_message("conv load: %s", filename);
if (want_box) {
// If we want an interface box
conv_set_disp_default();
mouse_set_sprite(0); // Also if we want a text box, lock the mouse into arrow mode
mouse_lock_sprite(0);
player_set_commands_allowed(false); // with commands off
// Hide the interface if it's visible
if (INTERFACE_VISIBLE)
interface_hide();
}
// if not in rooms.db, use actual filename
char *str = env_find(filename);
if (str)
Common::strcpy_s(fullPathname, str);
else
Common::sprintf_s(fullPathname, "%s.chk", filename);
SysFile fp(fullPathname);
if (!fp.exists()) {
// Force the file open
error_show(FL, 'CNVL', "couldn't conv_load %s", fullPathname);
}
const int32 cSize = fp.size();
if (conv_get_handle() != nullptr) {
conv_unload();
}
Conv *convers = new Conv();
convers->chunkSize = cSize;
convers->conv = nullptr;
convers->myCNode = 0;
convers->exit_now = CONV_NEW;
convers->node_hash = 0;
convers->mode = CONV_GET_TEXT_MODE;
convers->c_entry_num = 1;
_GC(myFinalTrigger) = kernel_trigger_create(myTrigger);
convers->conv = (char *)mem_alloc(cSize * sizeof(char), "conv char data");
if (!fp.read((byte *)convers->conv, cSize)) {
conv_set_handle(nullptr);
delete convers;
convers = nullptr;
fp.close();
return nullptr;
}
conv_swap_words(convers);
find_and_set_conv_name(convers);
_GC(glob_x) = x1;
_GC(glob_y) = y1;
if (want_box)
set_dlg_rect();
if (_GC(restore_conv))
convers = conv_restore_state(convers);
_GC(restore_conv) = 1;
conv_set_handle(convers);
fp.close();
return convers;
}
void conv_load_and_prepare(const char *filename, int trigger, bool ignoreIt) {
player_set_commands_allowed(false);
if (!ignoreIt) {
conv_load(filename, 10, 375, trigger, true);
conv_set_shading(100);
conv_set_text_colours(3, 1, 2, 22, 10, 14);
conv_set_font_spacing(10, 2);
}
}
void conv_unload(Conv *c) {
mouse_unlock_sprite();
if (_GC(interface_was_visible)) { // Turn interface back on if it was on
interface_show();
}
_GC(globConv) = nullptr;
if (c)
conv_save_state(c);
player_set_commands_allowed(_GC(playerCommAllowed));
_G(player).command_ready = false;
_G(player).ready_to_walk = false;
_G(player).need_to_walk = false;
Common::strcpy_s(_G(player).verb, "");
Common::strcpy_s(_G(player).noun, "");
kernel_trigger_dispatchx(_GC(myFinalTrigger));
if (c) {
if (c->conv)
mem_free(c->conv);
delete c;
}
_GC(globConv) = c = nullptr;
}
void conv_unload() {
conv_unload(conv_get_handle());
}
// only called if node is visible.
// gets the TEXT chunks inside a node.
int conv_get_text(int32 offset, int32 size, Conv *c) {
int32 i = offset, tag, next;
int result = 0;
size -= sizeof(entry_chunk);
while (i < offset + size) {
conv_ops_get_entry(i, &next, &tag, c);
if (tag == TEXT_CHUNK) {
result = 1;
text_chunk *text = get_text(c, i);
assert(text);
const int32 text_len = conv_ops_text_strlen(get_string(c, c->myCNode + i + sizeof(text_chunk)));
_G(cdd).snd_files[_G(cdd).num_txt_ents] = get_string(c, c->myCNode + i + sizeof(text_chunk));
_G(cdd).text[_G(cdd).num_txt_ents] = get_string(c, c->myCNode + i + sizeof(text_chunk) + text_len);
const int32 text_width = gr_font_string_width(_G(cdd).text[_G(cdd).num_txt_ents], 1);
if (text_width > _GC(width))
_GC(width) = text_width;
_G(cdd).num_txt_ents++;
}
i = next;
}
return result;
}
} // End of namespace M4

149
engines/m4/adv_r/conv_io.h Normal file
View File

@@ -0,0 +1,149 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_CONV_IO_H
#define M4_ADV_R_CONV_IO_H
#include "common/array.h"
#include "common/serializer.h"
#include "m4/m4_types.h"
#include "m4/adv_r/conv.h"
#include "m4/graphics/gr_pal.h"
#include "m4/gui/gui_dialog.h"
namespace M4 {
#define HIDDEN 0x00000004
#define DESTROYED 0x00000008
#define INITIAL 1
#define PERSISTENT 2
#define CONV_OK 0
#define CONV_QUIT -1
#define CONV_NEW -2
#define CONV_BAIL -3
#define CONV_UNKNOWN_MODE 0
#define CONV_GET_TEXT_MODE 1
#define CONV_SET_TEXT_MODE 2
#define CONV_GET_MESG_MODE 3
#define CONV_UPDATE_MODE 4
#define DECL_POINTER 1
struct Converstation_Globals {
Common::Array<byte> convSave; // Original used an actual file. We use a memory buffer
int event = 0;
int event_ready = 0;
char conv_name[16];
Conv *globConv = nullptr;
bool playerCommAllowed = false;
int32 myFinalTrigger = 0;
bool interface_was_visible = false; // to remember to turn it back on
int restore_conv = 1;
int ent = 0;
bool swap = false;
int32 conv_font_spacing_h = 0;
int32 conv_font_spacing_v = 5;
int32 conv_default_h = conv_font_spacing_h;
int32 conv_default_v = conv_font_spacing_v;
int32 conv_shading = 65;
int32 conv_normal_colour = __BLACK;
int32 conv_normal_colour_alt1 = __GREEN;
int32 conv_normal_colour_alt2 = __GREEN;
int32 conv_hilite_colour = __YELLOW;
int32 conv_default_hilite_colour = __YELLOW;
int32 conv_hilite_colour_alt1 = __YELLOW;
int32 conv_hilite_colour_alt2 = __YELLOW;
int32 conv_default_normal_colour = __BLACK;
TextScrn *myTextScrn = nullptr;
int32 width = 0, height = 0;
int32 glob_x = 0, glob_y = 0;
int32 r_x1 = 0, r_y1 = 0, r_x2 = 0, r_y2 = 0;
int n_t_e = 0;
void syncGame(Common::Serializer &s);
void conv_reset_all();
};
Conv *conv_load(const char *filename, int x1, int y1, int32 myTrigger, bool want_box = true);
void conv_load_and_prepare(const char *filename, int trigger, bool ignoreIt = false);
void conv_unload(Conv *c);
void conv_unload();
void conv_shutdown();
Conv *conv_get_handle();
void conv_set_handle(Conv *c);
void conv_resume(Conv *c);
void conv_resume();
void conv_reset(const char *filename);
void conv_reset_all();
void conv_play(Conv *c);
void conv_play();
const char *conv_sound_to_play();
int32 conv_whos_talking();
int32 conv_get_decl_val(Conv *c, decl_chunk *decl);
void conv_set_decl_val(Conv *c, decl_chunk *decl, int32 val);
void conv_export_value(Conv *c, int32 val, int index);
void conv_export_value_curr(int32 val, int index);
void conv_export_pointer(Conv *c, int32 *val, int index);
void conv_export_pointer_curr(int32 *val, int index);
void conv_set_font_spacing(int32 h, int32 v);
void conv_set_text_colour(int32 norm_colour, int32 hi_colour);
void conv_set_text_colours(int32 norm_colour, int32 norm_colour_alt1, int32 norm_colour_alt2,
int32 hi_colour, int32 hi_colour_alt1, int32 hi_colour_alt2);
void conv_set_shading(int32 shade);
void conv_set_box_xy(int32 x, int32 y);
void conv_get_dlg_coords(int32 *x1, int32 *y1, int32 *x2, int32 *y2);
void conv_set_dlg_coords(int32 x1, int32 y1, int32 x2, int32 y2);
void conv_set_default_text_colour(int32 norm_colour, int32 hi_colour);
void conv_set_default_hv(int32 h, int32 v);
int conv_get_event();
void conv_set_event(int e);
int conv_is_event_ready();
void conv_swap_words(Conv *c);
int32 conv_current_node();
int32 conv_current_entry();
int conv_toggle_flags(entry_chunk *entry);
int ok_status(entry_chunk *entry);
int conv_get_text(int32 offset, int32 size, Conv *c);
void cdd_init();
} // End of namespace M4
#endif

View File

@@ -0,0 +1,52 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/str.h"
#include "common/textconsole.h"
#include "m4/adv_db_r/db_catalog.h"
#include "m4/adv_r/db_env.h"
namespace M4 {
char *env_find(const Common::String &descName) {
static char name[144];
static char resultPath[144];
int32 sceneCode;
Common::strcpy_s(name, descName.c_str());
if (descName.hasPrefixIgnoreCase(".raw") || descName.hasPrefixIgnoreCase(".hmp")) {
return name;
} else {
db_rmlst_get_asset_room_path(name, resultPath, &sceneCode);
if (strlen(resultPath) == 0)
return nullptr;
env_get_path(name, sceneCode, resultPath);
return name;
}
}
char *env_get_path(char *resultPath, int room_num, char *fileName) {
error("env_get_path not implemented in ScummVM");
}
} // End of namespace M4

35
engines/m4/adv_r/db_env.h Normal file
View File

@@ -0,0 +1,35 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_DB_ENV_H
#define M4_ADV_R_DB_ENV_H
#include "common/str.h"
namespace M4 {
char *env_find(const Common::String &descName);
char *env_get_path(char *resultPath, int room_num, char *fileName);
} // End of namespace M4
#endif

77
engines/m4/adv_r/kernel.h Normal file
View File

@@ -0,0 +1,77 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_KERNEL_H
#define M4_ADV_R_KERNEL_H
#include "m4/adv_r/adv.h"
#include "m4/wscript/ws_machine.h"
namespace M4 {
#define CACHE_NOT_OVERRIDE_BY_FLAG_PARSE 2
#define KERNEL_RESTORING_GAME -2
#define KERNEL_SCRATCH_SIZE 256 // Size of game scratch area
struct Kernel {
uint32 scratch[KERNEL_SCRATCH_SIZE]; // Scratch variables for room
bool hag_mode = true;
uint32 clock = 0; // Current game timing clock
int32 trigger = 0; // Game trigger code, if any
int32 letter_box_x = 0;
int32 letter_box_y = 0;
int32 restore_slot = -1;
int16 first_non_walker_cel_hash = 0;
int16 last_save = 0; // Most recent save slot #
char save_file_name[8] = { 0 };
bool restore_game = false; // TRUE if we wanna restore
bool teleported_in = false; // Flag if player teleported to room
int32 fade_up_time = 0;
int16 first_fade = 0;
bool fading_to_grey = false;
bool suppress_fadeup = false;
bool force_restart = false;
bool pause = false;
KernelTriggerType trigger_mode = KT_DAEMON; // trigger was/is invoked in this mode
bool call_daemon_every_loop = false;
bool continue_handling_trigger = true; // set to True in apps code when trigger is to
// be handled by the next layer (scene/section/global daemon code)
int suppress_cache = CACHE_NOT_OVERRIDE_BY_FLAG_PARSE;
bool start_up_with_dbg_ws = false;
bool use_debug_monitor = false;
bool use_log_file = false;
bool track_open_close = false;
bool going = false;
bool camera_pan_instant = false;
bool unused = false;
size_t mem_avail() const { return 7999999; }
bool cameraPans() const { return !camera_pan_instant; }
};
} // namespace M4
#endif

View File

@@ -0,0 +1,34 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/adv_r/other.h"
#include "m4/adv_r/adv_file.h"
#include "m4/core/errors.h"
namespace M4 {
void other_save_game_for_resurrection(void) {
if (kernel_save_game(0, nullptr, 0, nullptr, 0)) {
error_show(FL, 0, "couldn't other_save_game_for_res");
}
}
} // namespace M4

34
engines/m4/adv_r/other.h Normal file
View File

@@ -0,0 +1,34 @@
/* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_ADV_R_OTHER_H
#define M4_ADV_R_OTHER_H
#include "m4/m4_types.h"
namespace M4 {
void other_save_game_for_resurrection();
} // namespace M4
#endif