diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/common.hh | 2 | ||||
| -rw-r--r-- | src/map.cc | 403 | ||||
| -rw-r--r-- | src/map.hh | 5 | ||||
| -rw-r--r-- | src/stubs.hh | 5 | ||||
| -rw-r--r-- | src/util.cc | 2 |
5 files changed, 411 insertions, 6 deletions
diff --git a/src/common.hh b/src/common.hh index 58a28c3..bd3ccd7 100644 --- a/src/common.hh +++ b/src/common.hh @@ -181,7 +181,7 @@ struct TReadStream { struct TReadBuffer: TReadStream { // REGULAR FUNCTIONS // ========================================================================= - TReadBuffer(uint8 *Data, int Size); + TReadBuffer(const uint8 *Data, int Size); // VIRTUAL FUNCTIONS // ========================================================================= @@ -970,6 +970,7 @@ void LoadSector(const char *FileName, int SectorX, int SectorY, int SectorZ){ OffsetX = (int)SectorOffset[0]; OffsetY = (int)SectorOffset[1]; Script.readSymbol(':'); + // TODO(fusion): Probably check if offsets are within bounds? continue; } @@ -1297,6 +1298,406 @@ void SaveMap(void){ SavingMap = false; } +void RefreshSector(int SectorX, int SectorY, int SectorZ, TReadStream *Stream){ + // TODO(fusion): `matrix3d::at` will return the first entry if the coordinates + // are out of bounds which that is problematic, specially here. + if(SectorX < SectorXMin || SectorXMax < SectorX + || SectorY < SectorYMin || SectorYMax < SectorY + || SectorZ < SectorZMin || SectorZMax < SectorZ){ + error("RefreshSector: Sector %d/%d/%d is out of bounds.", + SectorX, SectorY, SectorZ); + return; + } + + ASSERT(Sector); + TSector *Sec = *Sector->at(SectorX, SectorY, SectorZ); + if(Sec && (Sec->MapFlags & 0x01) != 0){ + print(3, "Refreshe Sektor %d/%d/%d ...\n", SectorX, SectorY, SectorZ); + while(!Stream->eof()){ + uint8 OffsetX = Stream->readByte(); + uint8 OffsetY = Stream->readByte(); + if(OffsetX < 32 && OffsetY < 32){ + Object Con = Sec->MapCon[OffsetX][OffsetY]; + + // TODO(fusion): This loop was done a bit differently but I suppose + // iterating it directly is clearer and as long as we don't access + // the object after `DeleteObject`, it should work the same. + Object Obj = Con.getAttribute(CONTENT); + while(Obj != NONE){ + Object Next = Obj.getNextObject(); + if(!Obj.getObjectType().isCreatureContainer()){ + DeleteObject(Obj); + } + Obj = Next; + } + + LoadObjects(Stream, Con); + } + } + } +} + +void PatchSector(int SectorX, int SectorY, int SectorZ, bool FullSector, + TReadScriptFile *Script, bool SaveHouses){ + if(SectorX < SectorXMin || SectorXMax < SectorX + || SectorY < SectorYMin || SectorYMax < SectorY + || SectorZ < SectorZMin || SectorZMax < SectorZ){ + error("PatchSector: Sector %d/%d/%d is out of bounds.", + SectorX, SectorY, SectorZ); + return; + } + + ASSERT(Sector); + TSector *Sec = *Sector->at(SectorX, SectorY, SectorZ); + bool NewSector = (Sec == NULL); + if(NewSector){ + print(2, "Lege Sektor %d/%d/%d neu an.\n", SectorX, SectorY, SectorZ); + InitSector(SectorX, SectorY, SectorZ); + Sec = *Sector->at(SectorX, SectorY, SectorZ); + ASSERT(Sec != NULL); + } + + bool FieldTreated[32][32] = {}; + bool FieldPatched[32][32] = {}; + + // NOTE(fusion): Step 1. + // Patch fields specified in the input script. House fields are NOT patched + // if `SaveHouses` is set. + { + bool House = false; + int OffsetX = -1; + int OffsetY = -1; + while(true){ + Script->nextToken(); + if(Script->Token == ENDOFFILE){ + break; + } + + if(Script->Token == SPECIAL && Script->getSpecial() == ','){ + continue; + } + + if(Script->Token == BYTES){ + uint8 *SectorOffset = Script->getBytesequence(); + OffsetX = (int)SectorOffset[0]; + OffsetY = (int)SectorOffset[1]; + Script->readSymbol(':'); + + // TODO(fusion): Probably check if offsets are within bounds? + FieldTreated[OffsetX][OffsetY] = true; + int CoordX = SectorX * 32 + OffsetX; + int CoordY = SectorY * 32 + OffsetY; + int CoordZ = SectorZ; + + // TODO(fusion): Maybe some inlined function? + House = IsHouse(CoordX, CoordY, CoordZ); + if(!House && CoordinateFlag(CoordX, CoordY, CoordZ, HOOKSOUTH)){ + House = IsHouse(CoordX - 1, CoordY + 1, CoordZ) + || IsHouse(CoordX, CoordY + 1, CoordZ) + || IsHouse(CoordX + 1, CoordY + 1, CoordZ); + } + if(!House && CoordinateFlag(CoordX, CoordY, CoordZ, HOOKEAST)){ + House = IsHouse(CoordX + 1, CoordY - 1, CoordZ) + || IsHouse(CoordX + 1, CoordY, CoordZ) + || IsHouse(CoordX + 1, CoordY + 1, CoordZ); + } + + if(House){ + if(SaveHouses){ + continue; + } + CleanHouseField(CoordX, CoordY, CoordZ); + } + + // NOTE(fusion): Similar to `RefreshSector`. + Object Con = Sec->MapCon[OffsetX][OffsetY]; + Object Obj = Con.getAttribute(CONTENT); + while(Obj != NONE){ + Object Next = Obj.getNextObject(); + if(!Obj.getObjectType().isCreatureContainer()){ + DeleteObject(Obj); + } + Obj = Next; + } + // NOTE(fusion): Clear map container flags. See note in `GetObjectCoordinates`. + AccessObject(Con)->Attributes[3] &= 0xFFFF00FF; + FieldPatched[OffsetX][OffsetY] = true; + continue; + } + + if(Script->Token != IDENTIFIER){ + Script->error("next map point expected"); + } + + if(OffsetX == -1 || OffsetY == -1){ + Script->error("coordinate expected"); + } + + const char *Identifier = Script->getIdentifier(); + if(strcmp(Identifier, "refresh") == 0){ + Sec->MapFlags |= 1; + if(!House || !SaveHouses){ + AccessObject(Sec->MapCon[OffsetX][OffsetY])->Attributes[3] |= 0x100; + } + }else if(strcmp(Identifier, "nologout") == 0){ + Sec->MapFlags |= 2; + if(!House || !SaveHouses){ + AccessObject(Sec->MapCon[OffsetX][OffsetY])->Attributes[3] |= 0x200; + } + }else if(strcmp(Identifier, "protectionzone") == 0){ + Sec->MapFlags |= 4; + if(!House || !SaveHouses){ + AccessObject(Sec->MapCon[OffsetX][OffsetY])->Attributes[3] |= 0x400; + } + }else if(strcmp(Identifier, "content") == 0){ + Script->readSymbol('='); + HelpBuffer.reset(); + if(!House || !SaveHouses){ + LoadObjects(Script, &HelpBuffer, false); + TReadBuffer ReadBuffer(HelpBuffer.Data, HelpBuffer.Position); + LoadObjects(&ReadBuffer, Sec->MapCon[OffsetX][OffsetY]); + }else{ + // NOTE(fusion): Skip content. + LoadObjects(Script, &HelpBuffer, true); + } + }else{ + Script->error("unknown map flag"); + } + } + } + + // NOTE(fusion): Step 2. + // Patch fields not specified in the input script if `FullSector` is set. + // Note that patching in this case is simply deleting a field's objects. + // House fields are NOT patched if `SaveHouses` is set. + if(FullSector){ + for(int OffsetX = 0; OffsetX < 32; OffsetX += 1) + for(int OffsetY = 0; OffsetY < 32; OffsetY += 1){ + if(FieldTreated[OffsetX][OffsetY]){ + continue; + } + + int CoordX = SectorX * 32 + OffsetX; + int CoordY = SectorY * 32 + OffsetY; + int CoordZ = SectorZ; + + // TODO(fusion): Maybe some inlined function? + bool House = IsHouse(CoordX, CoordY, CoordZ); + if(!House && CoordinateFlag(CoordX, CoordY, CoordZ, HOOKSOUTH)){ + House = IsHouse(CoordX - 1, CoordY + 1, CoordZ) + || IsHouse(CoordX, CoordY + 1, CoordZ) + || IsHouse(CoordX + 1, CoordY + 1, CoordZ); + } + if(!House && CoordinateFlag(CoordX, CoordY, CoordZ, HOOKEAST)){ + House = IsHouse(CoordX + 1, CoordY - 1, CoordZ) + || IsHouse(CoordX + 1, CoordY, CoordZ) + || IsHouse(CoordX + 1, CoordY + 1, CoordZ); + } + + if(House){ + if(SaveHouses){ + continue; + } + CleanHouseField(CoordX, CoordY, CoordZ); + } + + // NOTE(fusion): Same as in the parsing loop above. + Object Con = Sec->MapCon[OffsetX][OffsetY]; + Object Obj = Con.getAttribute(CONTENT); + while(Obj != NONE){ + Object Next = Obj.getNextObject(); + if(!Obj.getObjectType().isCreatureContainer()){ + DeleteObject(Obj); + } + Obj = Next; + } + AccessObject(Con)->Attributes[3] &= 0xFFFF00FF; + FieldPatched[OffsetX][OffsetY] = true; + } + } + + // NOTE(fusion): Step 3. + // Parse original sector file, transfering non patched fields to a new sector file. + char FileName[4096]; + char FileNameBak[4096]; + snprintf(FileName, sizeof(FileName), "%s/%04d-%04d-%02d.sec", + ORIGMAPPATH, SectorX, SectorY, SectorZ); + snprintf(FileNameBak, sizeof(FileNameBak), "%s/%04d-%04d-%02d.sec~", + ORIGMAPPATH, SectorX, SectorY, SectorZ); + + TWriteScriptFile OUT; + OUT.open(FileNameBak); + OUT.writeText("# Tibia - graphical Multi-User-Dungeon"); + OUT.writeLn(); + OUT.writeText("# Data for sector "); + OUT.writeNumber(SectorX); + OUT.writeText("/"); + OUT.writeNumber(SectorY); + OUT.writeText("/"); + OUT.writeNumber(SectorZ); + OUT.writeLn(); + OUT.writeLn(); + + if(!NewSector){ + TReadScriptFile IN; + IN.open(FileName); + + int OffsetX = -1; + int OffsetY = -1; + int AttrCount = 0; + while(true){ + IN.nextToken(); + if(IN.Token == ENDOFFILE){ + IN.close(); + break; + } + + if(IN.Token == SPECIAL && IN.getSpecial() == ','){ + continue; + } + + if(IN.Token == BYTES){ + uint8 *SectorOffset = IN.getBytesequence(); + OffsetX = (int)SectorOffset[0]; + OffsetY = (int)SectorOffset[1]; + IN.readSymbol(':'); + AttrCount = 0; + // TODO(fusion): Probably check if offsets are within bounds? + if(!FieldPatched[OffsetX][OffsetY]){ + OUT.writeNumber(OffsetX); + OUT.writeText("-"); + OUT.writeNumber(OffsetY); + OUT.writeText(": "); + } + continue; + } + + if(IN.Token != IDENTIFIER){ + IN.error("next map point expected"); + } + + if(OffsetX == -1 || OffsetY == -1){ + IN.error("coordinate expected"); + } + + const char *Identifier = IN.getIdentifier(); + if(strcmp(Identifier, "refresh") == 0){ + if(!FieldPatched[OffsetX][OffsetY]){ + if(AttrCount > 0){ + OUT.writeText(", "); + } + OUT.writeText("Refresh"); + AttrCount += 1; + } + }else if(strcmp(Identifier, "nologout") == 0){ + if(!FieldPatched[OffsetX][OffsetY]){ + if(AttrCount > 0){ + OUT.writeText(", "); + } + OUT.writeText("NoLogout"); + AttrCount += 1; + } + }else if(strcmp(Identifier, "protectionzone") == 0){ + if(!FieldPatched[OffsetX][OffsetY]){ + if(AttrCount > 0){ + OUT.writeText(", "); + } + OUT.writeText("ProtectionZone"); + AttrCount += 1; + } + }else if(strcmp(Identifier, "content") == 0){ + IN.readSymbol('='); + HelpBuffer.reset(); + LoadObjects(&IN, &HelpBuffer, false); + if(!FieldPatched[OffsetX][OffsetY]){ + if(AttrCount > 0){ + OUT.writeText(", "); + } + OUT.writeText("Content="); + TReadBuffer ReadBuffer(HelpBuffer.Data, HelpBuffer.Position); + SaveObjects(&ReadBuffer, &OUT); + AttrCount += 1; + } + }else{ + IN.error("unknown map flag"); + } + } + } + + // NOTE(fusion): Step 4. + // Transfer patched fields from memory into the new sector file. + for(int OffsetX = 0; OffsetX < 32; OffsetX += 1) + for(int OffsetY = 0; OffsetY < 32; OffsetY += 1){ + if(!FieldPatched[OffsetX][OffsetY]){ + continue; + } + + Object Con = Sec->MapCon[OffsetX][OffsetY]; + Object First = Con.getAttribute(CONTENT); + uint8 Flags = GetMapContainerFlags(Con); + if(First != NONE || Flags != 0){ + OUT.writeNumber(OffsetX); + OUT.writeText("-"); + OUT.writeNumber(OffsetY); + OUT.writeText(": "); + + int AttrCount = 0; + + if(Flags & 1){ + if(AttrCount > 0){ + OUT.writeText(", "); + } + OUT.writeText("Refresh"); + AttrCount += 1; + } + + if(Flags & 2){ + if(AttrCount > 0){ + OUT.writeText(", "); + } + OUT.writeText("NoLogout"); + AttrCount += 1; + } + + if(Flags & 4){ + if(AttrCount > 0){ + OUT.writeText(", "); + } + OUT.writeText("ProtectionZone"); + AttrCount += 1; + } + + if(First != NONE){ + if(AttrCount > 0){ + OUT.writeText(", "); + } + OUT.writeText("Content="); + HelpBuffer.reset(); + SaveObjects(First, &HelpBuffer, false); + TReadBuffer ReadBuffer(HelpBuffer.Data, HelpBuffer.Position); + SaveObjects(&ReadBuffer, &OUT); + AttrCount += 1; + } + + OUT.writeLn(); + } + } + OUT.close(); + + // NOTE(fusion): Step 5. + // Replace original sector file with the new one. + if(!NewSector){ + unlink(FileName); + } + + if(rename(FileNameBak, FileName) != 0){ + int ErrCode = errno; + error("PatchSector: Fehler %d beim Umbenennen von %s.\n", ErrCode, FileNameBak); + error("# Fehler %d: %s.\n", ErrCode, strerror(ErrCode)); + throw "cannot patch ORIGMAP"; + } +} + void InitMap(void){ ReadMapConfig(); @@ -1429,6 +1830,8 @@ Object CreateObject(void){ return Object(NextObjectID); } +// TODO(fusion): This is only used from `DeleteObject`. Should probably be an +// internal helper. void DestroyObject(Object Obj){ if(!Obj.exists()){ error("DestroyObject: Übergebenes Objekt existiert nicht.\n"); @@ -120,8 +120,9 @@ void SaveObjects(Object Obj, TWriteStream *Stream, bool Stop); void SaveObjects(TReadStream *Stream, TWriteScriptFile *Script); void SaveSector(char *FileName, int SectorX, int SectorY, int SectorZ); void SaveMap(void); -//RefreshSector -//PatchSector +void RefreshSector(int SectorX, int SectorY, int SectorZ, TReadStream *Stream); +void PatchSector(int SectorX, int SectorY, int SectorZ, bool FullSector, + TReadScriptFile *Script, bool SaveHouses); void InitMap(void); void ExitMap(bool Save); diff --git a/src/stubs.hh b/src/stubs.hh index 1c3bde0..d0b7d90 100644 --- a/src/stubs.hh +++ b/src/stubs.hh @@ -11,7 +11,7 @@ // IMPORTANT(fusion): These function definitions exist to test compilation. They're // not yet implemented and will cause the linker to fail with unresolved symbols. -typedef void TRefreshSectorFunction(int SectorX, int SectorY, int SectorZ, TReadStream *Stream); +typedef void TRefreshSectorFunction(int SectorX, int SectorY, int SectorZ, const uint8 *Data, int Size); typedef void TSendMailsFunction(TPlayerData *PlayerData); extern void AbortWriter(void); @@ -20,6 +20,7 @@ extern void AnnounceChangedCreature(uint32 CreatureID, int Type); extern void BroadcastMessage(int Mode, const char *Text, ...) ATTR_PRINTF(2, 3); extern void Change(Object Obj, ObjectType NewType, uint32 Value); extern bool CheckRight(uint32 CreatureID, RIGHT Right); +extern void CleanHouseField(int x, int y, int z); extern void CleanupDynamicStrings(void); extern void CreatePlayerList(bool Online); extern void DeleteDynamicString(uint32 Number); @@ -46,7 +47,7 @@ extern void ProcessWriterThreadReplies(void); extern void ReceiveData(void); extern void RefreshCylinders(void); extern void RefreshMap(void); -extern void RefreshSector(int SectorX, int SectorY, int SectorZ, TReadStream *Stream); +extern void RefreshSector(int SectorX, int SectorY, int SectorZ, const uint8 *Data, int Size); extern void SavePlayerDataOrder(void); extern void SendAll(void); extern void SendAmbiente(TConnection *Connection); diff --git a/src/util.cc b/src/util.cc index e0e335b..0982cd7 100644 --- a/src/util.cc +++ b/src/util.cc @@ -228,7 +228,7 @@ void TReadStream::readBytes(uint8 *Buffer, int Count){ // TReadBuffer // ============================================================================= -TReadBuffer::TReadBuffer(uint8 *Data, int Size){ +TReadBuffer::TReadBuffer(const uint8 *Data, int Size){ if(Data == NULL){ error("TReadBuffer::TReadBuffer: data ist NULL.\n"); Size = 0; |
