#include "framework.h" #include "GameLogicScript.h" #include "items.h" #include "box.h" #include "lara.h" #include "savegame.h" #include "lot.h" #include "sound.h" #include "setup.h" #include "level.h" using namespace std; extern GameFlow* g_GameFlow; GameScript* g_GameScript; bool WarningsAsErrors = false; GameScript::GameScript(sol::state* lua) { m_lua = lua; // Add constants //ExecuteScript("Scripts\\Constants.lua"); m_lua->new_enum("Object", { {"LARA", ID_LARA} }); // Add the item type m_lua->new_usertype("Position", "X", sol::property(&GameScriptPosition::GetXPos, &GameScriptPosition::SetXPos), "Y", sol::property(&GameScriptPosition::GetYPos, &GameScriptPosition::SetYPos), "Z", sol::property(&GameScriptPosition::GetZPos, &GameScriptPosition::SetZPos), "new", sol::no_constructor ); m_lua->new_usertype("Rotation", "X", sol::property(&GameScriptRotation::GetXRot, &GameScriptRotation::SetXRot), "Y", sol::property(&GameScriptRotation::GetYRot, &GameScriptRotation::SetYRot), "Z", sol::property(&GameScriptRotation::GetZRot, &GameScriptRotation::SetZRot), "new", sol::no_constructor ); m_lua->new_usertype("Item", "Position", sol::property(&GameScriptItem::GetPosition), "Rotation", sol::property(&GameScriptItem::GetRotation), "HP", sol::property(&GameScriptItem::GetHP, &GameScriptItem::SetHP), "Room", sol::property(&GameScriptItem::GetRoom, &GameScriptItem::SetRoom), "CurrentState", sol::property(&GameScriptItem::GetCurrentState, &GameScriptItem::SetCurrentState), "GoalState", sol::property(&GameScriptItem::GetGoalState, &GameScriptItem::SetGoalState), "RequiredState", sol::property(&GameScriptItem::GetRequiredState, &GameScriptItem::SetRequiredState), "new", sol::no_constructor ); m_lua->set_function("EnableItem", &GameScriptItem::EnableItem); m_lua->set_function("DisableItem", &GameScriptItem::DisableItem); m_lua->new_usertype("Variable", sol::meta_function::index, &LuaVariables::GetVariable, sol::meta_function::new_index, &LuaVariables::SetVariable, "new", sol::no_constructor ); // GameScript type /*m_lua->new_usertype("GameScript", "PlayAudioTrack", &GameScript::PlayAudioTrack, "ChangeAmbientSoundTrack", &GameScript::ChangeAmbientSoundTrack, "MakeItemInvisible", &GameScript::MakeItemInvisible, "GetSecretsCount", &GameScript::GetSecretsCount, "SetSecretsCount", &GameScript::SetSecretsCount, "AddOneSecret", &GameScript::AddOneSecret, "JumpToLevel", &GameScript::JumpToLevel, "PlaySoundEffect", &GameScript::PlaySoundEffect, "PlaySoundEffectAtPosition", &GameScript::PlaySoundEffectAtPosition );*/ m_lua->set_function("GetItemByID", &GameScript::GetItemById); m_lua->set_function("GetItemByName", &GameScript::GetItemByName); m_lua->set_function("CreatePosition", &GameScript::CreatePosition); m_lua->set_function("CreateRotation", &GameScript::CreateRotation); m_lua->set_function("CalculateDistance", &GameScript::CalculateDistance); m_lua->set_function("CalculateHorizontalDistance", &GameScript::CalculateHorizontalDistance); // Add global variables and namespaces //(*m_lua)["TR"] = this; } void GameScript::AddTrigger(LuaFunction* function) { m_triggers.push_back(function); (*m_lua).script(function->Code); } void GameScript::AddLuaId(int luaId, short itemNumber) { m_itemsMapId.insert(pair(luaId, itemNumber)); } void GameScript::AddLuaName(string luaName, short itemNumber) { m_itemsMapName.insert(pair(luaName, itemNumber)); } void GameScript::FreeLevelScripts() { /* // Delete all triggers for (int i = 0; i < m_triggers.size(); i++) { LuaFunction* trigger = m_triggers[i]; char* name = (char*)trigger->Name.c_str(); (*m_lua)[name] = NULL; delete m_triggers[i]; } m_triggers.clear(); // Clear the items mapping m_itemsMap.clear(); (*m_lua)["Lara"] = NULL; //delete m_Lara; */ } bool GameScript::ExecuteScript(const string& luaFilename, string& message) { auto result = m_lua->safe_script_file(luaFilename, sol::environment(m_lua->lua_state(), sol::create, m_lua->globals()), sol::script_pass_on_error); if (!result.valid()) { sol::error error = result; message = error.what(); return false; } return true; } bool GameScript::ExecuteString(const string& command, string& message) { auto result = m_lua->safe_script(command, sol::environment(m_lua->lua_state(), sol::create, m_lua->globals()), sol::script_pass_on_error); if (!result.valid()) { sol::error error = result; message = error.what(); return false; } return true; } bool GameScript::ExecuteTrigger(short index) { return true; /* // Is this a valid trigger? if (index >= m_triggers.size()) return true; LuaFunction* trigger = m_triggers[index]; // We want to execute a trigger just one time // TODO: implement in the future continoous trigger? if (trigger->Executed) return true; // Get the trigger function name char* name = (char*)trigger->Name.c_str(); // Execute trigger bool result = (*m_lua)[name](); // Trigger was executed, don't execute it anymore trigger->Executed = result; m_locals.for_each([&](sol::object const& key, sol::object const& value) { if (value.is()) std::cout << key.as() << " " << value.as() << std::endl; else if (value.is()) std::cout << key.as() << " " << value.as() << std::endl; else std::cout << key.as() << " " << value.as() << std::endl; }); return result; */ } void GameScript::PlayAudioTrack(short track) { S_CDPlay(track, SOUND_TRACK_ONESHOT); } void GameScript::ChangeAmbientSoundTrack(short track) { CurrentAtmosphere = track; S_CDStop(); S_CDPlay(track, SOUND_TRACK_BGM); } void GameScript::JumpToLevel(int levelNum) { if (levelNum >= g_GameFlow->GetNumLevels()) return; LevelComplete = levelNum; } int GameScript::GetSecretsCount() { return Savegame.Level.Secrets; } void GameScript::SetSecretsCount(int secretsNum) { if (secretsNum > 255) return; Savegame.Level.Secrets = secretsNum; } void GameScript::AddOneSecret() { if (Savegame.Level.Secrets >= 255) return; Savegame.Level.Secrets++; S_CDPlay(6, 0); } /* void GameScript::MakeItemInvisible(short id) { if (m_itemsMap.find(id) == m_itemsMap.end()) return; short itemNum = m_itemsMap[id]; ITEM_INFO* item = &g_Level.Items[itemNum]; if (item->active) { if (Objects[item->objectNumber].intelligent) { if (item->status == ITEM_ACTIVE) { item->touchBits = 0; item->status = ITEM_INVISIBLE; DisableBaddieAI(itemNum); } } else { item->touchBits = 0; item->status = ITEM_INVISIBLE; } } } */ template void GameScript::GetVariables(map& locals, map& globals) { for (const auto& it : m_locals.variables) { if (it.second.is()) locals.insert(pair(it.first, it.second.as())); } for (const auto& it : m_globals.variables) { if (it.second.is()) globals.insert(pair(it.first, it.second.as())); } } template void GameScript::GetVariables(map& locals, map& globals); template void GameScript::GetVariables(map& locals, map& globals); template void GameScript::GetVariables(map& locals, map& globals); template void GameScript::SetVariables(map& locals, map& globals) { m_locals.variables.clear(); for (const auto& it : locals) { m_locals.variables.insert(pair(it.first, sol::object(m_lua->lua_state(), sol::in_place, it.second))); } for (const auto& it : globals) { m_globals.variables.insert(pair(it.first, sol::object(m_lua->lua_state(), sol::in_place, it.second))); } } template void GameScript::SetVariables(map& locals, map& globals); template void GameScript::SetVariables(map& locals, map& globals); template void GameScript::SetVariables(map& locals, map& globals); unique_ptr GameScript::GetItemById(int id) { if (m_itemsMapId.find(id) == m_itemsMapId.end()) { if (WarningsAsErrors) throw "item id not found"; return unique_ptr(nullptr); } return unique_ptr(new GameScriptItem(m_itemsMapId[id])); } unique_ptr GameScript::GetItemByName(string name) { if (m_itemsMapName.find(name) == m_itemsMapName.end()) { if (WarningsAsErrors) throw "item name not found"; return unique_ptr(nullptr); } return unique_ptr(new GameScriptItem(m_itemsMapName[name])); } void GameScript::PlaySoundEffectAtPosition(short id, int x, int y, int z, int flags) { PHD_3DPOS pos; pos.xPos = x; pos.yPos = y; pos.zPos = z; pos.xRot = 0; pos.yRot = 0; pos.zRot = 0; SoundEffect(id, &pos, flags); } void GameScript::PlaySoundEffect(short id, int flags) { SoundEffect(id, NULL, flags); } void GameScript::AssignItemsAndLara() { m_lua->set("Level", m_locals); m_lua->set("Game", m_globals); m_lua->set("Lara", GameScriptItem(Lara.itemNumber)); } void GameScript::ResetVariables() { (*m_lua)["Lara"] = NULL; } GameScriptPosition GameScript::CreatePosition(float x, float y, float z) { return GameScriptPosition(x, y, z); } GameScriptPosition GameScript::CreateSectorPosition(float x, float y, float z) { return GameScriptPosition(1024 * x + 512, 1024 * y + 512, 1024 * z + 512); } GameScriptRotation GameScript::CreateRotation(float x, float y, float z) { return GameScriptRotation(x, y, z); } float GameScript::CalculateDistance(GameScriptPosition pos1, GameScriptPosition pos2) { return sqrt(SQUARE(pos1.GetXPos() - pos2.GetXPos()) + SQUARE(pos1.GetYPos() - pos2.GetYPos()) + SQUARE(pos1.GetZPos() - pos2.GetZPos())); } float GameScript::CalculateHorizontalDistance(GameScriptPosition pos1, GameScriptPosition pos2) { return sqrt(SQUARE(pos1.GetXPos() - pos2.GetXPos()) + SQUARE(pos1.GetZPos() - pos2.GetZPos())); } GameScriptPosition::GameScriptPosition(float x, float y, float z) : xPos(x), yPos(y), zPos(z) { } GameScriptPosition::GameScriptPosition(function readX, function writeX, function readY, function writeY, function readZ, function writeZ) : readXPos(readX), writeXPos(writeX), readYPos(readY), writeYPos(writeY), readZPos(readZ), writeZPos(writeZ) { } float GameScriptPosition::GetXPos() { if (readXPos) xPos = readXPos(); return xPos; } void GameScriptPosition::SetXPos(float x) { xPos = x; if (writeXPos) writeXPos(xPos); } float GameScriptPosition::GetYPos() { if (readYPos) yPos = readYPos(); return yPos; } void GameScriptPosition::SetYPos(float y) { yPos = y; if (writeYPos) writeYPos(yPos); } float GameScriptPosition::GetZPos() { if (readZPos) zPos = readZPos(); return zPos; } void GameScriptPosition::SetZPos(float z) { zPos = z; if (writeZPos) writeZPos(zPos); } GameScriptRotation::GameScriptRotation(float x, float y, float z) : xRot(x), yRot(y), zRot(z) { } GameScriptRotation::GameScriptRotation(function readX, function writeX, function readY, function writeY, function readZ, function writeZ) : readXRot(readX), writeXRot(writeX), readYRot(readY), writeYRot(writeY), readZRot(readZ), writeZRot(writeZ) { } float GameScriptRotation::GetXRot() { if (readXRot) xRot = readXRot(); return xRot; } void GameScriptRotation::SetXRot(float x) { x = remainder(x, 360); if (x < 0) x += 360; xRot = x; if (writeXRot) writeXRot(xRot); } float GameScriptRotation::GetYRot() { if (readYRot) yRot = readYRot(); return yRot; } void GameScriptRotation::SetYRot(float y) { y = remainder(y, 360); if (y < 0) y += 360; yRot = y; if (writeYRot) writeYRot(yRot); } float GameScriptRotation::GetZRot() { if (readZRot) zRot = readZRot(); return zRot; } void GameScriptRotation::SetZRot(float z) { z = remainder(z, 360); if (z < 0) z += 360; zRot = z; if (writeZRot) writeZRot(zRot); } GameScriptItem::GameScriptItem(short itemNumber) : NativeItemNumber(itemNumber), NativeItem(&g_Level.Items[itemNumber]) { } GameScriptPosition GameScriptItem::GetPosition() { return GameScriptPosition( [this]() -> float { return NativeItem->pos.xPos; }, [this](float x) -> void { NativeItem->pos.xPos = x; }, [this]() -> float { return NativeItem->pos.yPos; }, [this](float y) -> void { NativeItem->pos.yPos = y; }, [this]() -> float { return NativeItem->pos.zPos; }, [this](float z) -> void { NativeItem->pos.zPos = z; } ); } GameScriptRotation GameScriptItem::GetRotation() { return GameScriptRotation( [this]() -> float { return TO_DEGREES(NativeItem->pos.xRot); }, [this](float x) -> void { NativeItem->pos.xRot = ANGLE(x); }, [this]() -> float { return TO_DEGREES(NativeItem->pos.yRot); }, [this](float y) -> void { NativeItem->pos.yRot = ANGLE(y); }, [this]() -> float { return TO_DEGREES(NativeItem->pos.zRot); }, [this](float z) -> void { NativeItem->pos.zRot = ANGLE(z); } ); } short GameScriptItem::GetHP() { return NativeItem->hitPoints; } void GameScriptItem::SetHP(short hp) { if (hp < 0 || hp > Objects[NativeItem->objectNumber].hitPoints) { if (WarningsAsErrors) throw runtime_error("invalid HP"); if (hp < 0) { hp = 0; } else if (hp > Objects[NativeItem->objectNumber].hitPoints) { hp = Objects[NativeItem->objectNumber].hitPoints; } } NativeItem->hitPoints = hp; } short GameScriptItem::GetRoom() { return NativeItem->roomNumber; } void GameScriptItem::SetRoom(short room) { if (room < 0 || room >= g_Level.Rooms.size()) { if (WarningsAsErrors) throw runtime_error("invalid room number"); return; } NativeItem->roomNumber = room; } void GameScriptItem::EnableItem() { if (!NativeItem->active) { if (Objects[NativeItem->objectNumber].intelligent) { if (NativeItem->status == ITEM_DEACTIVATED) { NativeItem->touchBits = 0; NativeItem->status = ITEM_ACTIVE; AddActiveItem(NativeItemNumber); EnableBaddieAI(NativeItemNumber, 1); } else if (NativeItem->status == ITEM_INVISIBLE) { NativeItem->touchBits = 0; if (EnableBaddieAI(NativeItemNumber, 0)) NativeItem->status = ITEM_ACTIVE; else NativeItem->status = ITEM_INVISIBLE; AddActiveItem(NativeItemNumber); } } else { NativeItem->touchBits = 0; AddActiveItem(NativeItemNumber); NativeItem->status = ITEM_ACTIVE; } } } void GameScriptItem::DisableItem() { if (NativeItem->active) { if (Objects[NativeItem->objectNumber].intelligent) { if (NativeItem->status == ITEM_ACTIVE) { NativeItem->touchBits = 0; NativeItem->status = ITEM_DEACTIVATED; RemoveActiveItem(NativeItemNumber); DisableBaddieAI(NativeItemNumber); } } else { NativeItem->touchBits = 0; RemoveActiveItem(NativeItemNumber); NativeItem->status = ITEM_DEACTIVATED; } } } short GameScriptItem::GetCurrentState() { return NativeItem->currentAnimState; } void GameScriptItem::SetCurrentState(short state) { NativeItem->currentAnimState = state; } short GameScriptItem::GetGoalState() { return NativeItem->goalAnimState; } void GameScriptItem::SetGoalState(short state) { NativeItem->goalAnimState = state; } short GameScriptItem::GetRequiredState() { return NativeItem->requiredAnimState; } void GameScriptItem::SetRequiredState(short state) { NativeItem->requiredAnimState = state; } sol::object LuaVariables::GetVariable(string key) { if (variables.find(key) == variables.end()) return sol::lua_nil; return variables[key]; } void LuaVariables::SetVariable(string key, sol::object value) { switch (value.get_type()) { case sol::type::lua_nil: variables.erase(key); break; case sol::type::boolean: case sol::type::number: case sol::type::string: variables[key] = value; break; default: if (WarningsAsErrors) throw runtime_error("unsupported variable type"); break; } }