scummvm/engines/twp/genlib.cpp
2024-12-10 21:52:42 +01:00

1116 lines
32 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 "common/config-manager.h"
#include "common/crc.h"
#include "twp/twp.h"
#include "twp/detection.h"
#include "twp/dialog.h"
#include "twp/hud.h"
#include "twp/resmanager.h"
#include "twp/room.h"
#include "twp/sqgame.h"
#include "twp/squtil.h"
#include "twp/squirrel/sqvm.h"
#include "twp/squirrel/sqstring.h"
#include "twp/squirrel/sqstate.h"
#include "twp/squirrel/sqtable.h"
#include "twp/squirrel/sqfuncproto.h"
#include "twp/squirrel/sqclosure.h"
#include "twp/tsv.h"
namespace Twp {
struct GetArray {
explicit GetArray(Common::Array<HSQOBJECT> &objs) : _objs(objs) {}
void operator()(HSQOBJECT &o) {
_objs.push_back(o);
}
private:
Common::Array<HSQOBJECT> &_objs;
};
template<typename T>
static void shuffle(Common::Array<T> &array) {
if (array.size() > 1) {
for (size_t i = 0; i < array.size(); i++) {
size_t j = g_twp->getRandomSource().getRandomNumberRng(0, array.size() - 1);
SWAP(array[j], array[i]);
}
}
}
static SQInteger activeVerb(HSQUIRRELVM v) {
sqpush(v, g_twp->_hud->_verb.id.id);
return 1;
}
static SQInteger adhocalytics(HSQUIRRELVM v) {
warning("adhocalytics not implemented");
return 0;
}
static SQInteger arrayShuffle(HSQUIRRELVM v) {
if (sq_gettype(v, 2) != OT_ARRAY)
return sq_throwerror(v, "An array is expected");
HSQOBJECT obj;
sq_getstackobj(v, 2, &obj);
Common::Array<HSQOBJECT> arr;
GetArray g(arr);
sqgetitems(obj, g);
shuffle(arr);
sq_newarray(v, 0);
for (auto &it : arr) {
sqpush(v, it);
sq_arrayappend(v, -2);
}
return 1;
}
// Returns TRUE if the specified entry exists in the assets.
static SQInteger assetExists(HSQUIRRELVM v) {
const SQChar *filename;
if (SQ_FAILED(sq_getstring(v, 2, &filename)))
return sq_throwerror(v, "failed to get filename");
sqpush(v, g_twp->_pack->assetExists(filename));
return 1;
}
// Moves the camera to the specified x, y location.
// If a spot is specified, will move to the x, y specified by that spot.
// .. code-block:: Squirrel
// cameraAt(450, 128)
//
// enterRoomFromDoor(Bridge.startRight)
// actorAt(ray, Bridge.startLeft)
// actorAt(reyes, Bridge.startRight)
// cameraAt(Bridge.bridgeBody)
static SQInteger cameraAt(HSQUIRRELVM v) {
SQInteger numArgs = sq_gettop(v);
Math::Vector2d pos;
if (numArgs == 3) {
SQInteger x, y;
if (SQ_FAILED(sqget(v, 2, x)))
return sq_throwerror(v, "failed to get x");
if (SQ_FAILED(sqget(v, 3, y)))
return sq_throwerror(v, "failed to get y");
pos = Math::Vector2d(x, y);
} else if (numArgs == 2) {
Common::SharedPtr<Object> obj = sqobj(v, 2);
if (!obj)
return sq_throwerror(v, "failed to get spot or actor");
g_twp->follow(nullptr);
g_twp->setRoom(obj->_room);
pos = obj->getUsePos();
} else {
return sq_throwerror(v, Common::String::format("invalid argument number: %lld", numArgs).c_str());
}
g_twp->follow(nullptr);
g_twp->cameraAt(pos);
return 0;
}
// Sets how far the camera can pan.
static SQInteger cameraBounds(HSQUIRRELVM v) {
SQInteger xMin, xMax, yMin, yMax;
if (SQ_FAILED(sqget(v, 2, xMin)))
return sq_throwerror(v, "failed to get xMin");
if (SQ_FAILED(sqget(v, 3, xMax)))
return sq_throwerror(v, "failed to get xMax");
if (SQ_FAILED(sqget(v, 4, yMin)))
return sq_throwerror(v, "failed to get yMin");
if (SQ_FAILED(sqget(v, 5, yMax)))
return sq_throwerror(v, "failed to get yMax");
g_twp->_camera->setBounds(Rectf::fromMinMax(Math::Vector2d(xMin, yMin), Math::Vector2d(xMax, yMax)));
return 0;
}
static SQInteger cameraFollow(HSQUIRRELVM v) {
Common::SharedPtr<Object> actor = sqactor(v, 2);
g_twp->follow(actor);
Math::Vector2d pos = actor->_node->getPos();
Common::SharedPtr<Room> oldRoom = g_twp->_room;
if (actor->_room)
g_twp->setRoom(actor->_room);
if (oldRoom != actor->_room)
g_twp->cameraAt(pos);
return 0;
}
// Moves the camera to the specified room.
//
// Does not move any of the actors.
//
// .. code-block:: Squirrel
// aStreetPhoneBook =
// {
// name = "phone book"
// verbLookAt = function()
// {
// cameraInRoom(PhoneBook)
// }
// }
static SQInteger cameraInRoom(HSQUIRRELVM v) {
Common::SharedPtr<Room> room = sqroom(v, 2);
if (room) {
g_twp->setRoom(room);
} else {
Common::SharedPtr<Object> obj = sqobj(v, 2);
if (!obj || !obj->_room) {
return sq_throwerror(v, "failed to get room");
}
g_twp->setRoom(obj->_room);
}
return 0;
}
// Pans the camera to the specified x, y location over the duration using the transition method.
// Transition methods are: EASE_IN, EASE_INOUT, EASE_OUT, LINEAR, SLOW_EASE_IN, SLOW_EASE_OUT.
//
// .. code-block:: Squirrel
// cameraPanTo(450, 128, pan_time, EASE_INOUT)
// inputOff()
// actorWalkTo(currentActor, Highway.detectiveSpot1)
// breakwhilewalking(currentActor)
// cameraPanTo(currentActor, 2.0)
static SQInteger cameraPanTo(HSQUIRRELVM v) {
SQInteger numArgs = sq_gettop(v);
Math::Vector2d pos;
float duration = 0.f;
InterpolationKind interpolation = IK_LINEAR;
if (numArgs == 3) {
Common::SharedPtr<Object> obj = sqobj(v, 2);
if (!obj)
return sq_throwerror(v, "failed to get object/actor");
pos = obj->getUsePos();
if (SQ_FAILED(sqget(v, 3, duration)))
return sq_throwerror(v, "failed to get duration");
} else if (numArgs == 4) {
if (sq_gettype(v, 2) == OT_INTEGER) {
SQInteger x;
if (SQ_FAILED(sqget(v, 2, x)))
return sq_throwerror(v, "failed to get x");
if (SQ_FAILED(sqget(v, 3, duration)))
return sq_throwerror(v, "failed to get duration");
SQInteger im;
if (SQ_FAILED(sqget(v, 4, im)))
return sq_throwerror(v, "failed to get interpolation method");
pos = Math::Vector2d(x, g_twp->getGfx().cameraPos().getY());
interpolation = (InterpolationKind)im;
} else {
Common::SharedPtr<Object> obj = sqobj(v, 2);
if (SQ_FAILED(sqget(v, 3, duration)))
return sq_throwerror(v, "failed to get duration");
SQInteger im;
if (SQ_FAILED(sqget(v, 4, im)))
return sq_throwerror(v, "failed to get interpolation method");
pos = obj->_node->getAbsPos();
interpolation = (InterpolationKind)im;
}
} else if (numArgs == 5) {
SQInteger x, y;
if (SQ_FAILED(sqget(v, 2, x)))
return sq_throwerror(v, "failed to get x");
if (SQ_FAILED(sqget(v, 3, y)))
return sq_throwerror(v, "failed to get y");
if (SQ_FAILED(sqget(v, 4, duration)))
return sq_throwerror(v, "failed to get duration");
SQInteger im;
if (SQ_FAILED(sqget(v, 5, im)))
return sq_throwerror(v, "failed to get interpolation method");
pos = Math::Vector2d(x, y);
interpolation = (InterpolationKind)im;
} else {
return sq_throwerror(v, Common::String::format("invalid argument number: %lld", numArgs).c_str());
}
debugC(kDebugGenScript, "cameraPanTo: (%f,%f), dur=%f, method=%d", pos.getX(), pos.getY(), duration, interpolation);
g_twp->follow(nullptr);
g_twp->_camera->panTo(pos, duration, interpolation);
return 0;
}
// Returns the current camera position x, y.
static SQInteger cameraPos(HSQUIRRELVM v) {
sqpush(v, g_twp->cameraPos());
return 1;
}
// Converts an integer to a char.
static SQInteger sqChr(HSQUIRRELVM v) {
SQInteger value;
if (SQ_FAILED(sqget(v, 2, value)))
return sq_throwerror(v, "failed to get value");
Common::String s;
s += char(value);
sqpush(v, Common::move(s));
return 1;
}
// Returns x coordinates of the mouse in screen coordinates.
static SQInteger cursorPosX(HSQUIRRELVM v) {
sqpush(v, g_twp->_cursor.pos.getX());
return 1;
}
// Returns y coordinates of the mouse in screen coordinates.
static SQInteger cursorPosY(HSQUIRRELVM v) {
sqpush(v, g_twp->_cursor.pos.getY());
return 1;
}
static SQInteger distance(HSQUIRRELVM v) {
if (sq_gettype(v, 2) == OT_INTEGER) {
SQInteger num1;
if (SQ_FAILED(sqget(v, 2, num1)))
return sq_throwerror(v, "failed to get num1");
SQInteger num2;
if (SQ_FAILED(sqget(v, 3, num2)))
return sq_throwerror(v, "failed to get num2");
float d = abs(num1 - num2);
sqpush(v, d);
return 1;
}
Common::SharedPtr<Object> obj1 = sqobj(v, 2);
if (!obj1)
return sq_throwerror(v, "failed to get object1 or actor1");
Common::SharedPtr<Object> obj2 = sqobj(v, 3);
if (!obj2)
return sq_throwerror(v, "failed to get object2 or actor2");
Math::Vector2d d = obj1->_node->getAbsPos() - obj2->_node->getAbsPos();
sqpush(v, sqrt(d.getX() * d.getX() + d.getY() * d.getY()));
return 1;
}
static SQInteger findScreenPosition(HSQUIRRELVM v) {
if (sq_gettype(v, 2) == OT_INTEGER) {
SQInteger verb;
if (SQ_FAILED(sqget(v, 2, verb)))
return sq_throwerror(v, "failed to get verb");
ActorSlot *actorSlot = g_twp->_hud->actorSlot(g_twp->_actor);
if (!actorSlot)
return 0;
for (int i = 1; i < MAX_VERBS; i++) {
const Verb &vb = actorSlot->verbSlots[i]._verb;
if (vb.id.id == verb) {
SpriteSheet *verbSheet = g_twp->_resManager->spriteSheet("VerbSheet");
const SpriteSheetFrame *verbFrame = &verbSheet->getFrame(Common::String::format("%s_en", vb.image.c_str()));
Math::Vector2d pos(verbFrame->spriteSourceSize.left + verbFrame->frame.width() / 2.f, verbFrame->sourceSize.getY() - verbFrame->spriteSourceSize.top - verbFrame->spriteSourceSize.height() + verbFrame->frame.height() / 2.f);
debugC(kDebugGenScript, "findScreenPosition(%lld) => %f,%f", verb, pos.getX(), pos.getY());
sqpush(v, Common::move(pos));
return 1;
}
}
return sq_throwerror(v, "failed to find verb");
}
Common::SharedPtr<Object> obj = sqobj(v, 2);
if (!obj)
return sq_throwerror(v, "failed to get object or actor");
if (obj->inInventory()) {
sqpush(v, g_twp->_uiInv.getPos(obj));
return 1;
}
Math::Vector2d rPos = g_twp->roomToScreen(obj->_node->getAbsPos());
Math::Vector2d pos(rPos.getX() + obj->_node->getSize().getX() / 2.f, rPos.getY() + obj->_node->getSize().getY() / 2.f);
debugC(kDebugGenScript, "findScreenPosition(%s) => (%f,%f)", obj->_name.c_str(), pos.getX(), pos.getY());
sqpush(v, Common::move(pos));
return 1;
}
static SQInteger frameCounter(HSQUIRRELVM v) {
return sqpush(v, g_twp->_frameCounter);
}
static Common::String toPlatform(const Common::String &value) {
const char *platformsSrc[] = {"xbox", "switch", "playstation", "playstation2"};
const char *platformsDst[] = {"XBOX", "SWITCH", "PS4", "PS4"};
for (int i = 0; i < ARRAYSIZE(platformsSrc); i++) {
if (value == platformsSrc[i]) {
return platformsDst[i];
}
}
return "0";
}
static SQInteger getUserPref(HSQUIRRELVM v) {
Common::String key;
if (SQ_FAILED(sqget(v, 2, key))) {
return sq_throwerror(v, _SC("failed to get key"));
}
if (key == "platform" && ConfMan.hasKey(key)) {
sqpush(v, toPlatform(ConfMan.get(key)));
return 1;
}
SQInteger numArgs = sq_gettop(v) - 1;
// is there a fault value as argument ?
if (numArgs > 1) {
SQObjectType type = sq_gettype(v, 3);
if (type == SQObjectType::OT_STRING) {
Common::String str;
if (SQ_FAILED(sqget(v, 3, str))) {
return sq_throwerror(v, _SC("failed to get string"));
}
sqpush(v, ConfMan.hasKey(key) ? ConfMan.get(key) : str);
return 1;
}
if (type == SQObjectType::OT_INTEGER) {
SQInteger integer;
if (SQ_FAILED(sqget(v, 3, integer))) {
return sq_throwerror(v, _SC("failed to get integer"));
}
if (ConfMan.hasKey(key)) {
Common::String value = ConfMan.get(key);
bool bValue = false;
if (parseBool(value, bValue)) {
sqpush(v, bValue);
return 1;
}
sqpush(v, ConfMan.getInt(key));
return 1;
}
sqpush(v, integer);
return 1;
}
if (type == SQObjectType::OT_FLOAT) {
SQFloat fl;
if (SQ_FAILED(sqget(v, 3, fl))) {
return sq_throwerror(v, _SC("failed to get float"));
}
sqpush(v, ConfMan.hasKey(key) ? atof(ConfMan.get(key).c_str()) : fl);
return 1;
}
return sq_throwerror(v, _SC("failed to get user preferences"));
}
if (ConfMan.hasKey(key)) {
Common::String value = ConfMan.get(key);
bool isNumber = !value.empty();
bool isFloat = false;
for (size_t i = 0; i < value.size(); i++) {
if (!isFloat && value[i] == '.') {
isFloat = true;
continue;
}
if (!Common::isDigit(value[i])) {
isNumber = false;
break;
}
}
if (!isNumber) {
sqpush(v, value);
return 1;
}
if (isFloat) {
sqpush(v, atof(value.c_str()));
return 1;
}
sqpush(v, (SQInteger)atol(value.c_str()));
return 1;
}
sq_pushnull(v);
return 1;
}
static SQInteger getPrivatePref(HSQUIRRELVM v) {
Common::String key;
if (SQ_FAILED(sqget(v, 2, key))) {
return sq_throwerror(v, "failed to get key");
} else if (ConfMan.hasKey(key)) {
Common::String value = ConfMan.get(key);
char *errpos;
int ivalue = (int)strtol(value.c_str(), &errpos, 0);
if (value.c_str() == errpos) {
return sqpush(v, ConfMan.get(key));
}
return sqpush(v, ivalue);
} else if (sq_gettop(v) == 3) {
HSQOBJECT obj;
sq_getstackobj(v, 3, &obj);
sqpush(v, obj);
return 1;
}
sq_pushnull(v);
return 1;
}
static SQInteger incutscene(HSQUIRRELVM v) {
sqpush(v, g_twp->_cutscene != nullptr);
return 1;
}
static SQInteger indialog(HSQUIRRELVM v) {
sqpush(v, (int)g_twp->_dialog->getState());
return 1;
}
static SQInteger integer(HSQUIRRELVM v) {
float f = 0.f;
if (SQ_FAILED(sqget(v, 2, f)))
return sq_throwerror(v, "failed to get float value");
sqpush(v, static_cast<int>(f));
return 1;
}
static SQInteger in_array(HSQUIRRELVM v) {
HSQOBJECT obj;
sq_resetobject(&obj);
if (SQ_FAILED(sqget(v, 2, obj)))
return sq_throwerror(v, "Failed to get object");
HSQOBJECT arr;
sq_resetobject(&arr);
if (SQ_FAILED(sqget(v, 3, arr)))
return sq_throwerror(v, "Failed to get array");
Common::Array<HSQOBJECT> objs;
sq_pushobject(v, arr);
sq_pushnull(v); // null iterator
while (SQ_SUCCEEDED(sq_next(v, -2))) {
HSQOBJECT tmp;
sq_getstackobj(v, -1, &tmp);
objs.push_back(tmp);
sq_pop(v, 2); // pops key and val before the nex iteration
}
sq_pop(v, 1); // pops the null iterator
for (auto &it : objs) {
sq_pushobject(v, obj);
sq_pushobject(v, it);
if (sq_cmp(v) == 0) {
sq_pop(v, 2);
sqpush(v, 1);
return 1;
}
sq_pop(v, 2);
}
sq_pushinteger(v, 0);
return 1;
}
static SQInteger is_array(HSQUIRRELVM v) {
return sqpush(v, sq_gettype(v, 2) == OT_ARRAY);
}
static SQInteger is_function(HSQUIRRELVM v) {
SQObjectType type = sq_gettype(v, 2);
return sqpush(v, type == OT_CLOSURE || type == OT_NATIVECLOSURE);
}
static SQInteger is_string(HSQUIRRELVM v) {
return sqpush(v, sq_gettype(v, 2) == OT_STRING);
}
static SQInteger is_table(HSQUIRRELVM v) {
return sqpush(v, sq_gettype(v, 2) == OT_TABLE);
}
// Returns a random number from from to to inclusively.
// The number is a pseudo-random number and the game will produce the same sequence of numbers unless primed using randomSeed(seed).
//
// .. code-block:: Squirrel
// wait_time = random(0.5, 2.0)
static SQInteger sqrandom(HSQUIRRELVM v) {
if (sq_gettype(v, 2) == OT_FLOAT || sq_gettype(v, 3) == OT_FLOAT) {
SQFloat min, max;
sq_getfloat(v, 2, &min);
sq_getfloat(v, 3, &max);
if (min > max)
SWAP(min, max);
SQFloat value = g_twp->getRandom(min, max);
sq_pushfloat(v, value);
} else {
SQInteger min, max;
sq_getinteger(v, 2, &min);
sq_getinteger(v, 3, &max);
if (min > max)
SWAP(min, max);
SQInteger value = g_twp->getRandomSource().getRandomNumberRngSigned(min, max);
sq_pushinteger(v, value);
}
return 1;
}
// Returns an array of all the lines of the given `filename`.
static SQInteger loadArray(HSQUIRRELVM v) {
const SQChar *orgFilename = nullptr;
if (SQ_FAILED(sqget(v, 2, orgFilename)))
return sq_throwerror(v, "failed to get filename");
debugC(kDebugGenScript, "loadArray: %s", orgFilename);
Common::String filename = ResManager::getKey(orgFilename);
GGPackEntryReader entry;
entry.open(*g_twp->_pack, g_twp->_pack->assetExists(filename.c_str()) ? Common::move(filename) : orgFilename);
sq_newarray(v, 0);
while (!entry.eos()) {
Common::String line = entry.readLine();
sq_pushstring(v, line.c_str(), -1);
sq_arrayappend(v, -2);
}
return 1;
}
static SQInteger markAchievement(HSQUIRRELVM v) {
Common::String id;
if (SQ_FAILED(sqget(v, 2, id)))
return sq_throwerror(v, "failed to get id");
SQInteger numArgs = sq_gettop(v);
switch (numArgs) {
case 2:
AchMan.setAchievement(id);
break;
case 4: {
SQInteger count, total;
if (SQ_FAILED(sqget(v, 3, count)))
return sq_throwerror(v, "failed to get count");
if (SQ_FAILED(sqget(v, 4, total)))
return sq_throwerror(v, "failed to get total");
AchMan.setStatInt(Common::String::format("ST%s", id.substr(3).c_str()), count);
if (count == total) {
AchMan.setAchievement(id);
}
} break;
default:
error("TODO: markAchievement not implemented");
break;
}
return 0;
}
static SQInteger markProgress(HSQUIRRELVM v) {
warning("TODO: markProgress not implemented");
return 0;
}
static SQInteger markStat(HSQUIRRELVM v) {
warning("TODO: markStat not implemented");
return 0;
}
// Returns the internal int value of x
static SQInteger ord(HSQUIRRELVM v) {
Common::String letter;
if (SQ_FAILED(sqget(v, 2, letter)))
return sq_throwerror(v, "Failed to get letter");
if (letter.size() > 0) {
sq_pushinteger(v, (int)(letter[0]));
} else {
sq_pushinteger(v, 0);
}
return 1;
}
// Executes a verb sentence as though the player had inputted/constructed it themselves.
// You can push several sentences one after the other.
// They will execute in reverse order (it's a stack).
static SQInteger pushSentence(HSQUIRRELVM v) {
SQInteger nArgs = sq_gettop(v);
SQInteger id;
if (SQ_FAILED(sqget(v, 2, id)))
return sq_throwerror(v, "Failed to get verb id");
if (id == VERB_DIALOG) {
SQInteger choice;
if (SQ_FAILED(sqget(v, 3, choice)))
return sq_throwerror(v, "Failed to get choice");
// use pushSentence with VERB_DIALOG
g_twp->_dialog->choose(choice);
return 0;
}
Common::SharedPtr<Object> obj1;
Common::SharedPtr<Object> obj2;
if (nArgs >= 3) {
obj1 = sqobj(v, 3);
if (!obj1)
return sq_throwerror(v, "Failed to get obj1");
}
if (nArgs == 4) {
obj2 = sqobj(v, 4);
if (!obj2)
return sq_throwerror(v, "Failed to get obj2");
}
VerbId verb;
verb.id = id;
g_twp->execSentence(nullptr, verb, obj1, obj2);
return 0;
}
// Selects an item randomly from the given array or listed options.
//
// .. code-block:: Squirrel
// local line = randomfrom(lines)
// breakwhiletalking(willie)
// mumbleLine(willie, line)
//
// local snd = randomfrom(soundBeep1, soundBeep2, soundBeep3, soundBeep4, soundBeep5, soundBeep6)
// playObjectSound(snd, Highway.pigeonVan)
static SQInteger randomFrom(HSQUIRRELVM v) {
if (sq_gettype(v, 2) == OT_ARRAY) {
HSQOBJECT obj;
sq_resetobject(&obj);
SQInteger size = sq_getsize(v, 2);
assert(size > 0);
int index = g_twp->getRandomSource().getRandomNumber(size - 1);
assert(index >= 0);
int i = 0;
sq_push(v, 2); // array
sq_pushnull(v); // null iterator
while (SQ_SUCCEEDED(sq_next(v, -2))) {
sq_getstackobj(v, -1, &obj);
sq_pop(v, 2); // pops key and val before the nex iteration
if (index == i) {
sq_pop(v, 2); // pops the null iterator and array
sq_pushobject(v, obj);
return 1;
}
i++;
}
sq_pop(v, 2); // pops the null iterator and array
sq_pushobject(v, obj);
} else {
SQInteger size = sq_gettop(v);
int index = g_twp->getRandomSource().getRandomNumber(size - 2);
assert(index >= 0);
sq_push(v, 2 + index);
}
return 1;
}
// Returns TRUE or FALSE based on the percent, which needs to be from 0.0 to 1.0.
//
// A percent of 0.0 will always return FALSE and 1.0 will always return TRUE.
// `randomOdds(0.3333)` will return TRUE about one third of the time.
//
// .. code-block:: Squirrel
// if (randomOdds(0.5) { ... }
static SQInteger randomOdds(HSQUIRRELVM v) {
float value = 0.0f;
if (SQ_FAILED(sqget(v, 2, value)))
return sq_throwerror(v, "failed to get value");
float rnd = g_twp->getRandom();
bool res = rnd <= value;
sq_pushbool(v, res);
return 1;
}
// Initializes a new Rand state using the given seed.
// Providing a specific seed will produce the same results for that seed each time.
// The resulting state is independent of the default RNG's state.
static SQInteger randomseed(HSQUIRRELVM v) {
SQInteger nArgs = sq_gettop(v);
switch (nArgs) {
case 1: {
sqpush(v, (int)g_twp->getRandomSource().getSeed());
return 1;
}
case 2: {
SQInteger seed = 0;
if (sq_gettype(v, 2) == OT_NULL) {
g_twp->getRandomSource().setSeed(g_twp->getRandomSource().generateNewSeed());
return 0;
}
if (SQ_FAILED(sqget(v, 2, seed))) {
return sq_throwerror(v, "failed to get seed");
}
g_twp->getRandomSource().setSeed(seed);
return 0;
}
}
return sq_throwerror(v, "invalid number of parameters for randomseed");
}
static SQInteger refreshUI(HSQUIRRELVM v) {
// TODO: refreshUI
warning("refreshUI not implemented");
return 0;
}
// Returns the x and y dimensions of the current screen/window.
//
// .. code-block:: Squirrel
// function clickedAt(x,y) {
// local screenHeight = screenSize().y
// local exitButtonB = screenHeight - (exitButtonPadding + 16)
// if (y > exitButtonB) { ... }
// }
static SQInteger screenSize(HSQUIRRELVM v) {
Math::Vector2d screen = g_twp->_room->getScreenSize();
sqpush(v, Common::move(screen));
return 1;
}
static SQInteger setDebugger(HSQUIRRELVM v) {
// TODO: setDebugger
warning("setDebugger not implemented");
return 0;
}
static SQInteger setPrivatePref(HSQUIRRELVM v) {
Common::String key;
if (SQ_FAILED(sqget(v, 2, key))) {
return sq_throwerror(v, _SC("failed to get key"));
}
SQObjectType type = sq_gettype(v, 3);
switch (type) {
case SQObjectType::OT_STRING: {
Common::String str;
if (SQ_FAILED(sqget(v, 3, str))) {
return sq_throwerror(v, _SC("failed to get str"));
}
ConfMan.set(key, str);
return 0;
}
case SQObjectType::OT_INTEGER:
SQInteger integer;
if (SQ_FAILED(sqget(v, 3, integer))) {
return sq_throwerror(v, _SC("failed to get integer"));
}
ConfMan.setInt(key, (int)integer);
return 0;
default:
break;
}
warning("setPrivatePref not implemented");
return 0;
}
static SQInteger setUserPref(HSQUIRRELVM v) {
Common::String key;
if (SQ_FAILED(sqget(v, 2, key))) {
return sq_throwerror(v, _SC("failed to get key"));
}
SQObjectType type = sq_gettype(v, 3);
switch (type) {
case SQObjectType::OT_STRING: {
Common::String str;
if (SQ_FAILED(sqget(v, 3, str))) {
return sq_throwerror(v, _SC("failed to get str"));
}
ConfMan.set(key, str);
return 0;
}
case SQObjectType::OT_INTEGER:
SQInteger integer;
if (SQ_FAILED(sqget(v, 3, integer))) {
return sq_throwerror(v, _SC("failed to get integer"));
}
ConfMan.setInt(key, (int)integer);
return 0;
default:
break;
}
warning("setUserPref not implemented");
return 0;
}
static SQInteger setVerb(HSQUIRRELVM v) {
SQInteger actorSlot;
if (SQ_FAILED(sqget(v, 2, actorSlot)))
return sq_throwerror(v, "failed to get actor slot");
SQInteger verbSlot;
if (SQ_FAILED(sqget(v, 3, verbSlot)))
return sq_throwerror(v, "failed to get verb slot");
HSQOBJECT table;
if (SQ_FAILED(sqget(v, 4, table)))
return sq_throwerror(v, "failed to get verb definitionTable");
if (!sq_istable(table))
return sq_throwerror(v, "verb definitionTable is not a table");
SQInteger id = 0;
Common::String image;
Common::String text;
Common::String fun;
Common::String key;
SQInteger flags = 0;
if (SQ_FAILED(sqgetf(table, "verb", id)))
return sq_throwerror(v, "failed to get verb");
if (SQ_FAILED(sqgetf(table, "text", text)))
return sq_throwerror(v, "failed to get verb text");
if (sqrawexists(table, "image") && SQ_FAILED(sqgetf(table, "image", image)))
return sq_throwerror(v, "failed to get verb image");
if (sqrawexists(table, "func") && SQ_FAILED(sqgetf(table, "func", fun)))
return sq_throwerror(v, "failed to get verb func");
if (sqrawexists(table, "key") && SQ_FAILED(sqgetf(table, "key", key)))
return sq_throwerror(v, "failed to get verb key");
if (sqrawexists(table, "flags") && SQ_FAILED(sqgetf(table, "flags", flags)))
return sq_throwerror(v, "failed to get verb flags");
debugC(kDebugGenScript, "setVerb %lld, %lld, %lld, %s", actorSlot, verbSlot, id, text.c_str());
VerbId verbId;
verbId.id = id;
g_twp->_hud->_actorSlots[actorSlot - 1].verbSlots[verbSlot]._verb = Verb(verbId, image, fun, text, key, flags);
return 0;
}
static SQInteger startDialog(HSQUIRRELVM v) {
SQInteger nArgs = sq_gettop(v);
Common::String dialog;
if (SQ_FAILED(sqget(v, 2, dialog)))
return sq_throwerror(v, "failed to get dialog");
Common::String node = "start";
if (nArgs == 3) {
if (SQ_FAILED(sqget(v, 3, node))) {
return sq_throwerror(v, "failed to get node");
}
}
Common::String actor = g_twp->_actor ? g_twp->_actor->_key : "";
g_twp->_dialog->start(actor, dialog, node);
return 0;
}
static SQInteger stopSentence(HSQUIRRELVM v) {
SQInteger nArgs = sq_gettop(v);
switch (nArgs) {
case 1: {
for (auto layer : g_twp->_room->_layers) {
for (auto obj : layer->_objects) {
obj->_exec.enabled = false;
}
}
break;
case 2: {
Common::SharedPtr<Object> obj = sqobj(v, 2);
obj->_exec.enabled = false;
break;
}
default:
warning("stopSentence not implemented with %lld arguments", nArgs);
break;
}
}
return 0;
}
// Counts the occurrences of a substring sub in the string `str`.
static SQInteger strcount(HSQUIRRELVM v) {
const char *str;
const char *sub;
if (SQ_FAILED(sqget(v, 2, str)))
return sq_throwerror(v, "Failed to get str");
if (SQ_FAILED(sqget(v, 3, sub)))
return sq_throwerror(v, "Failed to get sub");
int count = 0;
while ((str = strstr(str, sub))) {
str += strlen(sub);
++count;
}
sq_pushinteger(v, count);
return 1;
}
static SQInteger strcrc(HSQUIRRELVM v) {
Common::CRC32 crc;
const SQChar *str;
if (SQ_FAILED(sq_getstring(v, 2, &str))) {
return sq_throwerror(v, _SC("failed to get string"));
}
uint32 u = crc.crcFast((const byte *)str, strlen(str));
sq_pushinteger(v, (SQInteger)u);
return 1;
}
static SQInteger strfind(HSQUIRRELVM v) {
const SQChar *str1;
if (SQ_FAILED(sq_getstring(v, 2, &str1))) {
return sq_throwerror(v, _SC("failed to get string1"));
}
const SQChar *str2;
if (SQ_FAILED(sq_getstring(v, 3, &str2))) {
return sq_throwerror(v, _SC("failed to get string1"));
}
const char *p = strstr(str1, str2);
if (p == nullptr) {
sq_pushinteger(v, -1);
} else {
sq_pushinteger(v, p - str1);
}
return 1;
}
static SQInteger strfirst(HSQUIRRELVM v) {
const SQChar *str;
if (SQ_FAILED(sq_getstring(v, 2, &str))) {
return sq_throwerror(v, _SC("failed to get string"));
}
if (strlen(str) > 0) {
const SQChar s[2]{str[0], '\0'};
sq_pushstring(v, s, 1);
return 1;
}
sq_pushnull(v);
return 1;
}
static SQInteger strlast(HSQUIRRELVM v) {
const SQChar *str;
if (SQ_FAILED(sq_getstring(v, 2, &str))) {
return sq_throwerror(v, _SC("failed to get string"));
}
auto len = strlen(str);
if (len > 0) {
const SQChar s[2]{str[len - 1], '\0'};
sq_pushstring(v, s, 1);
return 1;
}
sq_pushnull(v);
return 1;
}
static SQInteger strlines(HSQUIRRELVM v) {
Common::String text;
if (SQ_FAILED(sqget(v, 2, text))) {
return sq_throwerror(v, _SC("failed to get text"));
}
Common::String line;
Common::MemoryReadStream ms((const byte *)text.c_str(), text.size());
sq_newarray(v, 0);
while (!ms.eos()) {
line = ms.readLine();
sq_pushstring(v, line.c_str(), line.size());
sq_arrayappend(v, -2);
}
return 1;
}
static SQInteger strreplace(HSQUIRRELVM v) {
const SQChar *input;
const SQChar *search;
const SQChar *replace;
if (SQ_FAILED(sq_getstring(v, 2, &input))) {
return sq_throwerror(v, _SC("failed to get input"));
}
if (SQ_FAILED(sq_getstring(v, 3, &search))) {
return sq_throwerror(v, _SC("failed to get search"));
}
if (SQ_FAILED(sq_getstring(v, 4, &replace))) {
return sq_throwerror(v, _SC("failed to get replace"));
}
Common::String strInput(input);
Common::String strSearch(search);
Common::String strReplace(replace);
strInput = replaceAll(strInput, strSearch, strReplace);
sq_pushstring(v, strInput.c_str(), strInput.size());
return 1;
}
// Splits the string `str` into substrings using a string separator.
static SQInteger strsplit(HSQUIRRELVM v) {
Common::String str;
const SQChar *delimiter;
if (SQ_FAILED(sqget(v, 2, str)))
return sq_throwerror(v, "Failed to get str");
if (SQ_FAILED(sqget(v, 3, delimiter)))
return sq_throwerror(v, "Failed to get delimiter");
sq_newarray(v, 0);
size_t delLen = strlen(delimiter);
if (delLen == 0)
return 1;
size_t len = str.size();
char *s = str.begin();
size_t result = strcspn(s, delimiter);
while (result != len) {
sq_pushstring(v, s, result);
sq_arrayappend(v, -2);
s += (result + delLen);
len -= (result + delLen);
result = strcspn(s, delimiter);
}
sq_pushstring(v, s, result);
sq_arrayappend(v, -2);
return 1;
}
static SQInteger translate(HSQUIRRELVM v) {
const SQChar *text;
if (SQ_FAILED(sqget(v, 2, text)))
return sq_throwerror(v, "Failed to get text");
Common::String newText = g_twp->getTextDb().getText(text);
debugC(kDebugGenScript, "translate(%s): %s", text, newText.c_str());
sqpush(v, Common::move(newText));
return 1;
}
void sqgame_register_genlib(HSQUIRRELVM v) {
regFunc(v, activeVerb, _SC("activeVerb"));
regFunc(v, adhocalytics, _SC("adhocalytics"));
regFunc(v, arrayShuffle, _SC("arrayShuffle"));
regFunc(v, assetExists, _SC("assetExists"));
regFunc(v, cameraAt, _SC("cameraAt"));
regFunc(v, cameraBounds, _SC("cameraBounds"));
regFunc(v, cameraFollow, _SC("cameraFollow"));
regFunc(v, cameraInRoom, _SC("cameraInRoom"));
regFunc(v, cameraPanTo, _SC("cameraPanTo"));
regFunc(v, cameraPos, _SC("cameraPos"));
regFunc(v, sqChr, _SC("chr"));
regFunc(v, cursorPosX, _SC("cursorPosX"));
regFunc(v, cursorPosY, _SC("cursorPosY"));
regFunc(v, distance, _SC("distance"));
regFunc(v, findScreenPosition, _SC("findScreenPosition"));
regFunc(v, frameCounter, _SC("frameCounter"));
regFunc(v, getUserPref, _SC("getUserPref"));
regFunc(v, getPrivatePref, _SC("getPrivatePref"));
regFunc(v, incutscene, _SC("incutscene"));
regFunc(v, indialog, _SC("indialog"));
regFunc(v, integer, _SC("int"));
regFunc(v, in_array, _SC("in_array"));
regFunc(v, is_array, _SC("is_array"));
regFunc(v, is_function, _SC("is_function"));
regFunc(v, is_string, _SC("is_string"));
regFunc(v, is_table, _SC("is_table"));
regFunc(v, sqrandom, _SC("random"));
regFunc(v, loadArray, _SC("loadArray"));
regFunc(v, markAchievement, _SC("markAchievement"));
regFunc(v, markProgress, _SC("markProgress"));
regFunc(v, markStat, _SC("markStat"));
regFunc(v, ord, _SC("ord"));
regFunc(v, pushSentence, _SC("pushSentence"));
regFunc(v, randomFrom, _SC("randomfrom"));
regFunc(v, randomOdds, _SC("randomOdds"));
regFunc(v, randomOdds, _SC("randomodds"));
regFunc(v, randomseed, _SC("randomseed"));
regFunc(v, refreshUI, _SC("refreshUI"));
regFunc(v, screenSize, _SC("screenSize"));
regFunc(v, setDebugger, _SC("setDebugger"));
regFunc(v, setPrivatePref, _SC("setPrivatePref"));
regFunc(v, setUserPref, _SC("setUserPref"));
regFunc(v, setVerb, _SC("setVerb"));
regFunc(v, startDialog, _SC("startDialog"));
regFunc(v, stopSentence, _SC("stopSentence"));
regFunc(v, strcount, _SC("strcount"));
regFunc(v, strcrc, _SC("strcrc"));
regFunc(v, strfind, _SC("strfind"));
regFunc(v, strfirst, _SC("strfirst"));
regFunc(v, strlast, _SC("strlast"));
regFunc(v, strlines, _SC("strlines"));
regFunc(v, strreplace, _SC("strreplace"));
regFunc(v, strsplit, _SC("strsplit"));
regFunc(v, translate, _SC("translate"));
}
} // namespace Twp