@ -231,6 +231,16 @@ public:
}
} ;
struct MonsterConversionData {
int8_t monsterLevel ;
uint16_t experience ;
uint8_t toHitSpecial ;
} ;
struct LevelConversionData {
MonsterConversionData monsterConversionData [ MaxMonsters ] ;
} ;
void LoadItemData ( LoadHelper & file , Item & item )
{
item . _iSeed = file . NextLE < uint32_t > ( ) ;
@ -577,7 +587,7 @@ void LoadPlayer(LoadHelper &file, Player &player)
bool gbSkipSync = false ;
void LoadMonster ( LoadHelper * file , Monster & monster )
void LoadMonster ( LoadHelper * file , Monster & monster , MonsterConversionData * monsterConversionData = nullptr )
{
monster . levelType = file - > NextLE < int32_t > ( ) ;
monster . mode = static_cast < MonsterMode > ( file - > NextLE < int32_t > ( ) ) ;
@ -639,9 +649,15 @@ void LoadMonster(LoadHelper *file, Monster &monster)
monster . corpseId = file - > NextLE < int8_t > ( ) ;
monster . whoHit = file - > NextLE < int8_t > ( ) ;
file - > Skip ( 1 ) ; // Skip level - now calculated on the fly
file - > Skip ( 1 ) ; // Alignment
file - > Skip ( 2 ) ; // Skip exp - now calculated from monstdat when the monster dies
if ( monsterConversionData ! = nullptr )
monsterConversionData - > monsterLevel = file - > NextLE < int8_t > ( ) ;
else
file - > Skip ( 1 ) ; // Skip level - now calculated on the fly
file - > Skip ( 1 ) ; // Alignment
if ( monsterConversionData ! = nullptr )
monsterConversionData - > experience = file - > NextLE < uint16_t > ( ) ;
else
file - > Skip ( 2 ) ; // Skip exp - now calculated from monstdat when the monster dies
if ( monster . isPlayerMinion ( ) ) // Don't skip for golems
monster . toHit = file - > NextLE < uint8_t > ( ) ;
@ -649,7 +665,10 @@ void LoadMonster(LoadHelper *file, Monster &monster)
file - > Skip ( 1 ) ; // Skip hit as it's already initialized
monster . minDamage = file - > NextLE < uint8_t > ( ) ;
monster . maxDamage = file - > NextLE < uint8_t > ( ) ;
file - > Skip ( 1 ) ; // Skip toHitSpecial as it's already initialized
if ( monsterConversionData ! = nullptr )
monsterConversionData - > toHitSpecial = file - > NextLE < uint8_t > ( ) ;
else
file - > Skip ( 1 ) ; // Skip toHitSpecial as it's already initialized
monster . minDamageSpecial = file - > NextLE < uint8_t > ( ) ;
monster . maxDamageSpecial = file - > NextLE < uint8_t > ( ) ;
monster . armorClass = file - > NextLE < uint8_t > ( ) ;
@ -1374,7 +1393,7 @@ void SavePlayer(SaveHelper &file, const Player &player)
// Omit pointer pReserved
}
void SaveMonster ( SaveHelper * file , Monster & monster )
void SaveMonster ( SaveHelper * file , Monster & monster , MonsterConversionData * monsterConversionData = nullptr )
{
file - > WriteLE < int32_t > ( monster . levelType ) ;
file - > WriteLE < int32_t > ( static_cast < int > ( monster . mode ) ) ;
@ -1446,14 +1465,23 @@ void SaveMonster(SaveHelper *file, Monster &monster)
file - > WriteLE < int8_t > ( monster . corpseId ) ;
file - > WriteLE < int8_t > ( monster . whoHit ) ;
file - > WriteLE < int8_t > ( static_cast < int8_t > ( monster . level ( sgGameInitInfo . nDifficulty ) ) ) ;
if ( monsterConversionData ! = nullptr )
file - > WriteLE < int8_t > ( monsterConversionData - > monsterLevel ) ;
else
file - > WriteLE < int8_t > ( static_cast < int8_t > ( monster . level ( sgGameInitInfo . nDifficulty ) ) ) ;
file - > Skip ( 1 ) ; // Alignment
file - > WriteLE < uint16_t > ( static_cast < uint16_t > ( std : : min < unsigned > ( std : : numeric_limits < uint16_t > : : max ( ) , monster . exp ( sgGameInitInfo . nDifficulty ) ) ) ) ;
if ( monsterConversionData ! = nullptr )
file - > WriteLE < uint16_t > ( monsterConversionData - > experience ) ;
else
file - > WriteLE < uint16_t > ( static_cast < uint16_t > ( std : : min < unsigned > ( std : : numeric_limits < uint16_t > : : max ( ) , monster . exp ( sgGameInitInfo . nDifficulty ) ) ) ) ;
file - > WriteLE < uint8_t > ( static_cast < uint8_t > ( std : : min < uint16_t > ( monster . toHit , std : : numeric_limits < uint8_t > : : max ( ) ) ) ) ; // For backwards compatibility
file - > WriteLE < uint8_t > ( monster . minDamage ) ;
file - > WriteLE < uint8_t > ( monster . maxDamage ) ;
file - > WriteLE < uint8_t > ( static_cast < uint8_t > ( std : : min < uint16_t > ( monster . toHitSpecial ( sgGameInitInfo . nDifficulty ) , std : : numeric_limits < uint8_t > : : max ( ) ) ) ) ; // For backwards compatibility
if ( monsterConversionData ! = nullptr )
file - > WriteLE < uint8_t > ( monsterConversionData - > toHitSpecial ) ;
else
file - > WriteLE < uint8_t > ( static_cast < uint8_t > ( std : : min < uint16_t > ( monster . toHitSpecial ( sgGameInitInfo . nDifficulty ) , std : : numeric_limits < uint8_t > : : max ( ) ) ) ) ; // For backwards compatibility
file - > WriteLE < uint8_t > ( monster . minDamageSpecial ) ;
file - > WriteLE < uint8_t > ( monster . maxDamageSpecial ) ;
file - > WriteLE < uint8_t > ( monster . armorClass ) ;
@ -1786,6 +1814,187 @@ void LoadLevelSeeds()
}
}
void SaveLevel ( SaveWriter & saveWriter , LevelConversionData * levelConversionData )
{
Player & myPlayer = * MyPlayer ;
DoUnVision ( myPlayer . position . tile , myPlayer . _pLightRad ) ; // fix for vision staying on the level
if ( leveltype = = DTYPE_TOWN )
DungeonSeeds [ 0 ] = AdvanceRndSeed ( ) ;
char szName [ MaxMpqPathSize ] ;
GetTempLevelNames ( szName ) ;
SaveHelper file ( saveWriter , szName , 256 * 1024 ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < int8_t > ( dCorpse [ i ] [ j ] ) ;
}
}
file . WriteBE ( static_cast < int32_t > ( ActiveMonsterCount ) ) ;
file . WriteBE < int32_t > ( ActiveItemCount ) ;
file . WriteBE < int32_t > ( ActiveObjectCount ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( unsigned monsterId : ActiveMonsters )
file . WriteBE < uint32_t > ( monsterId ) ;
for ( size_t i = 0 ; i < ActiveMonsterCount ; i + + ) {
MonsterConversionData * monsterConversionData = nullptr ;
if ( levelConversionData ! = nullptr )
monsterConversionData = & levelConversionData - > monsterConversionData [ ActiveMonsters [ i ] ] ;
SaveMonster ( & file , Monsters [ ActiveMonsters [ i ] ] , monsterConversionData ) ;
}
for ( int objectId : ActiveObjects )
file . WriteLE < int8_t > ( objectId ) ;
for ( int objectId : AvailableObjects )
file . WriteLE < int8_t > ( objectId ) ;
for ( int i = 0 ; i < ActiveObjectCount ; i + + ) {
SaveObject ( file , Objects [ ActiveObjects [ i ] ] ) ;
}
}
auto itemIndexes = SaveDroppedItems ( file ) ;
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < uint8_t > ( static_cast < uint8_t > ( dFlags [ i ] [ j ] & DungeonFlag : : SavedFlags ) ) ;
}
SaveDroppedItemLocations ( file , itemIndexes ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteBE < int32_t > ( dMonster [ i ] [ j ] ) ;
}
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < int8_t > ( dObject [ i ] [ j ] ) ;
}
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < uint8_t > ( dLight [ i ] [ j ] ) ;
}
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < uint8_t > ( dPreLight [ i ] [ j ] ) ;
}
for ( int j = 0 ; j < DMAXY ; j + + ) {
for ( int i = 0 ; i < DMAXX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < uint8_t > ( AutomapView [ i ] [ j ] ) ;
}
}
if ( ! setlevel )
myPlayer . _pLvlVisited [ currlevel ] = true ;
else
myPlayer . _pSLvlVisited [ setlvlnum ] = true ;
}
void LoadLevel ( LevelConversionData * levelConversionData )
{
char szName [ MaxMpqPathSize ] ;
std : : optional < SaveReader > archive = OpenSaveArchive ( gSaveNumber ) ;
GetTempLevelNames ( szName ) ;
if ( ! archive | | ! archive - > HasFile ( szName ) )
GetPermLevelNames ( szName ) ;
LoadHelper file ( std : : move ( archive ) , szName ) ;
if ( ! file . IsValid ( ) )
app_fatal ( _ ( " Unable to open save file archive " ) ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
dCorpse [ i ] [ j ] = file . NextLE < int8_t > ( ) ;
}
MoveLightsToCorpses ( ) ;
}
ActiveMonsterCount = file . NextBE < int32_t > ( ) ;
auto savedItemCount = file . NextBE < uint32_t > ( ) ;
ActiveObjectCount = file . NextBE < int32_t > ( ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( unsigned & monsterId : ActiveMonsters )
monsterId = file . NextBE < uint32_t > ( ) ;
for ( size_t i = 0 ; i < ActiveMonsterCount ; i + + ) {
Monster & monster = Monsters [ ActiveMonsters [ i ] ] ;
MonsterConversionData * monsterConversionData = nullptr ;
if ( levelConversionData ! = nullptr )
monsterConversionData = & levelConversionData - > monsterConversionData [ ActiveMonsters [ i ] ] ;
LoadMonster ( & file , monster , monsterConversionData ) ;
if ( monster . isUnique ( ) & & monster . lightId ! = NO_LIGHT )
Lights [ monster . lightId ] . isInvalid = false ;
}
if ( ! gbSkipSync ) {
for ( size_t i = 0 ; i < ActiveMonsterCount ; i + + )
SyncMonsterAnim ( Monsters [ ActiveMonsters [ i ] ] ) ;
}
for ( int & objectId : ActiveObjects )
objectId = file . NextLE < int8_t > ( ) ;
for ( int & objectId : AvailableObjects )
objectId = file . NextLE < int8_t > ( ) ;
for ( int i = 0 ; i < ActiveObjectCount ; i + + )
LoadObject ( file , Objects [ ActiveObjects [ i ] ] ) ;
if ( ! gbSkipSync ) {
for ( int i = 0 ; i < ActiveObjectCount ; i + + )
SyncObjectAnim ( Objects [ ActiveObjects [ i ] ] ) ;
}
}
LoadDroppedItems ( file , savedItemCount ) ;
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
dFlags [ i ] [ j ] = static_cast < DungeonFlag > ( file . NextLE < uint8_t > ( ) ) & DungeonFlag : : LoadedFlags ;
}
// skip dItem indexes, this gets populated in LoadDroppedItems
file . Skip < uint8_t > ( MAXDUNX * MAXDUNY ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
dMonster [ i ] [ j ] = file . NextBE < int32_t > ( ) ;
}
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
dObject [ i ] [ j ] = file . NextLE < int8_t > ( ) ;
}
file . Skip < uint8_t > ( MAXDUNY * MAXDUNX ) ; // dLight
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
dPreLight [ i ] [ j ] = file . NextLE < uint8_t > ( ) ;
}
for ( int j = 0 ; j < DMAXY ; j + + ) {
for ( int i = 0 ; i < DMAXX ; i + + ) { // NOLINT(modernize-loop-convert)
const auto automapView = static_cast < MapExplorationType > ( file . NextLE < uint8_t > ( ) ) ;
AutomapView [ i ] [ j ] = automapView = = MAP_EXP_OLD ? MAP_EXP_SELF : automapView ;
}
}
// No need to load dLight, we can recreate it accurately from LightList
memcpy ( dLight , dPreLight , sizeof ( dLight ) ) ; // resets the light on entering a level to get rid of incorrect light
ChangeLightXY ( Players [ MyPlayerId ] . lightId , Players [ MyPlayerId ] . position . tile ) ; // forces player light refresh
} else {
memset ( dLight , 0 , sizeof ( dLight ) ) ;
}
if ( ! gbSkipSync ) {
AutomapZoomReset ( ) ;
ResyncQuests ( ) ;
RedoMissileFlags ( ) ;
UpdateLighting = true ;
}
for ( Player & player : Players ) {
if ( player . plractive & & player . isOnActiveLevel ( ) )
Lights [ player . lightId ] . hasChanged = true ;
}
}
const int DiabloItemSaveSize = 368 ;
const int HellfireItemSaveSize = 372 ;
@ -1809,8 +2018,9 @@ void ConvertLevels(SaveWriter &saveWriter)
leveltype = GetLevelType ( currlevel ) ;
LoadLevel ( ) ;
SaveLevel ( saveWriter ) ;
LevelConversionData levelConversionData ;
LoadLevel ( & levelConversionData ) ;
SaveLevel ( saveWriter , & levelConversionData ) ;
}
setlevel = true ; // Convert quest levels
@ -1828,8 +2038,9 @@ void ConvertLevels(SaveWriter &saveWriter)
if ( ! LevelFileExists ( saveWriter ) )
continue ;
LoadLevel ( ) ;
SaveLevel ( saveWriter ) ;
LevelConversionData levelConversionData ;
LoadLevel ( & levelConversionData ) ;
SaveLevel ( saveWriter , & levelConversionData ) ;
}
gbSkipSync = false ;
@ -2579,176 +2790,12 @@ void SaveGame()
void SaveLevel ( SaveWriter & saveWriter )
{
Player & myPlayer = * MyPlayer ;
DoUnVision ( myPlayer . position . tile , myPlayer . _pLightRad ) ; // fix for vision staying on the level
if ( leveltype = = DTYPE_TOWN )
DungeonSeeds [ 0 ] = AdvanceRndSeed ( ) ;
char szName [ MaxMpqPathSize ] ;
GetTempLevelNames ( szName ) ;
SaveHelper file ( saveWriter , szName , 256 * 1024 ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < int8_t > ( dCorpse [ i ] [ j ] ) ;
}
}
file . WriteBE ( static_cast < int32_t > ( ActiveMonsterCount ) ) ;
file . WriteBE < int32_t > ( ActiveItemCount ) ;
file . WriteBE < int32_t > ( ActiveObjectCount ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( unsigned monsterId : ActiveMonsters )
file . WriteBE < uint32_t > ( monsterId ) ;
for ( size_t i = 0 ; i < ActiveMonsterCount ; i + + )
SaveMonster ( & file , Monsters [ ActiveMonsters [ i ] ] ) ;
for ( int objectId : ActiveObjects )
file . WriteLE < int8_t > ( objectId ) ;
for ( int objectId : AvailableObjects )
file . WriteLE < int8_t > ( objectId ) ;
for ( int i = 0 ; i < ActiveObjectCount ; i + + ) {
SaveObject ( file , Objects [ ActiveObjects [ i ] ] ) ;
}
}
auto itemIndexes = SaveDroppedItems ( file ) ;
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < uint8_t > ( static_cast < uint8_t > ( dFlags [ i ] [ j ] & DungeonFlag : : SavedFlags ) ) ;
}
SaveDroppedItemLocations ( file , itemIndexes ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteBE < int32_t > ( dMonster [ i ] [ j ] ) ;
}
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < int8_t > ( dObject [ i ] [ j ] ) ;
}
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < uint8_t > ( dLight [ i ] [ j ] ) ;
}
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < uint8_t > ( dPreLight [ i ] [ j ] ) ;
}
for ( int j = 0 ; j < DMAXY ; j + + ) {
for ( int i = 0 ; i < DMAXX ; i + + ) // NOLINT(modernize-loop-convert)
file . WriteLE < uint8_t > ( AutomapView [ i ] [ j ] ) ;
}
}
if ( ! setlevel )
myPlayer . _pLvlVisited [ currlevel ] = true ;
else
myPlayer . _pSLvlVisited [ setlvlnum ] = true ;
SaveLevel ( saveWriter , nullptr ) ;
}
void LoadLevel ( )
{
char szName [ MaxMpqPathSize ] ;
std : : optional < SaveReader > archive = OpenSaveArchive ( gSaveNumber ) ;
GetTempLevelNames ( szName ) ;
if ( ! archive | | ! archive - > HasFile ( szName ) )
GetPermLevelNames ( szName ) ;
LoadHelper file ( std : : move ( archive ) , szName ) ;
if ( ! file . IsValid ( ) )
app_fatal ( _ ( " Unable to open save file archive " ) ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
dCorpse [ i ] [ j ] = file . NextLE < int8_t > ( ) ;
}
MoveLightsToCorpses ( ) ;
}
ActiveMonsterCount = file . NextBE < int32_t > ( ) ;
auto savedItemCount = file . NextBE < uint32_t > ( ) ;
ActiveObjectCount = file . NextBE < int32_t > ( ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( unsigned & monsterId : ActiveMonsters )
monsterId = file . NextBE < uint32_t > ( ) ;
for ( size_t i = 0 ; i < ActiveMonsterCount ; i + + ) {
Monster & monster = Monsters [ ActiveMonsters [ i ] ] ;
LoadMonster ( & file , monster ) ;
if ( monster . isUnique ( ) & & monster . lightId ! = NO_LIGHT )
Lights [ monster . lightId ] . isInvalid = false ;
}
if ( ! gbSkipSync ) {
for ( size_t i = 0 ; i < ActiveMonsterCount ; i + + )
SyncMonsterAnim ( Monsters [ ActiveMonsters [ i ] ] ) ;
}
for ( int & objectId : ActiveObjects )
objectId = file . NextLE < int8_t > ( ) ;
for ( int & objectId : AvailableObjects )
objectId = file . NextLE < int8_t > ( ) ;
for ( int i = 0 ; i < ActiveObjectCount ; i + + )
LoadObject ( file , Objects [ ActiveObjects [ i ] ] ) ;
if ( ! gbSkipSync ) {
for ( int i = 0 ; i < ActiveObjectCount ; i + + )
SyncObjectAnim ( Objects [ ActiveObjects [ i ] ] ) ;
}
}
LoadDroppedItems ( file , savedItemCount ) ;
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
dFlags [ i ] [ j ] = static_cast < DungeonFlag > ( file . NextLE < uint8_t > ( ) ) & DungeonFlag : : LoadedFlags ;
}
// skip dItem indexes, this gets populated in LoadDroppedItems
file . Skip < uint8_t > ( MAXDUNX * MAXDUNY ) ;
if ( leveltype ! = DTYPE_TOWN ) {
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
dMonster [ i ] [ j ] = file . NextBE < int32_t > ( ) ;
}
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
dObject [ i ] [ j ] = file . NextLE < int8_t > ( ) ;
}
file . Skip < uint8_t > ( MAXDUNY * MAXDUNX ) ; // dLight
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) // NOLINT(modernize-loop-convert)
dPreLight [ i ] [ j ] = file . NextLE < uint8_t > ( ) ;
}
for ( int j = 0 ; j < DMAXY ; j + + ) {
for ( int i = 0 ; i < DMAXX ; i + + ) { // NOLINT(modernize-loop-convert)
const auto automapView = static_cast < MapExplorationType > ( file . NextLE < uint8_t > ( ) ) ;
AutomapView [ i ] [ j ] = automapView = = MAP_EXP_OLD ? MAP_EXP_SELF : automapView ;
}
}
// No need to load dLight, we can recreate it accurately from LightList
memcpy ( dLight , dPreLight , sizeof ( dLight ) ) ; // resets the light on entering a level to get rid of incorrect light
ChangeLightXY ( Players [ MyPlayerId ] . lightId , Players [ MyPlayerId ] . position . tile ) ; // forces player light refresh
} else {
memset ( dLight , 0 , sizeof ( dLight ) ) ;
}
if ( ! gbSkipSync ) {
AutomapZoomReset ( ) ;
ResyncQuests ( ) ;
RedoMissileFlags ( ) ;
UpdateLighting = true ;
}
for ( Player & player : Players ) {
if ( player . plractive & & player . isOnActiveLevel ( ) )
Lights [ player . lightId ] . hasChanged = true ;
}
LoadLevel ( nullptr ) ;
}
} // namespace devilution