scummvm/engines/glk/adrift/scgamest.cpp
2022-10-23 22:46:19 +02:00

1001 lines
30 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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 "glk/adrift/scare.h"
#include "glk/adrift/scprotos.h"
#include "glk/adrift/scgamest.h"
namespace Glk {
namespace Adrift {
/* Assorted definitions and constants. */
static const sc_uint GAME_MAGIC = 0x35aed26e;
/*
* gs_move_player_to_room()
* gs_player_in_room()
*
* Move the player to a given room, and check presence in a given room.
*/
void gs_move_player_to_room(sc_gameref_t game, sc_int room) {
assert(gs_is_game_valid(game));
if (room < 0) {
sc_fatal("gs_move_player_to_room: invalid room, %ld\n", room);
return;
} else if (room < game->room_count)
game->playerroom = room;
else
game->playerroom = lib_random_roomgroup_member(game,
room - game->room_count);
game->playerparent = -1;
game->playerposition = 0;
}
sc_bool gs_player_in_room(sc_gameref_t game, sc_int room) {
assert(gs_is_game_valid(game));
return game->playerroom == room;
}
/*
* gs_in_range()
*
* Helper for event, room, object, and npc range assertions.
*/
static sc_bool gs_in_range(sc_int value, sc_int limit) {
return value >= 0 && value < limit;
}
/*
* gs_*()
*
* Game accessors and mutators.
*/
sc_var_setref_t gs_get_vars(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->vars;
}
sc_prop_setref_t gs_get_bundle(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->bundle;
}
sc_filterref_t gs_get_filter(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->filter;
}
sc_memo_setref_t gs_get_memento(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->memento;
}
/*
* Game accessors and mutators for the player.
*/
void gs_set_playerroom(sc_gameref_t gs, sc_int room) {
assert(gs_is_game_valid(gs));
gs->playerroom = room;
}
void gs_set_playerposition(sc_gameref_t gs, sc_int position) {
assert(gs_is_game_valid(gs));
gs->playerposition = position;
}
void gs_set_playerparent(sc_gameref_t gs, sc_int parent) {
assert(gs_is_game_valid(gs));
gs->playerparent = parent;
}
sc_int gs_playerroom(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->playerroom;
}
sc_int gs_playerposition(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->playerposition;
}
sc_int gs_playerparent(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->playerparent;
}
/*
* Game accessors and mutators for events.
*/
sc_int gs_event_count(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->event_count;
}
void gs_set_event_state(sc_gameref_t gs, sc_int event, sc_int state) {
assert(gs_is_game_valid(gs) && gs_in_range(event, gs->event_count));
gs->events[event].state = state;
}
void gs_set_event_time(sc_gameref_t gs, sc_int event, sc_int etime) {
assert(gs_is_game_valid(gs) && gs_in_range(event, gs->event_count));
gs->events[event].time = etime;
}
sc_int gs_event_state(sc_gameref_t gs, sc_int event) {
assert(gs_is_game_valid(gs) && gs_in_range(event, gs->event_count));
return gs->events[event].state;
}
sc_int gs_event_time(sc_gameref_t gs, sc_int event) {
assert(gs_is_game_valid(gs) && gs_in_range(event, gs->event_count));
return gs->events[event].time;
}
void gs_decrement_event_time(sc_gameref_t gs, sc_int event) {
assert(gs_is_game_valid(gs) && gs_in_range(event, gs->event_count));
gs->events[event].time--;
}
/*
* Game accessors and mutators for rooms.
*/
sc_int gs_room_count(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->room_count;
}
void gs_set_room_seen(sc_gameref_t gs, sc_int room, sc_bool seen) {
assert(gs_is_game_valid(gs) && gs_in_range(room, gs->room_count));
gs->rooms[room].visited = seen;
}
sc_bool gs_room_seen(sc_gameref_t gs, sc_int room) {
assert(gs_is_game_valid(gs) && gs_in_range(room, gs->room_count));
return gs->rooms[room].visited;
}
/*
* Game accessors and mutators for tasks.
*/
sc_int gs_task_count(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->task_count;
}
void gs_set_task_done(sc_gameref_t gs, sc_int task, sc_bool done) {
assert(gs_is_game_valid(gs) && gs_in_range(task, gs->task_count));
gs->tasks[task].done = done;
}
void gs_set_task_scored(sc_gameref_t gs, sc_int task, sc_bool scored) {
assert(gs_is_game_valid(gs) && gs_in_range(task, gs->task_count));
gs->tasks[task].scored = scored;
}
sc_bool gs_task_done(sc_gameref_t gs, sc_int task) {
assert(gs_is_game_valid(gs) && gs_in_range(task, gs->task_count));
return gs->tasks[task].done;
}
sc_bool gs_task_scored(sc_gameref_t gs, sc_int task) {
assert(gs_is_game_valid(gs) && gs_in_range(task, gs->task_count));
return gs->tasks[task].scored;
}
/*
* Game accessors and mutators for objects.
*/
sc_int gs_object_count(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->object_count;
}
void gs_set_object_openness(sc_gameref_t gs, sc_int object, sc_int openness) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].openness = openness;
}
void gs_set_object_state(sc_gameref_t gs, sc_int object, sc_int state) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].state = state;
}
void gs_set_object_seen(sc_gameref_t gs, sc_int object, sc_bool seen) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].seen = seen;
}
void gs_set_object_unmoved(sc_gameref_t gs, sc_int object, sc_bool unmoved) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].unmoved = unmoved;
}
void gs_set_object_static_unmoved(sc_gameref_t gs, sc_int object, sc_bool unmoved) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].static_unmoved = unmoved;
}
sc_int gs_object_openness(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
return gs->objects[object].openness;
}
sc_int gs_object_state(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
return gs->objects[object].state;
}
sc_bool gs_object_seen(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
return gs->objects[object].seen;
}
sc_bool gs_object_unmoved(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
return gs->objects[object].unmoved;
}
sc_bool gs_object_static_unmoved(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
return gs->objects[object].static_unmoved;
}
sc_int gs_object_position(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
return gs->objects[object].position;
}
sc_int gs_object_parent(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
return gs->objects[object].parent;
}
static void gs_object_move_onto_unchecked(sc_gameref_t gs, sc_int object, sc_int onto) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].position = OBJ_ON_OBJECT;
gs->objects[object].parent = onto;
}
static void gs_object_move_into_unchecked(sc_gameref_t gs, sc_int object, sc_int into) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].position = OBJ_IN_OBJECT;
gs->objects[object].parent = into;
}
static void gs_object_make_hidden_unchecked(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].position = OBJ_HIDDEN;
gs->objects[object].parent = -1;
}
static void gs_object_player_get_unchecked(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].position = OBJ_HELD_PLAYER;
gs->objects[object].parent = -1;
}
static void gs_object_npc_get_unchecked(sc_gameref_t gs, sc_int object, sc_int npc) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].position = OBJ_HELD_NPC;
gs->objects[object].parent = npc;
}
static void gs_object_player_wear_unchecked(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].position = OBJ_WORN_PLAYER;
gs->objects[object].parent = 0;
}
static void gs_object_npc_wear_unchecked(sc_gameref_t gs, sc_int object, sc_int npc) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].position = OBJ_WORN_NPC;
gs->objects[object].parent = npc;
}
static void gs_object_to_room_unchecked(sc_gameref_t gs, sc_int object, sc_int room) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
gs->objects[object].position = room + 1;
gs->objects[object].parent = -1;
}
void gs_object_move_onto(sc_gameref_t gs, sc_int object, sc_int onto) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
if (gs->objects[object].position != OBJ_ON_OBJECT
|| gs->objects[object].parent != onto) {
gs_object_move_onto_unchecked(gs, object, onto);
gs->objects[object].unmoved = FALSE;
}
}
void gs_object_move_into(sc_gameref_t gs, sc_int object, sc_int into) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
if (gs->objects[object].position != OBJ_IN_OBJECT
|| gs->objects[object].parent != into) {
gs_object_move_into_unchecked(gs, object, into);
gs->objects[object].unmoved = FALSE;
}
}
void gs_object_make_hidden(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
if (gs->objects[object].position != OBJ_HIDDEN) {
gs_object_make_hidden_unchecked(gs, object);
gs->objects[object].unmoved = FALSE;
}
}
void gs_object_player_get(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
if (gs->objects[object].position != OBJ_HELD_PLAYER) {
gs_object_player_get_unchecked(gs, object);
gs->objects[object].unmoved = FALSE;
}
}
void gs_object_npc_get(sc_gameref_t gs, sc_int object, sc_int npc) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
if (gs->objects[object].position != OBJ_HELD_NPC
|| gs->objects[object].parent != npc) {
gs_object_npc_get_unchecked(gs, object, npc);
gs->objects[object].unmoved = FALSE;
}
}
void gs_object_player_wear(sc_gameref_t gs, sc_int object) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
if (gs->objects[object].position != OBJ_WORN_PLAYER) {
gs_object_player_wear_unchecked(gs, object);
gs->objects[object].unmoved = FALSE;
}
}
void gs_object_npc_wear(sc_gameref_t gs, sc_int object, sc_int npc) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
if (gs->objects[object].position != OBJ_WORN_NPC
|| gs->objects[object].parent != npc) {
gs_object_npc_wear_unchecked(gs, object, npc);
gs->objects[object].unmoved = FALSE;
}
}
void gs_object_to_room(sc_gameref_t gs, sc_int object, sc_int room) {
assert(gs_is_game_valid(gs) && gs_in_range(object, gs->object_count));
if (gs->objects[object].position != room + 1) {
gs_object_to_room_unchecked(gs, object, room);
gs->objects[object].unmoved = FALSE;
}
}
/*
* Game accessors and mutators for NPCs.
*/
sc_int gs_npc_count(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
return gs->npc_count;
}
void gs_set_npc_location(sc_gameref_t gs, sc_int npc, sc_int location) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count));
gs->npcs[npc].location = location;
}
sc_int gs_npc_location(sc_gameref_t gs, sc_int npc) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count));
return gs->npcs[npc].location;
}
void gs_set_npc_position(sc_gameref_t gs, sc_int npc, sc_int position) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count));
gs->npcs[npc].position = position;
}
sc_int gs_npc_position(sc_gameref_t gs, sc_int npc) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count));
return gs->npcs[npc].position;
}
void gs_set_npc_parent(sc_gameref_t gs, sc_int npc, sc_int parent) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count));
gs->npcs[npc].parent = parent;
}
sc_int gs_npc_parent(sc_gameref_t gs, sc_int npc) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count));
return gs->npcs[npc].parent;
}
void gs_set_npc_seen(sc_gameref_t gs, sc_int npc, sc_bool seen) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count));
gs->npcs[npc].seen = seen;
}
sc_bool gs_npc_seen(sc_gameref_t gs, sc_int npc) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count));
return gs->npcs[npc].seen;
}
sc_int gs_npc_walkstep_count(sc_gameref_t gs, sc_int npc) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count));
return gs->npcs[npc].walkstep_count;
}
void gs_set_npc_walkstep(sc_gameref_t gs,
sc_int npc, sc_int walk, sc_int walkstep) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count)
&& gs_in_range(walk, gs->npcs[npc].walkstep_count));
gs->npcs[npc].walksteps[walk] = walkstep;
}
sc_int gs_npc_walkstep(sc_gameref_t gs, sc_int npc, sc_int walk) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count)
&& gs_in_range(walk, gs->npcs[npc].walkstep_count));
return gs->npcs[npc].walksteps[walk];
}
void gs_decrement_npc_walkstep(sc_gameref_t gs, sc_int npc, sc_int walk) {
assert(gs_is_game_valid(gs) && gs_in_range(npc, gs->npc_count)
&& gs_in_range(walk, gs->npcs[npc].walkstep_count));
gs->npcs[npc].walksteps[walk]--;
}
/*
* Convenience functions for bulk clearance of references.
*/
void gs_clear_npc_references(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
memset(gs->npc_references,
FALSE, gs->npc_count * sizeof(*gs->npc_references));
}
void gs_clear_object_references(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
memset(gs->object_references,
FALSE, gs->object_count * sizeof(*gs->object_references));
}
void gs_set_multiple_references(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
memset(gs->multiple_references,
TRUE, gs->object_count * sizeof(*gs->multiple_references));
}
void gs_clear_multiple_references(sc_gameref_t gs) {
assert(gs_is_game_valid(gs));
memset(gs->multiple_references,
FALSE, gs->object_count * sizeof(*gs->multiple_references));
}
/*
* gs_create()
*
* Create and initialize a game state.
*/
sc_gameref_t gs_create(sc_var_setref_t vars, sc_prop_setref_t bundle, sc_filterref_t filter) {
sc_gameref_t game;
sc_vartype_t vt_key[4];
sc_int index_, bytes;
assert(vars && bundle && filter);
/* Create the initial state structure. */
game = (sc_gameref_t)sc_malloc(sizeof(*game));
game->magic = GAME_MAGIC;
/* Store the variables, properties bundle, and filter references. */
game->vars = vars;
game->bundle = bundle;
game->filter = filter;
/* Set memento to NULL for now; it's added later. */
game->memento = nullptr;
/* Initialize for no debugger. */
game->debugger = nullptr;
/* Initialize the undo buffers to NULL for now. */
game->temporary = nullptr;
game->undo = nullptr;
game->undo_available = FALSE;
/* Create rooms state array. */
vt_key[0].string = "Rooms";
game->room_count = prop_get_child_count(bundle, "I<-s", vt_key);
game->rooms = (sc_roomstate_t *)sc_malloc(game->room_count * sizeof(*game->rooms));
/* Set up initial rooms states. */
for (index_ = 0; index_ < game->room_count; index_++)
gs_set_room_seen(game, index_, FALSE);
/* Create objects state array. */
vt_key[0].string = "Objects";
game->object_count = prop_get_child_count(bundle, "I<-s", vt_key);
game->objects = (sc_objectstate_t *)sc_malloc(game->object_count * sizeof(*game->objects));
/* Set up initial object states. */
for (index_ = 0; index_ < game->object_count; index_++) {
const sc_char *inroomdesc;
sc_bool is_static, unmoved;
vt_key[1].integer = index_;
vt_key[2].string = "Static";
is_static = prop_get_boolean(bundle, "B<-sis", vt_key);
if (is_static) {
sc_int type;
vt_key[2].string = "Where";
vt_key[3].string = "Type";
type = prop_get_integer(bundle, "I<-siss", vt_key);
if (type == ROOMLIST_NPC_PART) {
sc_int parent;
game->objects[index_].position = OBJ_PART_NPC;
vt_key[2].string = "Parent";
parent = prop_get_integer(bundle, "I<-sis", vt_key) - 1;
game->objects[index_].parent = parent;
} else
gs_object_make_hidden_unchecked(game, index_);
} else {
sc_int initialparent, initialposition;
vt_key[2].string = "Parent";
initialparent = prop_get_integer(bundle, "I<-sis", vt_key);
vt_key[2].string = "InitialPosition";
initialposition = prop_get_integer(bundle, "I<-sis", vt_key);
switch (initialposition) {
case 0: /* Hidden. */
gs_object_make_hidden_unchecked(game, index_);
break;
case 1: /* Held. */
if (initialparent == 0) /* By player. */
gs_object_player_get_unchecked(game, index_);
else /* By NPC. */
gs_object_npc_get_unchecked(game, index_, initialparent - 1);
break;
case 2: /* In container. */
gs_object_move_into_unchecked(game, index_,
obj_container_object(game, initialparent));
break;
case 3: /* On surface. */
gs_object_move_onto_unchecked(game, index_,
obj_surface_object(game, initialparent));
break;
default: /* In room, or worn by player/NPC. */
if (initialposition >= 4
&& initialposition < 4 + game->room_count) {
gs_object_to_room_unchecked(game,
index_, initialposition - 4);
} else if (initialposition == 4 + game->room_count) {
if (initialparent == 0)
gs_object_player_wear_unchecked(game, index_);
else
gs_object_npc_wear_unchecked(game,
index_, initialparent - 1);
} else {
sc_error("gs_create: object in out of bounds room, %ld\n",
initialposition - 4 - game->room_count);
gs_object_to_room_unchecked(game, index_, -2);
}
}
}
vt_key[2].string = "CurrentState";
gs_set_object_state(game, index_,
prop_get_integer(bundle, "I<-sis", vt_key));
vt_key[2].string = "Openable";
gs_set_object_openness(game, index_,
prop_get_integer(bundle, "I<-sis", vt_key));
gs_set_object_seen(game, index_, FALSE);
vt_key[2].string = "InRoomDesc";
inroomdesc = prop_get_string(bundle, "S<-sis", vt_key);
if (!sc_strempty(inroomdesc)) {
vt_key[2].string = "OnlyWhenNotMoved";
if (prop_get_integer(bundle, "I<-sis", vt_key) == 1)
unmoved = TRUE;
else
unmoved = FALSE;
} else
unmoved = FALSE;
gs_set_object_unmoved(game, index_, unmoved);
gs_set_object_static_unmoved(game, index_, TRUE);
}
/* Create tasks state array. */
vt_key[0].string = "Tasks";
game->task_count = prop_get_child_count(bundle, "I<-s", vt_key);
game->tasks = (sc_taskstate_t *)sc_malloc(game->task_count * sizeof(*game->tasks));
/* Set up initial tasks states. */
for (index_ = 0; index_ < game->task_count; index_++) {
gs_set_task_done(game, index_, FALSE);
gs_set_task_scored(game, index_, FALSE);
}
/* Create events state array. */
vt_key[0].string = "Events";
game->event_count = prop_get_child_count(bundle, "I<-s", vt_key);
game->events = (sc_eventstate_t *)sc_malloc(game->event_count * sizeof(*game->events));
/* Set up initial events states. */
for (index_ = 0; index_ < game->event_count; index_++) {
sc_int startertype;
vt_key[1].integer = index_;
vt_key[2].string = "StarterType";
startertype = prop_get_integer(bundle, "I<-sis", vt_key);
switch (startertype) {
case 1:
gs_set_event_state(game, index_, ES_WAITING);
gs_set_event_time(game, index_, 0);
break;
case 2: {
sc_int start, end;
gs_set_event_state(game, index_, ES_WAITING);
vt_key[2].string = "StartTime";
start = prop_get_integer(bundle, "I<-sis", vt_key);
vt_key[2].string = "EndTime";
end = prop_get_integer(bundle, "I<-sis", vt_key);
gs_set_event_time(game, index_, sc_randomint(start, end));
break;
}
case 3:
gs_set_event_state(game, index_, ES_AWAITING);
gs_set_event_time(game, index_, 0);
break;
default:
break;
}
}
/* Create NPCs state array. */
vt_key[0].string = "NPCs";
game->npc_count = prop_get_child_count(bundle, "I<-s", vt_key);
game->npcs = (sc_npcstate_t *)sc_malloc(game->npc_count * sizeof(*game->npcs));
/* Set up initial NPCs states. */
for (index_ = 0; index_ < game->npc_count; index_++) {
sc_int walk, walkstep_count;
gs_set_npc_position(game, index_, 0);
gs_set_npc_parent(game, index_, -1);
gs_set_npc_seen(game, index_, FALSE);
vt_key[1].integer = index_;
vt_key[2].string = "StartRoom";
gs_set_npc_location(game, index_,
prop_get_integer(bundle, "I<-sis", vt_key));
vt_key[2].string = "Walks";
walkstep_count = prop_get_child_count(bundle, "I<-sis", vt_key);
game->npcs[index_].walkstep_count = walkstep_count;
game->npcs[index_].walksteps = (sc_int *)sc_malloc(walkstep_count
* sizeof(*game->npcs[0].walksteps));
for (walk = 0; walk < walkstep_count; walk++)
gs_set_npc_walkstep(game, index_, walk, 0);
}
/* Set up the player portions of the game state. */
vt_key[0].string = "Header";
vt_key[1].string = "StartRoom";
game->playerroom = prop_get_integer(bundle, "I<-ss", vt_key);
vt_key[0].string = "Globals";
vt_key[1].string = "ParentObject";
game->playerparent = prop_get_integer(bundle, "I<-ss", vt_key) - 1;
vt_key[1].string = "Position";
game->playerposition = prop_get_integer(bundle, "I<-ss", vt_key);
/* Initialize score notifications from game properties. */
vt_key[0].string = "Globals";
vt_key[1].string = "NoScoreNotify";
game->notify_score_change = !prop_get_boolean(bundle, "B<-ss", vt_key);
/* Miscellaneous state defaults. */
game->turns = 0;
game->score = 0;
game->bold_room_names = TRUE;
game->verbose = FALSE;
game->current_room_name = nullptr;
game->status_line = nullptr;
game->title = nullptr;
game->author = nullptr;
game->hint_text = nullptr;
/* Resource controls. */
res_clear_resource(&game->requested_sound);
res_clear_resource(&game->requested_graphic);
res_clear_resource(&game->playing_sound);
res_clear_resource(&game->displayed_graphic);
game->stop_sound = FALSE;
game->sound_active = FALSE;
/* Initialize wait turns from game properties. */
vt_key[0].string = "Globals";
vt_key[1].string = "WaitTurns";
game->waitturns = prop_get_integer(bundle, "I<-ss", vt_key);
/* Non-game conveniences. */
game->is_running = FALSE;
game->has_notified = FALSE;
game->is_admin = FALSE;
game->has_completed = FALSE;
game->waitcounter = 0;
game->do_again = FALSE;
game->redo_sequence = 0;
game->do_restart = FALSE;
game->do_restore = FALSE;
bytes = game->object_count * sizeof(*game->object_references);
game->object_references = (sc_bool *)sc_malloc(bytes);
memset(game->object_references, FALSE, bytes);
bytes = game->object_count * sizeof(*game->multiple_references);
game->multiple_references = (sc_bool *)sc_malloc(bytes);
memset(game->multiple_references, FALSE, bytes);
bytes = game->npc_count * sizeof(*game->npc_references);
game->npc_references = (sc_bool *)sc_malloc(bytes);
memset(game->npc_references, FALSE, bytes);
game->it_object = -1;
game->him_npc = -1;
game->her_npc = -1;
game->it_npc = -1;
// Return the constructed game state
return game;
}
/*
* gs_is_game_valid()
*
* Return TRUE if pointer is a valid game, FALSE otherwise.
*/
sc_bool gs_is_game_valid(const sc_gameref_t game) {
return game && game->magic == GAME_MAGIC;
}
/*
* gs_string_copy()
*
* Helper for gs_copy(), copies one malloc'ed string to another, or NULL
* if from is NULL, taking care not to leak memory.
*/
static void gs_string_copy(sc_char **to_string, const sc_char *from_string) {
/* Free any current contents of to_string. */
sc_free(*to_string);
/* Copy from_string if set, otherwise set to_string to NULL. */
if (from_string) {
size_t ln = strlen(from_string) + 1;
*to_string = (sc_char *)sc_malloc(ln);
Common::strcpy_s(*to_string, ln, from_string);
} else
*to_string = nullptr;
}
/*
* gs_copy()
*
* Deep-copy the dynamic parts of a game onto another existing
* game structure.
*/
void gs_copy(sc_gameref_t to, sc_gameref_t from) {
const sc_prop_setref_t bundle = from->bundle;
sc_vartype_t vt_key[3];
sc_int var_count, var, npc;
assert(gs_is_game_valid(to) && gs_is_game_valid(from));
/*
* Copy over references to the properties bundle and filter. The debugger
* is specifically excluded, as it's considered to be tied to the game.
*/
to->bundle = from->bundle;
to->filter = from->filter;
/* Copy over references to the undo buffers. */
to->temporary = from->temporary;
to->undo = from->undo;
to->undo_available = from->undo_available;
/* Copy over all variables values. */
vt_key[0].string = "Variables";
var_count = prop_get_child_count(bundle, "I<-s", vt_key);
for (var = 0; var < var_count; var++) {
const sc_char *name;
sc_int var_type;
vt_key[1].integer = var;
vt_key[2].string = "Name";
name = prop_get_string(bundle, "S<-sis", vt_key);
vt_key[2].string = "Type";
var_type = prop_get_integer(bundle, "I<-sis", vt_key);
switch (var_type) {
case TAFVAR_NUMERIC:
var_put_integer(to->vars, name, var_get_integer(from->vars, name));
break;
case TAFVAR_STRING:
var_put_string(to->vars, name, var_get_string(from->vars, name));
break;
default:
sc_fatal("gs_copy: unknown variable type, %ld\n", var_type);
}
}
/* Copy over the variable timestamp. */
var_set_elapsed_seconds(to->vars, var_get_elapsed_seconds(from->vars));
/* Copy over room states. */
assert(to->room_count == from->room_count);
memcpy(to->rooms, from->rooms, from->room_count * sizeof(*from->rooms));
/* Copy over object states. */
assert(to->object_count == from->object_count);
memcpy(to->objects, from->objects,
from->object_count * sizeof(*from->objects));
/* Copy over task states. */
assert(to->task_count == from->task_count);
memcpy(to->tasks, from->tasks, from->task_count * sizeof(*from->tasks));
/* Copy over event states. */
assert(to->event_count == from->event_count);
memcpy(to->events, from->events, from->event_count * sizeof(*from->events));
/* Copy over NPC states individually, to avoid walks problems. */
for (npc = 0; npc < from->npc_count; npc++) {
to->npcs[npc].location = from->npcs[npc].location;
to->npcs[npc].position = from->npcs[npc].position;
to->npcs[npc].parent = from->npcs[npc].parent;
to->npcs[npc].seen = from->npcs[npc].seen;
to->npcs[npc].walkstep_count = from->npcs[npc].walkstep_count;
/* Copy over NPC walks information. */
assert(to->npcs[npc].walkstep_count == from->npcs[npc].walkstep_count);
memcpy(to->npcs[npc].walksteps, from->npcs[npc].walksteps,
from->npcs[npc].walkstep_count
* sizeof(*from->npcs[npc].walksteps));
}
/* Copy over player information. */
to->playerroom = from->playerroom;
to->playerposition = from->playerposition;
to->playerparent = from->playerparent;
/*
* Copy over miscellaneous other details. Specifically exclude bold rooms,
* verbose, and score notification, so that they are invariant across copies,
* particularly undo/restore.
*/
to->turns = from->turns;
to->score = from->score;
gs_string_copy(&to->current_room_name, from->current_room_name);
gs_string_copy(&to->status_line, from->status_line);
gs_string_copy(&to->title, from->title);
gs_string_copy(&to->author, from->author);
gs_string_copy(&to->hint_text, from->hint_text);
/*
* Specifically exclude playing sound and displayed graphic from the copy
* so that they remain invariant across game copies.
*/
to->requested_sound = from->requested_sound;
to->requested_graphic = from->requested_graphic;
to->stop_sound = from->stop_sound;
to->is_running = from->is_running;
to->has_notified = from->has_notified;
to->is_admin = from->is_admin;
to->has_completed = from->has_completed;
to->waitturns = from->waitturns;
to->waitcounter = from->waitcounter;
to->do_again = from->do_again;
to->redo_sequence = from->redo_sequence;
to->do_restart = from->do_restart;
to->do_restore = from->do_restore;
memcpy(to->object_references, from->object_references,
from->object_count * sizeof(*from->object_references));
memcpy(to->multiple_references, from->multiple_references,
from->object_count * sizeof(*from->multiple_references));
memcpy(to->npc_references, from->npc_references,
from->npc_count * sizeof(*from->npc_references));
to->it_object = from->it_object;
to->him_npc = from->him_npc;
to->her_npc = from->her_npc;
to->it_npc = from->it_npc;
}
/*
* gs_destroy()
*
* Free all the memory associated with a game state.
*/
void gs_destroy(sc_gameref_t game) {
sc_int npc;
assert(gs_is_game_valid(game));
/* Free the malloc'ed state arrays. */
sc_free(game->rooms);
sc_free(game->objects);
sc_free(game->tasks);
sc_free(game->events);
for (npc = 0; npc < game->npc_count; npc++)
sc_free(game->npcs[npc].walksteps);
sc_free(game->npcs);
/* Free the malloc'ed object and NPC references. */
sc_free(game->object_references);
sc_free(game->multiple_references);
sc_free(game->npc_references);
/* Free malloc'ed game strings. */
sc_free(game->current_room_name);
sc_free(game->status_line);
sc_free(game->title);
sc_free(game->author);
sc_free(game->hint_text);
/* Poison and free the game state itself. */
memset(game, 0xaa, sizeof(*game));
sc_free(game);
}
} // End of namespace Adrift
} // End of namespace Glk