mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
1001 lines
30 KiB
C++
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
|