From 7b6f3d8441c8b240b839e648324e6318452f573c Mon Sep 17 00:00:00 2001 From: Flugente Date: Tue, 26 Jul 2016 20:44:42 +0000 Subject: [PATCH] New LUA functions: via GetModderLUAFact and SetModderLUAFact, a modder can set up to 1000 INT32 values exclusively in LUA scripts. This can be used to store mod-specific data in save games without additional code required git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8276 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameVersion.h | 3 +- SaveLoadGame.cpp | 69 +++++++++------ Strategic/Assignments.cpp | 176 ++++++++++++++++++++------------------ Strategic/Assignments.h | 13 ++- Strategic/LuaInitNPCs.cpp | 117 +++++++++++++++++++++++-- Strategic/Quests.cpp | 41 ++++++++- Strategic/Quests.h | 7 ++ 7 files changed, 308 insertions(+), 118 deletions(-) diff --git a/GameVersion.h b/GameVersion.h index a866f4dfc..cd0ddd015 100644 --- a/GameVersion.h +++ b/GameVersion.h @@ -21,6 +21,7 @@ extern CHAR16 zTrackingNumber[16]; // Keeps track of the saved game version. Increment the saved game version whenever // you will invalidate the saved game file +#define LUA_MODDERDATA 171 // Flugente: modders can set and use data in an exclusive array #define WORKERS 170 // Flugente: train workers for facilities #define WEATHER_CHANGE 169 // Flugente: sector-specific weather & more weather types #define ASD_INIT_FIX 168 // Flugente: fix for ASD feature introduced with ENEMY_HELICOPTERS @@ -89,7 +90,7 @@ extern CHAR16 zTrackingNumber[16]; #define AP100_SAVEGAME_DATATYPE_CHANGE 105 // Before this, we didn't have the 100AP structure changes #define NIV_SAVEGAME_DATATYPE_CHANGE 102 // Before this, we used the old structure system -#define SAVE_GAME_VERSION WORKERS +#define SAVE_GAME_VERSION LUA_MODDERDATA //#define RUSSIANGOLD #ifdef __cplusplus diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index bdabd2e83..90f9c47cb 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -3929,49 +3929,43 @@ BOOLEAN SaveGame( int ubSaveGameID, STR16 pGameDesc ) #ifdef JA2BETAVERSION SaveGameFilePosition( FileGetPos( hFile ), "All the Map Temp files" ); #endif - - - - - + if( !SaveQuestInfoToSavedGameFile( hFile ) ) { ScreenMsg( FONT_MCOLOR_WHITE, MSG_ERROR, L"ERROR writing quest info"); goto FAILED_TO_SAVE; } - #ifdef JA2BETAVERSION - SaveGameFilePosition( FileGetPos( hFile ), "Quest Info" ); - #endif - - +#ifdef JA2BETAVERSION + SaveGameFilePosition( FileGetPos( hFile ), "Quest Info" ); +#endif + if ( !SaveLUAModderDataToSavedGameFile( hFile ) ) + { + ScreenMsg( FONT_MCOLOR_WHITE, MSG_ERROR, L"ERROR writing lUA modder data" ); + goto FAILED_TO_SAVE; + } +#ifdef JA2BETAVERSION + SaveGameFilePosition( FileGetPos( hFile ), "LUA Modder Data" ); +#endif if( !SaveOppListInfoToSavedGame( hFile ) ) { ScreenMsg( FONT_MCOLOR_WHITE, MSG_ERROR, L"ERROR writing opplist"); goto FAILED_TO_SAVE; } - #ifdef JA2BETAVERSION - SaveGameFilePosition( FileGetPos( hFile ), "OppList info" ); - #endif - - - - - +#ifdef JA2BETAVERSION + SaveGameFilePosition( FileGetPos( hFile ), "OppList info" ); +#endif + if( !SaveMapScreenMessagesToSaveGameFile( hFile ) ) { ScreenMsg( FONT_MCOLOR_WHITE, MSG_ERROR, L"ERROR writing map screen messages"); goto FAILED_TO_SAVE; } - #ifdef JA2BETAVERSION - SaveGameFilePosition( FileGetPos( hFile ), "MapScreen Messages" ); - #endif - - - - - +#ifdef JA2BETAVERSION + SaveGameFilePosition( FileGetPos( hFile ), "MapScreen Messages" ); +#endif + if( !SaveNPCInfoToSaveGameFile( hFile ) ) { ScreenMsg( FONT_MCOLOR_WHITE, MSG_ERROR, L"ERROR writing NPC info"); @@ -5175,6 +5169,29 @@ BOOLEAN LoadSavedGame( int ubSavedGameID ) #endif } + uiRelEndPerc += 1; + SetRelativeStartAndEndPercentage( 0, uiRelStartPerc, uiRelEndPerc, L"LUA Modder Data..." ); + RenderProgressBar( 0, 100 ); + uiRelStartPerc = uiRelEndPerc; + + if ( guiCurrentSaveGameVersion >= LUA_MODDERDATA ) + { + if ( !LoadLUAModderDataFromSavedGameFile( hFile ) ) + { + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "LoadLUAModderDataFromSavedGameFile failed" ) ); + FileClose( hFile ); + return(FALSE); + } +#ifdef JA2BETAVERSION + LoadGameFilePosition( FileGetPos( hFile ), "LUA Modder Data" ); +#endif + } + else + { + //Init LUA Modder Data + InitLUAModderData(); + } + uiRelEndPerc += 1; SetRelativeStartAndEndPercentage( 0, uiRelStartPerc, uiRelEndPerc, L"OppList Info..." ); RenderProgressBar( 0, 100 ); diff --git a/Strategic/Assignments.cpp b/Strategic/Assignments.cpp index 5d0c7036c..db2b48fe0 100644 --- a/Strategic/Assignments.cpp +++ b/Strategic/Assignments.cpp @@ -6874,92 +6874,14 @@ void HandlePrisonerProcessingInSector( INT16 sMapX, INT16 sMapY, INT8 bZ ) // chance that prisoner will give us random info about enemy positions else if ( result < chances[PRISONER_INTERROGATION_DEFECT] + chances[PRISONER_INTERROGATION_INFO] ) { + UINT8 infotype = INFO_TYPE_NORMAL; + // there is a chance this guy might tell us about high-value targets! if ( Chance( gGameExternalOptions.ubPrisonerInterrogationEnemyGeneralInfoChance[prisonertype] ) ) - { - UINT16 unknownvipector = 0; - if ( GetRandomUnknownVIPSector( unknownvipector ) ) - { - // make this guy known to the player - StrategicMap[unknownvipector].usFlags |= ENEMY_VIP_PRESENT_KNOWN; - - CHAR16 str[128]; - GetSectorIDString( SECTORX( unknownvipector ), SECTORY( unknownvipector ), 0, str, TRUE ); - - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szPrisonerTextStr[STR_PRISONER_DETECTION_VIP], str ); - - // enough info from this guy - continue; - } - } - - // mobile tanks are a serious threat. Learning of their location would be genuninely useful - // loop over all mobile enemy groups that have tanks that we don't yet know of, and pick one of them at random - std::vector sectorswithunknowntanksvector; - for ( GROUP *pGroup = gpGroupList; pGroup; pGroup = pGroup->next ) - { - if ( pGroup->usGroupTeam == ENEMY_TEAM && pGroup->pEnemyGroup->ubNumTanks ) - { - UINT8 tanksector = SECTOR( pGroup->ubSectorX, pGroup->ubSectorY ); - - // if we don't yet know that there are tanks here, this would be useful information - if ( !(SectorInfo[tanksector].uiFlags & SF_ASSIGN_NOTICED_ENEMIES_KNOW_NUMBER) ) - { - sectorswithunknowntanksvector.push_back( tanksector ); - } - } - } - - if ( !sectorswithunknowntanksvector.empty( ) ) - { - UINT8 tanksector = sectorswithunknowntanksvector[ Random( sectorswithunknowntanksvector.size( ) ) ]; - - SectorInfo[tanksector].uiFlags |= (SF_ASSIGN_NOTICED_ENEMIES_HERE | SF_ASSIGN_NOTICED_ENEMIES_KNOW_NUMBER); - - if ( Chance( gGameExternalOptions.ubPrisonerProcessInfoDirectionChance ) ) - { - // we also learned the direction of the patrol - SectorInfo[tanksector].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_KNOW_DIRECTION; - } + infotype = INFO_TYPE_VIP; + if ( GiveInfoToPlayer( infotype ) ) ++revealedpositions; - - continue; - } - - UINT8 maxtries = 20; - for(UINT8 infotry = 0; infotry < maxtries; ++infotry) - { - UINT8 usX = 1 + Random( MAP_WORLD_X - 2 ); - UINT8 usY = 1 + Random( MAP_WORLD_Y - 2 ); - - // there need to be mobile enemies here - that the queen has troops in towns we do not own is hardly worthy information, and empty sectors aren't interesting - if ( NumMobileEnemiesInSector( usX, usY ) == 0 ) - continue; - - // not if we already know about this sector - if ( SectorInfo[ SECTOR( usX, usY ) ].uiFlags & SF_ASSIGN_NOTICED_ENEMIES_HERE ) - continue; - - // enemy patrol detected - SectorInfo[ SECTOR( usX, usY ) ].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_HERE; - - if ( Chance(gGameExternalOptions.ubPrisonerProcessInfoNumberChance) ) - { - // we also learned the number of enemies - SectorInfo[ SECTOR( usX, usY ) ].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_KNOW_NUMBER; - } - - if ( Chance(gGameExternalOptions.ubPrisonerProcessInfoDirectionChance) ) - { - // we also learned the direction of the patrol - SectorInfo[ SECTOR( usX, usY ) ].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_KNOW_DIRECTION; - } - - ++revealedpositions; - - break; - } } // chance prisoner will grant us ransom money else if ( result < chances[PRISONER_INTERROGATION_DEFECT] + chances[PRISONER_INTERROGATION_INFO] + chances[PRISONER_INTERROGATION_RANSOM] ) @@ -7091,6 +7013,96 @@ void HandlePrisonerProcessingInSector( INT16 sMapX, INT16 sMapY, INT8 bZ ) ChangeNumberOfPrisoners( pSectorInfo, interrogatedprisoners ); } +// this function gives a random bit of information to the player. The type of info depends on aInfoType. +// If higher-order info cannot be given (for example if all info is already known to the player), lower-order info wil be given instead +// returns TRUE if info was given, FALSE if not +BOOLEAN GiveInfoToPlayer(UINT8 aInfoType) +{ + // there is a chance this guy might tell us about high-value targets! + if ( aInfoType == INFO_TYPE_VIP ) + { + UINT16 unknownvipector = 0; + if ( GetRandomUnknownVIPSector( unknownvipector ) ) + { + // make this guy known to the player + StrategicMap[unknownvipector].usFlags |= ENEMY_VIP_PRESENT_KNOWN; + + CHAR16 str[128]; + GetSectorIDString( SECTORX( unknownvipector ), SECTORY( unknownvipector ), 0, str, TRUE ); + + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szPrisonerTextStr[STR_PRISONER_DETECTION_VIP], str ); + + return TRUE; + } + } + + // mobile tanks are a serious threat. Learning of their location would be genuinely useful + // loop over all mobile enemy groups that have tanks that we don't yet know of, and pick one of them at random + std::vector sectorswithunknowntanksvector; + for ( GROUP *pGroup = gpGroupList; pGroup; pGroup = pGroup->next ) + { + if ( pGroup->usGroupTeam == ENEMY_TEAM && pGroup->pEnemyGroup->ubNumTanks ) + { + UINT8 tanksector = SECTOR( pGroup->ubSectorX, pGroup->ubSectorY ); + + // if we don't yet know that there are tanks here, this would be useful information + if ( !(SectorInfo[tanksector].uiFlags & SF_ASSIGN_NOTICED_ENEMIES_KNOW_NUMBER) ) + { + sectorswithunknowntanksvector.push_back( tanksector ); + } + } + } + + if ( !sectorswithunknowntanksvector.empty( ) ) + { + UINT8 tanksector = sectorswithunknowntanksvector[Random( sectorswithunknowntanksvector.size( ) )]; + + SectorInfo[tanksector].uiFlags |= (SF_ASSIGN_NOTICED_ENEMIES_HERE | SF_ASSIGN_NOTICED_ENEMIES_KNOW_NUMBER); + + if ( Chance( gGameExternalOptions.ubPrisonerProcessInfoDirectionChance ) ) + { + // we also learned the direction of the patrol + SectorInfo[tanksector].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_KNOW_DIRECTION; + } + + return TRUE; + } + + UINT8 maxtries = 20; + for ( UINT8 infotry = 0; infotry < maxtries; ++infotry ) + { + UINT8 usX = 1 + Random( MAP_WORLD_X - 2 ); + UINT8 usY = 1 + Random( MAP_WORLD_Y - 2 ); + + // not if we already know about this sector + if ( SectorInfo[SECTOR( usX, usY )].uiFlags & SF_ASSIGN_NOTICED_ENEMIES_HERE ) + continue; + + // there need to be mobile enemies here - that the queen has troops in towns we do not own is hardly worthy information, and empty sectors aren't interesting + if ( NumMobileEnemiesInSector( usX, usY ) == 0 ) + continue; + + // enemy patrol detected + SectorInfo[SECTOR( usX, usY )].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_HERE; + + if ( Chance( gGameExternalOptions.ubPrisonerProcessInfoNumberChance ) ) + { + // we also learned the number of enemies + SectorInfo[SECTOR( usX, usY )].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_KNOW_NUMBER; + } + + if ( Chance( gGameExternalOptions.ubPrisonerProcessInfoDirectionChance ) ) + { + // we also learned the direction of the patrol + SectorInfo[SECTOR( usX, usY )].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_KNOW_DIRECTION; + } + + return TRUE; + } + + return FALSE; +} + // Flugente: prisons can riot if there aren't enough guards around void HandlePrison( INT16 sMapX, INT16 sMapY, INT8 bZ ) { diff --git a/Strategic/Assignments.h b/Strategic/Assignments.h index 3c7db283b..46b1eab6d 100644 --- a/Strategic/Assignments.h +++ b/Strategic/Assignments.h @@ -271,8 +271,19 @@ FLOAT GetBestSAMOperatorCTH_Player( INT16 sSectorX, INT16 sSectorY, INT16 sSecto INT16 GetTrainWorkerPts(SOLDIERTYPE *pSoldier); +// Flugente: via various means, the player can get info on the enemy - this can be enemy positions, movement, or the position of important targets +enum +{ + INFO_TYPE_NORMAL, + INFO_TYPE_VIP, -// Flugente: determine max items we can move, and the sector distance + INFO_TYPE_MAX, +}; + +// this function gives a random bit of information to the player. The type of info depends on aInfoType. +// If higher-order info cannot be given (for example if all info is already known to the player), lower-order info wil be given instead +// returns TRUE if info was given, FALSE if not +BOOLEAN GiveInfoToPlayer( UINT8 aInfoType ); // get bonus tarining pts due to an instructor for this student // HEADROCK HAM 3.5: Three functions below have lost an argument which is no longer required ("uiAtGunRange", which was "uiAtFacility" in HAM 3.4) diff --git a/Strategic/LuaInitNPCs.cpp b/Strategic/LuaInitNPCs.cpp index e545e7670..7f4a5356d 100644 --- a/Strategic/LuaInitNPCs.cpp +++ b/Strategic/LuaInitNPCs.cpp @@ -840,6 +840,7 @@ static int l_ProfilesStrategicInsertionData (lua_State *L); static int l_ResetBoxers( lua_State *L ); static int l_AddVolunteers( lua_State *L ); +static int l_AddInfo( lua_State *L ); static int l_CreateArmedCivilain( lua_State *L ); static int l_CreateCivilian( lua_State *L ); @@ -850,6 +851,12 @@ static int l_RemoveFortification( lua_State *L ); static int l_GetFact( lua_State *L ); static int l_SetFact( lua_State *L ); +static int l_GetModderLUAFact( lua_State *L ); +static int l_SetModderLUAFact( lua_State *L ); + +static int l_SetScreenMsg( lua_State *L ); +static int l_GetUsedLanguage( lua_State *L ); + using namespace std; UINT16 idProfil; @@ -1699,6 +1706,7 @@ void IniFunction(lua_State *L, BOOLEAN bQuests ) lua_register(L,"StartDialogueMessageBox", l_StartDialogueMessageBox); lua_register( L, "AddVolunteers", l_AddVolunteers ); + lua_register( L, "AddInfo", l_AddInfo ); lua_register(L, "CreateArmedCivilain", l_CreateArmedCivilain ); lua_register( L, "CreateCivilian", l_CreateCivilian ); @@ -1708,6 +1716,12 @@ void IniFunction(lua_State *L, BOOLEAN bQuests ) lua_register(L, "GetFact", l_GetFact ); lua_register(L, "SetFact", l_SetFact ); + + lua_register( L, "GetModderLUAFact", l_GetModderLUAFact ); + lua_register( L, "SetModderLUAFact", l_SetModderLUAFact ); + + lua_register( L, "SetScreenMsg", l_SetScreenMsg ); + lua_register( L, "GetUsedLanguage", l_GetUsedLanguage ); } #ifdef NEWMUSIC BOOLEAN LetLuaMusicControl(UINT8 Init) @@ -13011,6 +13025,19 @@ static int l_AddVolunteers( lua_State *L ) return 0; } +// add info +static int l_AddInfo( lua_State *L ) +{ + if ( lua_gettop( L ) ) + { + UINT8 infotype = lua_tointeger( L, 1 ); + + GiveInfoToPlayer( infotype ); + } + + return 0; +} + static int l_CreateArmedCivilain( lua_State *L ) { if ( lua_gettop( L ) >= 4 ) @@ -13098,7 +13125,7 @@ static int l_BuildFortification( lua_State *L ) static int l_RemoveFortification( lua_State *L ) { - if ( lua_gettop( L ) ) + if ( lua_gettop( L ) >= 3 ) { INT32 sGridNo = lua_tointeger( L, 1 ); INT8 sLevel = lua_tointeger( L, 2 ); @@ -13112,23 +13139,20 @@ static int l_RemoveFortification( lua_State *L ) static int l_GetFact( lua_State *L ) { - UINT8 val = 0; - if ( lua_gettop( L ) ) { UINT16 fact = lua_tointeger( L, 1 ); + UINT8 val = GetFact( fact ); - val = GetFact( fact ); + lua_pushinteger( L, val ); } - lua_pushinteger( L, val ); - return 1; } static int l_SetFact( lua_State *L ) { - if ( lua_gettop( L ) ) + if ( lua_gettop( L ) >= 2 ) { UINT16 fact = lua_tointeger( L, 1 ); UINT8 val = lua_tointeger( L, 2 ); @@ -13138,3 +13162,82 @@ static int l_SetFact( lua_State *L ) return 0; } + +extern INT32 gubModderLuaData[MODDER_LUA_DATA_MAX]; + +static int l_GetModderLUAFact( lua_State *L ) +{ + if ( lua_gettop( L ) ) + { + UINT16 num = lua_tointeger( L, 1 ); + + if ( num < MODDER_LUA_DATA_MAX ) + lua_pushinteger( L, gubModderLuaData[num] ); + } + + return 1; +} + +static int l_SetModderLUAFact( lua_State *L ) +{ + if ( lua_gettop( L ) >= 2 ) + { + UINT16 num = lua_tointeger( L, 1 ); + INT32 val = lua_tointeger( L, 2 ); + + if ( num < MODDER_LUA_DATA_MAX ) + gubModderLuaData[num] = val; + } + + return 0; +} + +static int l_SetScreenMsg( lua_State *L ) +{ + if ( lua_gettop( L ) >= 2 ) + { + UINT16 usColor = lua_tointeger( L, 1 ); + + size_t len = 0; + const char* str = lua_tolstring( L, 2, &len ); + + CHAR16 w_str[1000]; + + MultiByteToWideChar( CP_UTF8, 0, str, -1, w_str, sizeof(w_str) / sizeof(w_str[0]) ); + w_str[sizeof(w_str) / sizeof(w_str[0]) - 1] = '\0'; + + ScreenMsg( usColor, MSG_INTERFACE, w_str ); + } + + return 0; +} + +static int l_GetUsedLanguage( lua_State *L ) +{ + if ( lua_gettop( L ) ) + { + INT32 val = 0; + +#ifdef ENGLISH + val = 0; +#elif GERMAN + val = 1; +#elif RUSSIAN + val = 2; +#elif DUTCH + val = 3; +#elif POLISH + val = 4; +#elif FRENCH + val = 5; +#elif ITALIAN + val = 6; +#elif CHINESE + val = 7; +#endif + + lua_pushinteger( L, val ); + } + + return 1; +} diff --git a/Strategic/Quests.cpp b/Strategic/Quests.cpp index 6afa3e7ca..4fd38fe2e 100644 --- a/Strategic/Quests.cpp +++ b/Strategic/Quests.cpp @@ -60,6 +60,9 @@ extern SOLDIERTYPE * gpDestSoldier; UINT8 gubQuest[MAX_QUESTS]; UINT8 gubFact[ NUM_FACTS ]; // this has to be updated when we figure out how many facts we have +// Flugente: we save and load this array. It is to be used exclusively by modders, who can set this in lua scripts +INT32 gubModderLuaData[MODDER_LUA_DATA_MAX]; // this has to be updated when we figure out how many facts we have + INT16 gsFoodQuestSectorX; INT16 gsFoodQuestSectorY; @@ -1578,6 +1581,8 @@ void InitQuestEngine() gubBoxingMatchesWon = 0; gubBoxersRests = 0; gfBoxersResting = FALSE; + + InitLUAModderData( ); } @@ -1643,7 +1648,6 @@ BOOLEAN SaveQuestInfoToSavedGameFile( HWFILE hFile ) return(FALSE); } - return( TRUE ); } @@ -1671,6 +1675,41 @@ BOOLEAN LoadQuestInfoFromSavedGameFile( HWFILE hFile, UINT8 MaxQuest ) return( TRUE ); } +// Flugente: a huge array that can be used exclusively by modders +BOOLEAN SaveLUAModderDataToSavedGameFile( HWFILE hFile ) +{ + UINT32 uiNumBytesWritten; + + //Save all the states for the facts + FileWrite( hFile, gubModderLuaData, sizeof(INT32)* MODDER_LUA_DATA_MAX, &uiNumBytesWritten ); + if ( uiNumBytesWritten != sizeof(INT32)* MODDER_LUA_DATA_MAX ) + { + return(FALSE); + } + + return(TRUE); +} + +BOOLEAN LoadLUAModderDataFromSavedGameFile( HWFILE hFile ) +{ + UINT32 uiNumBytesRead; + + //Save all the states for the facts + FileRead( hFile, gubModderLuaData, sizeof(INT32)* MODDER_LUA_DATA_MAX, &uiNumBytesRead ); + if ( uiNumBytesRead != sizeof(INT32)* MODDER_LUA_DATA_MAX ) + { + return(FALSE); + } + + return(TRUE); +} + +void InitLUAModderData( ) +{ + memset( gubModderLuaData, 0, sizeof(gubModderLuaData) ); +} + + // SANDRO - a function to add quest-done point to merc records, and award some exp possibly void GiveQuestRewardPoint( INT16 sQuestSectorX, INT16 sQuestsSectorY, INT8 bExpReward, UINT8 bException ) { diff --git a/Strategic/Quests.h b/Strategic/Quests.h index f41a73c4b..f9944c59a 100644 --- a/Strategic/Quests.h +++ b/Strategic/Quests.h @@ -649,6 +649,13 @@ void InitQuestEngine(); BOOLEAN LoadQuestInfoFromSavedGameFile( HWFILE hFile, UINT8 MaxQuest ); BOOLEAN SaveQuestInfoToSavedGameFile( HWFILE hFile ); +// Flugente: a huge array that can be used exclusively by modders +#define MODDER_LUA_DATA_MAX 1000 + +BOOLEAN LoadLUAModderDataFromSavedGameFile( HWFILE hFile ); +BOOLEAN SaveLUAModderDataToSavedGameFile( HWFILE hFile ); +void InitLUAModderData(); + // added by SANDRO void GiveQuestRewardPoint( INT16 sQuestSectorX, INT16 sQuestsSectorY, INT8 bExpReward, UINT8 bException );