Ir para conteúdo

Salvar Mapa In Game


Capaverde

Posts Recomendados

Fiz isso com a idéia de permitir aos players construir casas no meio do mato e salvar essas casas e tal

Não consegui fazer salvar towns ainda (temple positions), daí você teria que editar o otbm e adicionar elas depois

 

Como eu fiz? Peguei o saveMap do remere (que é open source) e modifiquei um pouco, adaptando ao que o otserver tem.

Testei e funcionou em theforgottenserver 0.2rc9

 

 

Bom, vamos ao código

 

 

luascript.h:

static int32_t luaSaveMap(lua_State* L);

 

luascript.cpp, dentro de registerFunctions():

//saveMap()
lua_register(m_luaState, "saveMap", LuaScriptInterface::luaSaveMap);

 

luascript.cpp:

int32_t LuaScriptInterface::luaSaveMap(lua_State* L)
{
   //saveMap()
   g_game.saveMapzord();
}

 

game.h(public):

       void saveMapzord(){map->saveMapzord();}

 

map.h, embaixo de bool saveMap();:

 

	bool saveMapzord();

 

map.cpp:

 

bool Map::saveMapzord()
{
IOMap* loader = new IOMap();
bool saved = false;

for(uint32_t tries = 0; tries < 3; tries++)
{
	if(loader->saveMap(this, "eai.otbm", false))
	{
		saved = true;
		break;
	}
}

return saved;

}

 

iomap.h:

 

		bool saveMap(Map* map, const std::string& identifier, bool showdialog);

 

iomap.cpp:

 

bool IOMap::saveMap(Map* map, const std::string& identifier, bool showdialog) {
/* STOP!
 * Before you even think about modifying this, please reconsider.
 * while adding stuff to the binary format may be "cool", you'll
 * inevitably make it incompatible with any future releases of
 * the map editor, meaning you cannot reuse your map. Before you
 * try to modify this, PLEASE consider using an external file
 * like spawns.xml or houses.xml, as that will be MUCH easier
 * to port to newer versions of the editor than a custom binary
 * format.
 */

/*if(Items::dwMajorVersion < 3) {
	version = 0;
} else {
	version = 1;
}*/
FileLoader f;
f.openFile(identifier.c_str(), true, false);

f.startNode(0); {
	f.addU32(0); // Version
	f.addU16((uint16_t)map->mapWidth);
	f.addU16((uint16_t)map->mapHeight);
	f.addU32(Items::dwMajorVersion);
	f.addU32(Items::dwMinorVersion);

	f.startNode(OTBM_MAP_DATA); {
		f.addByte(OTBM_ATTR_DESCRIPTION);
		// Neither SimOne's nor OpenTibia cares for additional description tags
		f.addString("Saved with Remere's Map Editor ");

		f.addU8(OTBM_ATTR_DESCRIPTION);
		f.addString("Esse mapa é maneiro.");

		/*f.addU8(OTBM_ATTR_EXT_SPAWN_FILE);
		FileName fn(wxstr(map->spawnfile));
		f.addString(std::string((const char*)fn.GetFullName().mb_str(wxConvUTF8)));

		if(gui.GetCurrentVersion() > CLIENT_VERSION_760) {
			f.addU8(OTBM_ATTR_EXT_HOUSE_FILE);
			fn.Assign(wxstr(map->housefile));
			f.addString(std::string((const char*)fn.GetFullName().mb_str(wxConvUTF8)));
		}*/

		// Start writing tiles
		//uint64_t tiles_saved = 0;
		bool first = true;

		int local_x = -1, local_y = -1, local_z = -1;
for (uint64_t z=0; z<=15; ++z)
for (uint64_t xi = 0; xi<map->mapWidth; xi+=256)
for (uint64_t yi = 0; yi<map->mapHeight; yi+=256)
           for (uint64_t x = xi; x<xi+256; x++)  
               for (uint64_t y = yi; y<yi+256; y++){
		//MapIterator map_iterator = map.begin();
		//while(map_iterator != map.end()) {
			// Update progressbar
			//++tiles_saved;
			//if(showdialog && tiles_saved % 8192 == 0) {
				//gui.SetLoadDone(int(tiles_saved / double(map.getTileCount()) * 100.0));
			//}

			// Get tile
			Tile* save_tile = map->getTile(x,y,z); //Tile* save_tile = *map_iterator;				
        	if (!save_tile)
	      	continue;
			const Position& pos = save_tile->getPosition();

			/*// Is it an empty tile that we can skip? (Leftovers...)
			if(save_tile->size() == 0) {
				++map_iterator;
				continue;
			}*/

			// Decide if new node should be created
			if(pos.x < local_x || pos.x >= local_x + 256 ||
			   pos.y < local_y || pos.y >= local_y + 256 ||
			   pos.z != local_z) {
				// End last node
				if(!first) {
				f.endNode();
				}
				first = false;

				// Start new node
				f.startNode(OTBM_TILE_AREA);
				f.addU16(local_x = pos.x & 0xFF00);
				f.addU16(local_y = pos.y & 0xFF00);
				f.addU8( local_z = pos.z);
			}
			//HouseTile* houseTile = dynamic_cast<HouseTile*>(save_tile);
			f.startNode(/*houseTile? OTBM_HOUSETILE : */OTBM_TILE);

			f.addU8(pos.x & 0xFF);
			f.addU8(pos.y & 0xFF);

			/*if(houseTile) {
				f.addU32(houseTile->getHouse()->getHouseId());
			}*/
			/*if(save_tile->getMapFlags()) {
				f.addByte(OTBM_ATTR_TILE_FLAGS);
				f.addU32(save_tile->getMapFlags());
			}*/

			if(save_tile->ground) {
				Item* ground = save_tile->ground;
				/*if(ground->hasBorderEquivalent()) {
					bool found = false;
					for(ItemVector::iterator it = save_tile->items.begin(); it != save_tile->items.end(); ++it) {
						if((*it)->getGroundEquivalent() == ground->getID()) {
							// Do nothing
							// Found equivalent
							found = true;
							break;
						}
					}
					if(found == false) {
						ground->serializeItemNode_OTBM(*this, f);
					}
				} else*/ if(ground->isComplex()) {
					ground->serializeItemNode_OTBM(f);
				} else {
					f.addByte(OTBM_ATTR_ITEM);
					ground->serializeItemCompact_OTBM(f);
				}
			}

			for(ItemVector::reverse_iterator it = save_tile->downItems.rbegin(); it != save_tile->downItems.rend(); ++it) {
				//if(!(*it)->isMetaItem()) {
					(*it)->serializeItemNode_OTBM(f);
				//}
			}
			for(ItemVector::iterator it = save_tile->topItems.begin(); it != save_tile->topItems.end(); ++it) {
				//if(!(*it)->isMetaItem()) {
					(*it)->serializeItemNode_OTBM(f);
				//}
			}
			f.endNode();

			//++map_iterator;
		}
		// Only close the last node if one has actually been created
		if(!first) {
			f.endNode();
		}

		f.startNode(OTBM_TOWNS); {
			//for(TownMap::const_iterator it = townMap.begin(); it != townMap.end(); ++it) {
               for(TownMap::const_iterator it = Towns::getInstance().getFirstTown(); it != Towns::getInstance().getLastTown(); ++it){
				Town* town = it->second;
				f.startNode(OTBM_TOWN);

				f.addU32(town->getTownID());
				f.addString(town->getName());
				f.addU16(town->getTemplePosition().x);
				f.addU16(town->getTemplePosition().y);
				f.addU8 (town->getTemplePosition().z);
				f.endNode();
			}
		} f.endNode();
	} f.endNode();
//std::cout << tiles_saved << std::endl;
} f.endNode();

/*if(showdialog) gui.SetLoadDone(100, wxT("Saving spawns..."));
saveSpawns(map, identifier);
if(gui.GetCurrentVersion() > CLIENT_VERSION_760) {
	if(showdialog) gui.SetLoadDone(100, wxT("Saving houses..."));
	saveHouses(map, identifier);
}*/
return true;
}

 

item.h, public da class Item:

		//map-saving
	virtual bool serializeItemNode_OTBM(FileLoader& f) const;
	// Will write this item to the stream supplied in the argument
	virtual void serializeItemCompact_OTBM(FileLoader& f) const;
	virtual void serializeItemAttributes_OTBM(FileLoader& f) const;

 

item.h, public da class ItemAttributes:

virtual bool isComplex() const {return (15 & m_attributes) != 0;}

 

item.cpp:

bool Item::serializeItemNode_OTBM(FileLoader& f) const {
f.startNode(OTBM_ITEM);
f.addU16(id);

//if(maphandle.version == 0) {
	/*const ItemType& iType = items[id];

	if(iType.stackable || iType.isSplash() || iType.isFluidContainer()){
		f.addU8(getSubType());
	}*/
//}

serializeItemAttributes_OTBM(f);
f.endNode();

return true;
}

void Item::serializeItemAttributes_OTBM(FileLoader& stream) const {
//if(maphandle.version > 0) {
	const ItemType& iType = items[id];

	if(iType.stackable || iType.isSplash() || iType.isFluidContainer()){
		//stream.addU8(OTBM_ATTR_COUNT);
		stream.addU8(getItemCountOrSubtype());
	}
//}*/

/*	if(items.dwMinorVersion >= CLIENT_VERSION_820 && isCharged()) {
	stream.addU8(OTBM_ATTR_CHARGES);
	stream.addU16(getSubtype());
}*/

if(getActionId()) {
	stream.addU8(OTBM_ATTR_ACTION_ID);
	stream.addU16(getActionId());
}

if(getUniqueId()) {
	stream.addU8(OTBM_ATTR_UNIQUE_ID);
	stream.addU16(getUniqueId());
}

if(getText().length() > 0) {
	stream.addU8(OTBM_ATTR_TEXT);
	stream.addString(getText());
}

if(getSpecialDescription().length() > 0) {
	stream.addU8(OTBM_ATTR_DESC);
	stream.addString(getSpecialDescription());
}
}

void Item::serializeItemCompact_OTBM(FileLoader& stream) const {
stream.addU16(id);

/* This is impossible
const ItemType& iType = item_db[id];

if(iType.stackable || iType.isSplash() || iType.isFluidContainer()){
	stream.addU8(getSubtype());
}
*/
}

 

fileloader.cpp:

troca as funções addU8 e addU16 por essas(ou o mapa gerado vai tá corrompido, aconteceu comigo):

 

bool FileLoader::addU8(uint8_t u8) {
writeData(&u8, sizeof(u8), true);  //unescape=true, or else some FEsomething itemid might be recognized as the start of a node
return m_lastError == ERROR_NONE;
}

bool FileLoader::addU16(uint16_t u16) {
writeData(reinterpret_cast<uint8_t*>(&u16), sizeof(u16), true);
return m_lastError == ERROR_NONE;
}

 

 

Como usa isso? Só colocar saveMap() em algum script, mas olha que vai lagar.

Dá pra facilmente criar um npc que salva o mapa de x em x horas, e se você for reiniciar o server por algum motivo é só kickar todo mundo e usar uma talkaction que salve.

Editado por Capaverde
Link para o comentário
Compartilhar em outros sites

  • 2 weeks later...
  • 3 years later...
  • 2 years later...

Eis a função equivalente, puramente em Lua. Coloque-a no seu global.lua:

 

bit32 = bit
print = debugPrint
 
--SPECIAL CHARACTERS
    NODE_START = 0xfe
    NODE_END = 0xff
    ESCAPE_CHAR = 0xfd
 
--OTBM NODE TYPES
    OTBM_ROOTV1 = 1
    OTBM_MAP_DATA = 2
    OTBM_ITEM_DEF = 3
    OTBM_TILE_AREA = 4
    OTBM_TILE = 5
    OTBM_ITEM = 6
    OTBM_TILE_SQUARE = 7
    OTBM_TILE_REF = 8
    OTBM_SPAWNS = 9
    OTBM_SPAWN_AREA = 10
    OTBM_MONSTER = 11
    OTBM_TOWNS = 12
    OTBM_TOWN = 13
    OTBM_HOUSETILE = 14
 
--OTBM ATTR TYPES
    OTBM_ATTR_DESCRIPTION = 1
    OTBM_ATTR_EXT_FILE = 2
    OTBM_ATTR_TILE_FLAGS = 3
    OTBM_ATTR_ACTION_ID = 4
    OTBM_ATTR_UNIQUE_ID = 5
    OTBM_ATTR_TEXT = 6
    OTBM_ATTR_DESC = 7
    OTBM_ATTR_TELE_DEST = 8
    OTBM_ATTR_ITEM = 9
    OTBM_ATTR_DEPOT_ID = 10
    OTBM_ATTR_EXT_SPAWN_FILE = 11
    OTBM_ATTR_RUNE_CHARGES = 12
    OTBM_ATTR_EXT_HOUSE_FILE = 13
    OTBM_ATTR_HOUSEDOORID = 14
    OTBM_ATTR_COUNT = 15
    OTBM_ATTR_DURATION = 16
    OTBM_ATTR_DECAYING_STATE = 17
    OTBM_ATTR_WRITTENDATE = 18
    OTBM_ATTR_WRITTENBY = 19
    OTBM_ATTR_SLEEPERGUID = 20
    OTBM_ATTR_SLEEPSTART = 21
    OTBM_ATTR_CHARGES = 22
   
   
 
function lchar(i) s="" while i>0 do s=s..string.char(i%256) i=math.floor(i/256) end return s end
 
function format(s,size)
local len = #s
for x=1,size/8-len do
s=s.."\0"
end
return s
end
 
function writeData(f,data,size,unescape)
local thestr
if type(data) == "string" then
thestr = format(data,size)
elseif type(data) == "number" then
thestr = format(lchar(data),size)
elseif type(data) == "table" then
for k,v in pairs(data) do
print(k,v)
end
return
else
print(data)
return
end
for x=1,size/8 do
local c = thestr:sub(x,x)
--print(#thestr,size,string.byte(c))
--if c == string.char(0x0D) then error("daonde") end
    if unescape and (c==string.char(NODE_START) or c==string.char(NODE_END) or c==string.char(ESCAPE_CHAR)) then
        f:write(string.char(ESCAPE_CHAR))
    end
    f:write(c)
end
end
 
function addU8(f,data)
    writeData(f,data,8,true)
end
function addByte(f,data)
    writeData(f,data,8,false)
end
function addU16(f,data)
    writeData(f,data,16,true)
end
function addU32(f,data)
    writeData(f,data,32,false)
end
function addString(f,data)
    if #data > 0xffff then
        return false
    end
addU16(f,#data)
writeData(f,data,#data*8,false)
end
 
function startNode(f,c) --c is a char  
writeData(f,NODE_START,8,false)
writeData(f,c,8,true)
end
 
function endNode(f)
writeData(f,NODE_END,8,false)
end
 
 
WIDTH = 128         --put here the dimensions of your map
HEIGHT = 128        --
ITEMDWMAJORVERSION = 2
ITEMDWMINORVERSION = 8
 
 
function saveMap(name)  --e.g. "map.otbm"
local minv = math.huge
local maxv = 0
print("saveMap start")
f = io.open(name,"wb")
addU32(f,0);    --version
startNode(f,0)
    addU32(f,0);    --version again :O
    addU16(f,WIDTH)
    addU16(f,HEIGHT)
    addU32(f,ITEMDWMAJORVERSION)
    addU32(f,ITEMDWMINORVERSION)
 
    startNode(f,OTBM_MAP_DATA)
        --addByte(f,OTBM_ATTR_DESCRIPTION)
        --addString(f,"Created with saveMap script, a translation of Remere's")
        local first = true
        local l_x=-1
        local l_y=-1
        local l_Z=-1
        for z=0,7 do
        for xi=0,WIDTH-1,256 do
        for yi=0,HEIGHT-1,256 do
        for x=xi,math.min(xi+256-1,WIDTH-1) do
        for y=yi,math.min(yi+256-1,HEIGHT-1) do
            if x<l_x or x>=l_x+256 or y<l_y or y>=l_y+256 or z~=l_z then
                if not first then
                    endNode(f)
                end
                first = false
                --start new node
                startNode(f,OTBM_TILE_AREA)
                l_x=bit32.band(x,0xff00)
                l_y=bit32.band(y,0xff00)
                l_z=z
                addU16(f,l_x)
                addU16(f,l_y)
                addU8(f,l_z)
            end
                startNode(f,OTBM_TILE)
                addU8(f,bit32.band(x,0xff))
                addU8(f,bit32.band(y,0xff))
               
                for stackpos=0,10 do
                    local pos = {x=x,y=y,z=z,stackpos=stackpos}
                    local thing = getTileThingByPos(pos)
                    if (thing.itemid==0 and stackpos==0) then   --no tile, so we can skip it
                        break
                    end
                    if thing.itemid > 0 and stackpos ~= 253 then    --TODO: save item counts, save containers
                        addByte(f,OTBM_ATTR_ITEM)
                        addU16(f,thing.itemid)
                    end
                end
                endNode(f)
 
        end
        end
        end
        end
        end
        if not first then
            endNode(f)
        end
    endNode(f)
endNode(f)
f:close()
print(minv,maxv)
end

 

Dessa forma não precisa-se editar uma linha do source ou recompilar

Link para o comentário
Compartilhar em outros sites

  • 2 years later...
Em 28/10/2017 em 05:36, Capaverde disse:

Eis a função equivalente, puramente em Lua. Coloque-a no seu global.lua:

 


bit32 = bit
print = debugPrint
 
--SPECIAL CHARACTERS
    NODE_START = 0xfe
    NODE_END = 0xff
    ESCAPE_CHAR = 0xfd
 
--OTBM NODE TYPES
    OTBM_ROOTV1 = 1
    OTBM_MAP_DATA = 2
    OTBM_ITEM_DEF = 3
    OTBM_TILE_AREA = 4
    OTBM_TILE = 5
    OTBM_ITEM = 6
    OTBM_TILE_SQUARE = 7
    OTBM_TILE_REF = 8
    OTBM_SPAWNS = 9
    OTBM_SPAWN_AREA = 10
    OTBM_MONSTER = 11
    OTBM_TOWNS = 12
    OTBM_TOWN = 13
    OTBM_HOUSETILE = 14
 
--OTBM ATTR TYPES
    OTBM_ATTR_DESCRIPTION = 1
    OTBM_ATTR_EXT_FILE = 2
    OTBM_ATTR_TILE_FLAGS = 3
    OTBM_ATTR_ACTION_ID = 4
    OTBM_ATTR_UNIQUE_ID = 5
    OTBM_ATTR_TEXT = 6
    OTBM_ATTR_DESC = 7
    OTBM_ATTR_TELE_DEST = 8
    OTBM_ATTR_ITEM = 9
    OTBM_ATTR_DEPOT_ID = 10
    OTBM_ATTR_EXT_SPAWN_FILE = 11
    OTBM_ATTR_RUNE_CHARGES = 12
    OTBM_ATTR_EXT_HOUSE_FILE = 13
    OTBM_ATTR_HOUSEDOORID = 14
    OTBM_ATTR_COUNT = 15
    OTBM_ATTR_DURATION = 16
    OTBM_ATTR_DECAYING_STATE = 17
    OTBM_ATTR_WRITTENDATE = 18
    OTBM_ATTR_WRITTENBY = 19
    OTBM_ATTR_SLEEPERGUID = 20
    OTBM_ATTR_SLEEPSTART = 21
    OTBM_ATTR_CHARGES = 22
   
   
 
function lchar(i) s="" while i>0 do s=s..string.char(i%256) i=math.floor(i/256) end return s end
 
function format(s,size)
local len = #s
for x=1,size/8-len do
s=s.."\0"
end
return s
end
 
function writeData(f,data,size,unescape)
local thestr
if type(data) == "string" then
thestr = format(data,size)
elseif type(data) == "number" then
thestr = format(lchar(data),size)
elseif type(data) == "table" then
for k,v in pairs(data) do
print(k,v)
end
return
else
print(data)
return
end
for x=1,size/8 do
local c = thestr:sub(x,x)
--print(#thestr,size,string.byte(c))
--if c == string.char(0x0D) then error("daonde") end
    if unescape and (c==string.char(NODE_START) or c==string.char(NODE_END) or c==string.char(ESCAPE_CHAR)) then
        f:write(string.char(ESCAPE_CHAR))
    end
    f:write(c)
end
end
 
function addU8(f,data)
    writeData(f,data,8,true)
end
function addByte(f,data)
    writeData(f,data,8,false)
end
function addU16(f,data)
    writeData(f,data,16,true)
end
function addU32(f,data)
    writeData(f,data,32,false)
end
function addString(f,data)
    if #data > 0xffff then
        return false
    end
addU16(f,#data)
writeData(f,data,#data*8,false)
end
 
function startNode(f,c) --c is a char  
writeData(f,NODE_START,8,false)
writeData(f,c,8,true)
end
 
function endNode(f)
writeData(f,NODE_END,8,false)
end
 
 
WIDTH = 128         --put here the dimensions of your map
HEIGHT = 128        --
ITEMDWMAJORVERSION = 2
ITEMDWMINORVERSION = 8
 
 
function saveMap(name)  --e.g. "map.otbm"
local minv = math.huge
local maxv = 0
print("saveMap start")
f = io.open(name,"wb")
addU32(f,0);    --version
startNode(f,0)
    addU32(f,0);    --version again :O
    addU16(f,WIDTH)
    addU16(f,HEIGHT)
    addU32(f,ITEMDWMAJORVERSION)
    addU32(f,ITEMDWMINORVERSION)
 
    startNode(f,OTBM_MAP_DATA)
        --addByte(f,OTBM_ATTR_DESCRIPTION)
        --addString(f,"Created with saveMap script, a translation of Remere's")
        local first = true
        local l_x=-1
        local l_y=-1
        local l_Z=-1
        for z=0,7 do
        for xi=0,WIDTH-1,256 do
        for yi=0,HEIGHT-1,256 do
        for x=xi,math.min(xi+256-1,WIDTH-1) do
        for y=yi,math.min(yi+256-1,HEIGHT-1) do
            if x<l_x or x>=l_x+256 or y<l_y or y>=l_y+256 or z~=l_z then
                if not first then
                    endNode(f)
                end
                first = false
                --start new node
                startNode(f,OTBM_TILE_AREA)
                l_x=bit32.band(x,0xff00)
                l_y=bit32.band(y,0xff00)
                l_z=z
                addU16(f,l_x)
                addU16(f,l_y)
                addU8(f,l_z)
            end
                startNode(f,OTBM_TILE)
                addU8(f,bit32.band(x,0xff))
                addU8(f,bit32.band(y,0xff))
               
                for stackpos=0,10 do
                    local pos = {x=x,y=y,z=z,stackpos=stackpos}
                    local thing = getTileThingByPos(pos)
                    if (thing.itemid==0 and stackpos==0) then   --no tile, so we can skip it
                        break
                    end
                    if thing.itemid > 0 and stackpos ~= 253 then    --TODO: save item counts, save containers
                        addByte(f,OTBM_ATTR_ITEM)
                        addU16(f,thing.itemid)
                    end
                end
                endNode(f)
 
        end
        end
        end
        end
        end
        if not first then
            endNode(f)
        end
    endNode(f)
endNode(f)
f:close()
print(minv,maxv)
end

 

Dessa forma não precisa-se editar uma linha do source ou recompilar

sera que alguem pode dar uma ajudinha explicando como eu instalo e uso isso?

Link para o comentário
Compartilhar em outros sites

×
×
  • Criar Novo...