#include #include #include #include #include "DEBUG.H" #include "Dialogue Control.h" #include "FileMan.h" #include "GameSettings.h" #include "Soldier Profile.h" #include "strategic.h" #include "strategicmap.h" #include "Strategic Mines.h" #include "Strategic Mines LUA.h" #include "UndergroundInit.h" #include "connect.h" using namespace std; static bool locationStrToCoords(string str, INT16* x, INT16* y); // global LuaMines g_luaMines; ///////////////////////////////////////// //////////////////////////////////////// LuaMines::LuaMines() : m_log(L"initmines.log", false) { m_L = lua_open(); luaL_openlibs(m_L); } void LuaMines::LoadScript() { char * filename = "scripts\\initmines.lua"; UINT32 size, bytesRead; HWFILE file = FileOpen(filename, FILE_ACCESS_READ, FALSE); if (!file) { string msg("Cannot open file: "); msg.append(filename); m_log << msg << vfs::Log::endl; SGP_THROW(msg); } size = FileSize(filename); vector buffer = vector(size+1); buffer[size] = 0; FileRead(file, &buffer[0], size, &bytesRead); FileClose(file); if (luaL_dostring(m_L, &buffer[0])) { string error("lua parsing error: "); error.append(lua_tostring(m_L, -1)); m_log << error << vfs::Log::endl; lua_pop(m_L, 1); SGP_THROW(error); } else { lua_getglobal(m_L, "InitializeMines"); if (!lua_isfunction(m_L, -1)) { string error("missing LUA function 'InitializeMines'"); m_log << error << vfs::Log::endl; lua_pop(m_L, 1); SGP_THROW(error); } else m_initMinesFuncID = luaL_ref(m_L, LUA_REGISTRYINDEX); lua_getglobal(m_L, "InitializeHeadMiners"); if (!lua_isfunction(m_L, -1)) { string error("missing LUA function 'InitializeHeadMiners'"); m_log << error << vfs::Log::endl; lua_pop(m_L, 1); SGP_THROW(error); } else m_initMinersFuncID = luaL_ref(m_L, LUA_REGISTRYINDEX); lua_getglobal(m_L, "logging"); if (!lua_isnil(m_L, -1) && lua_isboolean(m_L, -1) && lua_toboolean(m_L, -1)) m_log.ToggleLogging(true); lua_pop(m_L, 1); } } void LuaMines::Reset() { gMineStatus.clear(); gHeadMinerData.clear(); // just to be sure ShutdownStaticExternalNPCFaces(); InitalizeStaticExternalNPCFaces(); } void LuaMines::InitMinerFaces() { if (gHeadMinerData.size() > 0) { for (vector::iterator miner = gHeadMinerData.begin(); miner != gHeadMinerData.end(); ++miner) { // load external face UINT32 extFaceIndex = static_cast( InitFace(static_cast(miner->usProfileId), NOBODY, FACE_FORCE_SMALL) ); if (extFaceIndex != -1) { miner->ubExternalFaceIndex = uiExternalStaticNPCFaces.size(); uiExternalStaticNPCFaces.push_back(extFaceIndex); } else miner->ubExternalFaceIndex = -1; } } } void LuaMines::InitializeMines() { Reset(); m_log << "Calling 'InitializeMines'" << vfs::Log::endl; // WANNE: Init mines in a multiplayer game, only if we already received the server game difficulty level (ubDifficultyLevel > 0)!! // set global, maybe pass as parameter instead? if (!is_networked || gGameOptions.ubDifficultyLevel > 0) { AssertT(gGameOptions.ubDifficultyLevel > 0); lua_pushinteger(m_L, gGameOptions.ubDifficultyLevel); lua_setglobal(m_L, "newDIFFICULTY_LEVEL"); m_log << "global newDIFFICULTY_LEVEL=" << static_cast(gGameOptions.ubDifficultyLevel) << vfs::Log::endl; // quick fix for ini override option lua_pushinteger(m_L, gGameExternalOptions.bWhichMineRunsOut); lua_setglobal(m_L, "iniWHICH_MINE_SHUTS_DOWN"); m_log << "global iniWHICH_MINE_SHUTS_DOWN=" << static_cast(gGameExternalOptions.bWhichMineRunsOut) << vfs::Log::endl; // load and call lua function lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_initMinesFuncID); if (lua_pcall(m_L, 0, 1, 0) != 0) { string error(lua_tostring(m_L, -1)); lua_pop(m_L, 1); SGP_THROW(error); } // The script is supposed to return an array of tables. // Iterate over each table. int currentTable = 1; // first table, lua indexing is 1-based while (1) { MINE_STATUS_TYPE mineStatus = MINE_STATUS_TYPE(); // load table lua_pushinteger(m_L, currentTable); lua_gettable(m_L, -2); if (lua_isnil(m_L, -1)) // done, no more tables break; lua_getfield(m_L, -1, "Location"); string loc = lua_tostring(m_L, -1); lua_pop(m_L, 1); lua_getfield(m_L, -1, "Type"); mineStatus.ubMineType = lua_tointeger(m_L, -1); lua_pop(m_L, 1); lua_getfield(m_L, -1, "MaxRemovalRate"); mineStatus.uiMaxRemovalRate = lua_tointeger(m_L, -1); lua_pop(m_L, 1); lua_getfield(m_L, -1, "RemainingOreSupply"); mineStatus.uiRemainingOreSupply = lua_tointeger(m_L, -1); lua_pop(m_L, 1); lua_getfield(m_L, -1, "Infectible"); mineStatus.fInfectible = lua_tointeger(m_L, -1) != 0; lua_pop(m_L, 1); lua_getfield(m_L, -1, "AssociatedUnderground"); if (!lua_isnil(m_L, -1)) { // again, this field is a table/array int currentSubTable = 1; // first lua index while (1) { // get next array element lua_pushinteger(m_L, currentSubTable); lua_gettable(m_L, -2); if (lua_isnil(m_L, -1)) // done break; string str(lua_tostring(m_L, -1)); AssociatedMineSector sector; if (locationStringToCoordinates(str, §or.x, §or.y, §or.z)) { sector.mineID = gMineStatus.size(); associatedMineSectors.push_back(sector); } lua_pop(m_L, 1); currentSubTable++; } lua_pop(m_L, 1); // pop nil } lua_pop(m_L, 1); // pop field or nil value if (locationStrToCoords(loc, &mineStatus.sSectorX, &mineStatus.sSectorY)) { // valid location, add new mine mineStatus.fEmpty = mineStatus.uiMaxRemovalRate == 0; mineStatus.fRunningOut = FALSE; mineStatus.fWarnedOfRunningOut = FALSE; mineStatus.fShutDown = FALSE; mineStatus.fPrevInvadedByMonsters = FALSE; mineStatus.fSpokeToHeadMiner = FALSE; mineStatus.fMineHasProducedForPlayer = FALSE; mineStatus.fQueenRetookProducingMine = FALSE; mineStatus.fShutDownIsPermanent = FALSE; // this was usually set to 0 except for the mine that is supposed to // be running out (in which case the calculation below applies) // TODO: see if this works for all mines mineStatus.uiOreRunningOutPoint = mineStatus.uiRemainingOreSupply / 4; // gather associated town id from sector coordinates int index = CALCULATE_STRATEGIC_INDEX(mineStatus.sSectorX, mineStatus.sSectorY); mineStatus.bAssociatedTown = StrategicMap[index].bNameId; gMineStatus.push_back(mineStatus); // log m_log << "create mine" << vfs::Log::endl; m_log << "\tSector: " << string(1, 'A'-1+mineStatus.sSectorY) << mineStatus.sSectorX << vfs::Log::endl; m_log << "\tType: " << (mineStatus.ubMineType ? "gold" : "silver") << vfs::Log::endl; m_log << "\tMaxRemovalRate: " << mineStatus.uiMaxRemovalRate << vfs::Log::endl; m_log << "\tRemainingOreSupply: " << mineStatus.uiRemainingOreSupply << vfs::Log::endl; m_log << "\tOreRunningOutPoint: " << mineStatus.uiOreRunningOutPoint << vfs::Log::endl; m_log << "\tInfectible: " << (mineStatus.fInfectible ? 1 : 0 ) << vfs::Log::endl; } else { m_log << "WARNING: not creating mine #" << currentTable << " due to bad location string (" << loc << ")" << vfs::Log::endl; } lua_pop(m_L, 1); // pop this table currentTable++; } // update global MAX_NUMBER_OF_MINES = gMineStatus.size(); lua_pop(m_L, 1); // pop nil value lua_pop(m_L, 1); // pop return value } } BOOLEAN LuaMines::InitializeHeadMiners(UINT8 firstMineID) { m_log << vfs::Log::endl; m_log << "Calling 'InitializeHeadMiners'" << vfs::Log::endl; lua_rawgeti(m_L, LUA_REGISTRYINDEX, m_initMinersFuncID); m_log << "parameter 1 ('mines')" << vfs::Log::endl; // push parameters lua_newtable(m_L); for (size_t i = 0; i < gMineStatus.size(); i++) { int luaIndex = i + 1; lua_pushnumber(m_L, luaIndex); m_log << "\tindex #" << luaIndex << ": "; lua_newtable(m_L); { MINE_STATUS_TYPE& mine = gMineStatus[i]; lua_pushstring(m_L, "MaxRemovalRate"); lua_pushinteger(m_L, mine.uiMaxRemovalRate); m_log << "\t\tMaxRemovalRate:" << mine.uiMaxRemovalRate << vfs::Log::endl; lua_settable(m_L, -3); } lua_settable(m_L, -3); } // add 1 due to lua indexing starting at 1 int luaFirstMine = firstMineID + 1; lua_pushnumber(m_L, luaFirstMine); m_log << "parameter 2 ('currentMine'): " << luaFirstMine << vfs::Log::endl; if (lua_pcall(m_L, 2, 1, 0) != 0) { string error(lua_tostring(m_L, -1)); lua_pop(m_L, 1); SGP_THROW(error); } if (lua_isnil(m_L, -1)) { // lua script did not initialize head miners yet lua_pop(m_L, 1); // pop return value (nil) m_log << "script returned nil" << vfs::Log::endl; return FALSE; } // The script is supposed to return an array of tables. // Iterate over each element. int currentTable = 1; while (1) { HEAD_MINER_TYPE headMiner; // get this element of result array lua_pushinteger(m_L, currentTable); lua_gettable(m_L, -2); if (lua_isnil(m_L, -1)) // done, no more elements in result break; lua_getfield(m_L, -1, "Profile"); headMiner.usProfileId = lua_tointeger(m_L, -1); lua_pop(m_L, 1); lua_getfield(m_L, -1, "Quotes"); INT8 quotes[4] = { -1, -1, -1, -1 }; for (int quoteIndex = 0; quoteIndex < 4; quoteIndex++) { lua_pushinteger(m_L, quoteIndex+1); // 1-based lua index lua_gettable(m_L, -2); if (lua_isnil(m_L, -1)) { // not enough quote ids, issue warning? lua_pop(m_L, 1); // pop nil break; } quotes[quoteIndex] = lua_tointeger(m_L, -1); lua_pop(m_L, 1); } memcpy(headMiner.bQuoteNum, quotes, 4); lua_pop(m_L, 1); // pop quotes array int mineId = -1; lua_getfield(m_L, -1, "MineID"); if (!lua_isnil(m_L, -1)) mineId = lua_tointeger(m_L, -1) - 1; // lua is 1-based, convert to 0-based lua_pop(m_L, 1); if (mineId > -1 && mineId < int(gMineStatus.size())) { MINE_STATUS_TYPE* mine = &gMineStatus[mineId]; MERCPROFILESTRUCT* minerProfile = &gMercProfiles[headMiner.usProfileId]; // place miner minerProfile->bTown = mine->bAssociatedTown; minerProfile->sSectorX = mine->sSectorX; minerProfile->sSectorY = mine->sSectorY; minerProfile->bSectorZ = 0; gHeadMinerData.push_back(headMiner); // log m_log << "create miner" << vfs::Log::endl; m_log << "\tProfileID: " << headMiner.usProfileId << vfs::Log::endl; m_log << "\tQuotes:"; for (int i = 0; i < 4; i++) m_log << " " << headMiner.bQuoteNum[i]; m_log << vfs::Log::endl; m_log << "\tMineID: " << mineId << vfs::Log::endl; m_log << "\tAssociated Town: " << minerProfile->bTown << vfs::Log::endl; m_log << "\tSector: " << string(1, 'A'-1+minerProfile->sSectorY) << minerProfile->sSectorX << vfs::Log::endl; } else { m_log << "WARNING: not creating miner #" << currentTable << " due to illegal mine id (" << mineId << ")" << vfs::Log::endl; } lua_pop(m_L, 1); // pop current element of result table currentTable++; } lua_pop(m_L, 1); // pop nil value lua_pop(m_L, 1); // pop return value InitMinerFaces(); // update global NUM_HEAD_MINERS = gHeadMinerData.size(); return TRUE; } LuaMines::~LuaMines() { lua_close(m_L); } BOOLEAN LuaMines::Load(HWFILE hFile) { Reset(); UINT32 uiNumBytesRead; INT32 numMines; MINE_STATUS_TYPE mineStatus; gMineStatus.clear(); FileRead(hFile, &numMines, sizeof(INT32), &uiNumBytesRead); if (uiNumBytesRead != sizeof(INT32)) return FALSE; for (int i = 0; i < numMines; i++) { FileRead(hFile, &mineStatus, sizeof(MINE_STATUS_TYPE), &uiNumBytesRead); if (uiNumBytesRead != sizeof(MINE_STATUS_TYPE)) return FALSE; gMineStatus.push_back(mineStatus); } MAX_NUMBER_OF_MINES = gMineStatus.size(); INT32 numSectors; AssociatedMineSector sector; associatedMineSectors.clear(); FileRead(hFile, &numSectors, sizeof(INT32), &uiNumBytesRead); if (uiNumBytesRead != sizeof(INT32)) return FALSE; for (int i = 0; i < numSectors; i++) { FileRead(hFile, §or, sizeof(AssociatedMineSector), &uiNumBytesRead); if (uiNumBytesRead != sizeof(AssociatedMineSector)) return FALSE; associatedMineSectors.push_back(sector); } INT32 numHeadMiners; HEAD_MINER_TYPE miner; gHeadMinerData.clear(); FileRead(hFile, &numHeadMiners, sizeof(INT32), &uiNumBytesRead); if (uiNumBytesRead != sizeof(INT32)) return FALSE; for (int i = 0; i < numHeadMiners; i++) { FileRead(hFile, &miner, sizeof(HEAD_MINER_TYPE), &uiNumBytesRead); if (uiNumBytesRead != sizeof(HEAD_MINER_TYPE)) return FALSE; gHeadMinerData.push_back(miner); } NUM_HEAD_MINERS = gHeadMinerData.size(); InitMinerFaces(); return TRUE; } BOOLEAN LuaMines::Save(HWFILE hFile) { UINT32 uiNumBytesWritten; INT32 numMines = gMineStatus.size(); FileWrite(hFile, &numMines, sizeof(INT32), &uiNumBytesWritten); if (uiNumBytesWritten != sizeof(INT32)) return FALSE; for (std::vector::iterator it = gMineStatus.begin(); it != gMineStatus.end(); ++it) { FileWrite(hFile, &*it, sizeof(MINE_STATUS_TYPE), &uiNumBytesWritten); if (uiNumBytesWritten != sizeof(MINE_STATUS_TYPE)) return FALSE; } INT32 numSectors = associatedMineSectors.size(); FileWrite(hFile, &numSectors, sizeof(INT32), &uiNumBytesWritten); if (uiNumBytesWritten != sizeof(INT32)) return FALSE; for (std::vector::iterator it = associatedMineSectors.begin(); it != associatedMineSectors.end(); ++it) { FileWrite(hFile, &*it, sizeof(AssociatedMineSector), &uiNumBytesWritten); if (uiNumBytesWritten != sizeof(AssociatedMineSector)) return FALSE; } INT32 numMiners = gHeadMinerData.size(); FileWrite(hFile, &numMiners, sizeof(INT32), &uiNumBytesWritten); if (uiNumBytesWritten != sizeof(INT32)) return FALSE; for (std::vector::iterator it = gHeadMinerData.begin(); it != gHeadMinerData.end(); ++it) { FileWrite(hFile, &*it, sizeof(HEAD_MINER_TYPE), &uiNumBytesWritten); if (uiNumBytesWritten != sizeof(HEAD_MINER_TYPE)) return FALSE; } return TRUE; } /////////////////////////////////////////// ////////////////////////////////////////////// static bool locationStrToCoords(string str, INT16* x, INT16* y) { if (str.length() < 2 || str.length() > 3) return false; char yy = tolower(str[0]); if (yy < 'a' || yy > 'p') return false; *y = yy - 'a' + 1; str = str.substr(1); stringstream ss; ss << str; ss >> *x; if (*x < 1 || *x > 16) return false; return true; }