more work on xml stuff (prolly done for today)
This commit is contained in:
parent
5015ced156
commit
aef3a5b8db
|
@ -79,8 +79,8 @@ set(otclient_SOURCES ${otclient_SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/houses.h
|
${CMAKE_CURRENT_LIST_DIR}/houses.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/towns.cpp
|
${CMAKE_CURRENT_LIST_DIR}/towns.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/towns.h
|
${CMAKE_CURRENT_LIST_DIR}/towns.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/monsters.cpp
|
${CMAKE_CURRENT_LIST_DIR}/creatures.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/monsters.h
|
${CMAKE_CURRENT_LIST_DIR}/creatures.h
|
||||||
|
|
||||||
# lua
|
# lua
|
||||||
${CMAKE_CURRENT_LIST_DIR}/luavaluecasts.cpp
|
${CMAKE_CURRENT_LIST_DIR}/luavaluecasts.cpp
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "creatures.h"
|
||||||
|
|
||||||
|
#include <framework/xml/tinyxml.h>
|
||||||
|
#include <framework/core/resourcemanager.h>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
void Creatures::loadMonsters(const std::string& file)
|
||||||
|
{
|
||||||
|
TiXmlDocument doc;
|
||||||
|
doc.Parse(g_resources.loadFile(file).c_str());
|
||||||
|
if(doc.Error())
|
||||||
|
stdext::throw_exception(stdext::format("cannot open monsters file '%s': '%s'", file, doc.ErrorDesc()));
|
||||||
|
|
||||||
|
TiXmlElement* root = doc.FirstChildElement();
|
||||||
|
if(!root || root->ValueStr() != "monsters")
|
||||||
|
stdext::throw_exception("malformed monsters xml file");
|
||||||
|
|
||||||
|
for(TiXmlElement* monster = root->FirstChildElement(); monster; monster = monster->NextSiblingElement()) {
|
||||||
|
std::string fname = file.substr(0, file.find_last_of('/')) + '/' + monster->Attribute("file");
|
||||||
|
if(fname.substr(fname.length() - 4) != ".xml")
|
||||||
|
fname += ".xml";
|
||||||
|
|
||||||
|
loadSingleCreature(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.Clear();
|
||||||
|
m_loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creatures::loadSingleCreature(const std::string& file)
|
||||||
|
{
|
||||||
|
return loadCreatureBuffer(g_resources.loadFile(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creatures::loadNpcs(const std::string &folder)
|
||||||
|
{
|
||||||
|
boost::filesystem::path npcPath(folder);
|
||||||
|
if(!boost::filesystem::exists(npcPath))
|
||||||
|
stdext::throw_exception(stdext::format("NPCs folder '%s' was not found.", folder));
|
||||||
|
|
||||||
|
for(boost::filesystem::directory_iterator it(npcPath), end; it != end; ++it) {
|
||||||
|
std::string f = it->path().string();
|
||||||
|
if(boost::filesystem::is_directory(it->status()) && ((f.size() > 4 ? f.substr(f.size() - 4) : "") != ".xml"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
loadCreatureBuffer(g_resources.loadFile(f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creatures::loadCreatureBuffer(const std::string &buffer)
|
||||||
|
{
|
||||||
|
TiXmlDocument doc;
|
||||||
|
doc.Parse(buffer.c_str());
|
||||||
|
if(doc.Error())
|
||||||
|
stdext::throw_exception(stdext::format("cannot load creature buffer: %s", doc.ErrorDesc()));
|
||||||
|
|
||||||
|
TiXmlElement* root = doc.FirstChildElement();
|
||||||
|
if(!root || root->ValueStr() != "npc")
|
||||||
|
stdext::throw_exception(("invalid root tag name"));
|
||||||
|
|
||||||
|
for(TiXmlElement* attrib = root->FirstChildElement(); attrib; attrib = attrib->NextSiblingElement()) {
|
||||||
|
if(attrib->ValueStr() != "npc" && attrib->ValueStr() != "monster")
|
||||||
|
stdext::throw_exception(stdext::format("invalid attribute '%s'", attrib->ValueStr()));
|
||||||
|
|
||||||
|
CreatureTypePtr newType(nullptr);
|
||||||
|
m_loadCreatureBuffer(attrib, newType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Creatures::m_loadCreatureBuffer(TiXmlElement* attrib, CreatureTypePtr& m)
|
||||||
|
{
|
||||||
|
if(m || std::find(m_creatures.begin(), m_creatures.end(), m) != m_creatures.end())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m = CreatureTypePtr(new CreatureType(stdext::trim(stdext::tolower(attrib->Attribute("name")))));
|
||||||
|
Outfit out;
|
||||||
|
|
||||||
|
int32 type;
|
||||||
|
if(!attrib->Attribute("type").empty())
|
||||||
|
type = attrib->readType<int32>("type");
|
||||||
|
else
|
||||||
|
type = attrib->readType<int32>("typeex");
|
||||||
|
|
||||||
|
out.setId(type);
|
||||||
|
{
|
||||||
|
out.setHead(attrib->readType<int>(("head")));
|
||||||
|
out.setBody(attrib->readType<int>(("body")));
|
||||||
|
out.setLegs(attrib->readType<int>(("legs")));
|
||||||
|
out.setFeet(attrib->readType<int>(("feet")));
|
||||||
|
out.setAddons(attrib->readType<int>(("addons")));
|
||||||
|
out.setMount(attrib->readType<int>(("mount")));
|
||||||
|
}
|
||||||
|
m->setOutfit(out);
|
||||||
|
m_creatures.push_back(m);
|
||||||
|
|
||||||
|
return type >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreatureTypePtr Creatures::getCreature(const std::string& name)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(m_creatures.begin(), m_creatures.end(),
|
||||||
|
[=] (const CreatureTypePtr& m) -> bool { return m->getName() == stdext::trim(stdext::tolower(name)); });
|
||||||
|
return it != m_creatures.end() ? *it : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreatureTypePtr Creatures::getCreature(const Position& pos)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(m_creatures.begin(), m_creatures.end(),
|
||||||
|
[=] (const CreatureTypePtr& m) -> bool { return m->getPos() == pos; });
|
||||||
|
return it != m_creatures.end() ? *it : nullptr;
|
||||||
|
}
|
|
@ -27,11 +27,11 @@
|
||||||
#include <framework/luaengine/luaobject.h>
|
#include <framework/luaengine/luaobject.h>
|
||||||
#include "outfit.h"
|
#include "outfit.h"
|
||||||
|
|
||||||
class MonsterType : public LuaObject
|
class CreatureType : public LuaObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MonsterType() { }
|
CreatureType() { }
|
||||||
MonsterType(const std::string& name)
|
CreatureType(const std::string& name)
|
||||||
: m_name(name) { }
|
: m_name(name) { }
|
||||||
|
|
||||||
void setPos(const Position& pos) { m_pos = pos; }
|
void setPos(const Position& pos) { m_pos = pos; }
|
||||||
|
@ -50,21 +50,27 @@ private:
|
||||||
int m_spawnTime;
|
int m_spawnTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Monsters
|
class Creatures
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void clear() { m_monsters.clear(); }
|
void clear() { m_creatures.clear(); }
|
||||||
|
|
||||||
void loadMonsters(const std::string& file);
|
void loadMonsters(const std::string& file);
|
||||||
void loadSingleMonster(const std::string& file, const MonsterTypePtr& m = nullptr);
|
void loadSingleCreature(const std::string& file);
|
||||||
|
void loadNpcs(const std::string& folder);
|
||||||
|
void loadCreatureBuffer(const std::string& buffer);
|
||||||
|
|
||||||
MonsterTypePtr getMonster(const std::string& name);
|
CreatureTypePtr getCreature(const std::string& name);
|
||||||
MonsterTypePtr getMonsterByPos(const Position& pos);
|
CreatureTypePtr getCreature(const Position& pos);
|
||||||
|
|
||||||
bool isLoaded() const { return m_loaded; }
|
bool isLoaded() const { return m_loaded; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool m_loadCreatureBuffer(TiXmlElement* elem, CreatureTypePtr& m);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<MonsterTypePtr> m_monsters;
|
std::vector<CreatureTypePtr> m_creatures;
|
||||||
bool m_loaded;
|
Boolean<false> m_loaded;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -48,7 +48,7 @@ class ThingTypeDat;
|
||||||
class ThingTypeOtb;
|
class ThingTypeOtb;
|
||||||
class House;
|
class House;
|
||||||
class Town;
|
class Town;
|
||||||
class MonsterType;
|
class CreatureType;
|
||||||
|
|
||||||
typedef std::shared_ptr<MapView> MapViewPtr;
|
typedef std::shared_ptr<MapView> MapViewPtr;
|
||||||
typedef std::shared_ptr<Tile> TilePtr;
|
typedef std::shared_ptr<Tile> TilePtr;
|
||||||
|
@ -68,13 +68,14 @@ typedef std::shared_ptr<ThingTypeDat> ThingTypeDatPtr;
|
||||||
typedef std::shared_ptr<ThingTypeOtb> ThingTypeOtbPtr;
|
typedef std::shared_ptr<ThingTypeOtb> ThingTypeOtbPtr;
|
||||||
typedef std::shared_ptr<House> HousePtr;
|
typedef std::shared_ptr<House> HousePtr;
|
||||||
typedef std::shared_ptr<Town> TownPtr;
|
typedef std::shared_ptr<Town> TownPtr;
|
||||||
typedef std::shared_ptr<MonsterType> MonsterTypePtr;
|
typedef std::shared_ptr<CreatureType> CreatureTypePtr;
|
||||||
|
|
||||||
typedef std::vector<ThingPtr> ThingList;
|
typedef std::vector<ThingPtr> ThingList;
|
||||||
typedef std::vector<ThingTypeDatPtr> ThingTypeDatList;
|
typedef std::vector<ThingTypeDatPtr> ThingTypeDatList;
|
||||||
typedef std::vector<ThingTypeOtbPtr> ThingTypeOtbList;
|
typedef std::vector<ThingTypeOtbPtr> ThingTypeOtbList;
|
||||||
typedef std::vector<HousePtr> HouseList;
|
typedef std::vector<HousePtr> HouseList;
|
||||||
typedef std::vector<TownPtr> TownList;
|
typedef std::vector<TownPtr> TownList;
|
||||||
|
typedef std::unordered_map<Position, TilePtr, PositionHasher> TileMap;
|
||||||
|
|
||||||
// net
|
// net
|
||||||
class ProtocolLogin;
|
class ProtocolLogin;
|
||||||
|
|
|
@ -25,40 +25,30 @@
|
||||||
#include <framework/core/resourcemanager.h>
|
#include <framework/core/resourcemanager.h>
|
||||||
|
|
||||||
House::House(uint32 hId, const std::string &name, const Position &pos)
|
House::House(uint32 hId, const std::string &name, const Position &pos)
|
||||||
: m_id(hId), m_name(name)
|
|
||||||
{
|
{
|
||||||
|
setId(hId);
|
||||||
|
setName(name);
|
||||||
if(pos.isValid())
|
if(pos.isValid())
|
||||||
m_doors.insert(std::make_pair(0, pos)); // first door
|
setEntry(pos);
|
||||||
}
|
|
||||||
|
|
||||||
void House::addDoor(uint16 doorId, const Position& pos)
|
|
||||||
{
|
|
||||||
if(m_doors.find(doorId) == m_doors.end())
|
|
||||||
m_doors.insert(std::make_pair(doorId, pos));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void House::setTile(const TilePtr& tile)
|
void House::setTile(const TilePtr& tile)
|
||||||
{
|
{
|
||||||
tile->setFlags(TILESTATE_HOUSE);
|
tile->setFlags(TILESTATE_HOUSE);
|
||||||
if(std::find(m_tiles.begin(), m_tiles.end(), tile) == m_tiles.end())
|
m_tiles.insert(std::make_pair(tile->getPosition(), tile));
|
||||||
m_tiles.push_back(tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void House::load(const TiXmlElement *elem)
|
void House::load(const TiXmlElement *elem)
|
||||||
{
|
{
|
||||||
std::string name = elem->Attribute("name");
|
std::string name = elem->Attribute("name");
|
||||||
if(name.empty())
|
if(name.empty())
|
||||||
name = stdext::format("UnNamed house #%u", getId());
|
name = stdext::format("Unnamed house #%lu", getId());
|
||||||
|
|
||||||
m_rent = elem->readType<uint32>("rent");
|
|
||||||
m_size = elem->readType<uint32>("size");
|
|
||||||
|
|
||||||
uint32 townId = elem->readType<uint32>("townid");
|
|
||||||
if(!g_map.getTown(townId))
|
|
||||||
stdext::throw_exception(stdext::format("invalid town id for house %d", townId));
|
|
||||||
|
|
||||||
|
setRent(elem->readType<uint32>("rent"));
|
||||||
|
setSize(elem->readType<uint32>("size"));
|
||||||
|
setTownId(elem->readType<uint32>("townid"));
|
||||||
m_isGuildHall = elem->readType<bool>("rent");
|
m_isGuildHall = elem->readType<bool>("rent");
|
||||||
addDoor(0, elem->readPos());
|
setEntry(elem->readPos("entry"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Houses::addHouse(const HousePtr& house)
|
void Houses::addHouse(const HousePtr& house)
|
||||||
|
@ -77,10 +67,7 @@ void Houses::removeHouse(uint32 houseId)
|
||||||
HousePtr Houses::getHouse(uint32 houseId)
|
HousePtr Houses::getHouse(uint32 houseId)
|
||||||
{
|
{
|
||||||
auto it = findHouse(houseId);
|
auto it = findHouse(houseId);
|
||||||
if(it != m_houses.end())
|
return it != m_houses.end() ? *it : nullptr;
|
||||||
return *it;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Houses::load(const std::string& fileName)
|
void Houses::load(const std::string& fileName)
|
||||||
|
@ -94,7 +81,7 @@ void Houses::load(const std::string& fileName)
|
||||||
if(!root || root->ValueTStr() != "houses")
|
if(!root || root->ValueTStr() != "houses")
|
||||||
stdext::throw_exception("invalid root tag name");
|
stdext::throw_exception("invalid root tag name");
|
||||||
|
|
||||||
for (TiXmlElement *elem = root->FirstChildElement(); elem; elem = elem->NextSiblingElement()) {
|
for(TiXmlElement *elem = root->FirstChildElement(); elem; elem = elem->NextSiblingElement()) {
|
||||||
if(elem->ValueTStr() != "house")
|
if(elem->ValueTStr() != "house")
|
||||||
stdext::throw_exception("invalid house tag.");
|
stdext::throw_exception("invalid house tag.");
|
||||||
|
|
||||||
|
@ -106,7 +93,7 @@ void Houses::load(const std::string& fileName)
|
||||||
house->load(elem);
|
house->load(elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
stdext::throw_exception("This has not been fully implemented yet.");
|
g_logger.debug("Loaded houses.xml successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
HouseList::iterator Houses::findHouse(uint32 houseId)
|
HouseList::iterator Houses::findHouse(uint32 houseId)
|
||||||
|
|
|
@ -27,32 +27,45 @@
|
||||||
#include "tile.h"
|
#include "tile.h"
|
||||||
|
|
||||||
#include <framework/luaengine/luaobject.h>
|
#include <framework/luaengine/luaobject.h>
|
||||||
|
#include <framework/util/attribstorage.h>
|
||||||
|
|
||||||
|
enum HouseAttributes
|
||||||
|
{
|
||||||
|
HouseAttribId,
|
||||||
|
HouseAttribName,
|
||||||
|
HouseAttribTown,
|
||||||
|
HouseAttribEntry,
|
||||||
|
HouseAttribSize,
|
||||||
|
HouseAttribRent
|
||||||
|
};
|
||||||
|
|
||||||
class House : public LuaObject
|
class House : public LuaObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
House() { }
|
House() { }
|
||||||
House(uint32 hId, const std::string& name = "", const Position& pos=Position());
|
House(uint32 hId, const std::string& name = "", const Position& pos=Position());
|
||||||
~House() { m_tiles.clear(); m_doors.clear(); }
|
~House() { m_tiles.clear(); }
|
||||||
|
|
||||||
void setId(uint32 hId) { m_id = hId; }
|
|
||||||
void setName(const std::string& name) { m_name = name; }
|
|
||||||
void addDoor(uint16 doorId, const Position& pos);
|
|
||||||
void setTile(const TilePtr& tile);
|
void setTile(const TilePtr& tile);
|
||||||
|
void setId(uint32 hId) { m_attribs.set(HouseAttribId, hId); }
|
||||||
|
void setName(const std::string& name) { m_attribs.set(HouseAttribName, name); }
|
||||||
|
void setTownId(uint32 tid) { m_attribs.set(HouseAttribTown, tid); }
|
||||||
|
void setSize(uint32 s) { m_attribs.set(HouseAttribSize, s); }
|
||||||
|
void setRent(uint32 r) { m_attribs.set(HouseAttribRent, r); }
|
||||||
|
void setEntry(const Position& p) { m_attribs.set(HouseAttribEntry, p); }
|
||||||
|
|
||||||
uint32 getId() const { return m_id; }
|
uint32 getId() { return m_attribs.get<uint32>(HouseAttribId); }
|
||||||
std::string getName() const { return m_name; }
|
std::string getName() { return m_attribs.get<std::string>(HouseAttribName); }
|
||||||
|
uint32 getRent() { return m_attribs.get<uint32>(HouseAttribRent); }
|
||||||
|
uint32 getSize() { return m_attribs.get<uint32>(HouseAttribSize); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void load(const TiXmlElement* elem);
|
void load(const TiXmlElement* elem);
|
||||||
void save(TiXmlElement &elem) { } // TODO
|
void save(TiXmlElement &elem) { } // TODO
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32 m_id, m_size, m_rent;
|
AttribStorage m_attribs;
|
||||||
std::string m_name;
|
TileMap m_tiles;
|
||||||
|
|
||||||
std::map<uint16, Position> m_doors;
|
|
||||||
std::vector<TilePtr> m_tiles;
|
|
||||||
Boolean<false> m_isGuildHall;
|
Boolean<false> m_isGuildHall;
|
||||||
|
|
||||||
friend class Houses;
|
friend class Houses;
|
||||||
|
|
|
@ -97,10 +97,10 @@ void OTClient::registerLuaFunctions()
|
||||||
g_lua.bindSingletonFunction("g_map", "loadOtcm", &Map::loadOtcm, &g_map);
|
g_lua.bindSingletonFunction("g_map", "loadOtcm", &Map::loadOtcm, &g_map);
|
||||||
g_lua.bindSingletonFunction("g_map", "saveOtcm", &Map::saveOtcm, &g_map);
|
g_lua.bindSingletonFunction("g_map", "saveOtcm", &Map::saveOtcm, &g_map);
|
||||||
g_lua.bindSingletonFunction("g_map", "loadMonsters", &Map::loadMonsters, &g_map);
|
g_lua.bindSingletonFunction("g_map", "loadMonsters", &Map::loadMonsters, &g_map);
|
||||||
g_lua.bindSingletonFunction("g_map", "loadSingleMonster", &Map::loadSingleMonster, &g_map);
|
g_lua.bindSingletonFunction("g_map", "loadSingleCreature", &Map::loadSingleCreature, &g_map);
|
||||||
g_lua.bindSingletonFunction("g_map", "getTown", &Map::getTown, &g_map);
|
g_lua.bindSingletonFunction("g_map", "getTown", &Map::getTown, &g_map);
|
||||||
g_lua.bindSingletonFunction("g_map", "getHouse", &Map::getHouse, &g_map);
|
g_lua.bindSingletonFunction("g_map", "getHouse", &Map::getHouse, &g_map);
|
||||||
g_lua.bindSingletonFunction("g_map", "getMonster", &Map::getMonster, &g_map);
|
g_lua.bindSingletonFunction("g_map", "getCreature", &Map::getCreature, &g_map);
|
||||||
|
|
||||||
g_lua.registerSingletonClass("g_game");
|
g_lua.registerSingletonClass("g_game");
|
||||||
g_lua.bindSingletonFunction("g_game", "loginWorld", &Game::loginWorld, &g_game);
|
g_lua.bindSingletonFunction("g_game", "loginWorld", &Game::loginWorld, &g_game);
|
||||||
|
@ -280,10 +280,9 @@ void OTClient::registerLuaFunctions()
|
||||||
g_lua.bindClassStaticFunction<House>("create", []{ return HousePtr(new House); });
|
g_lua.bindClassStaticFunction<House>("create", []{ return HousePtr(new House); });
|
||||||
g_lua.bindClassMemberFunction<House>("setId", &House::setId);
|
g_lua.bindClassMemberFunction<House>("setId", &House::setId);
|
||||||
g_lua.bindClassMemberFunction<House>("setName", &House::setName);
|
g_lua.bindClassMemberFunction<House>("setName", &House::setName);
|
||||||
g_lua.bindClassMemberFunction<House>("addDoor", &House::addDoor);
|
|
||||||
g_lua.bindClassMemberFunction<House>("addDoorPos", &House::addDoor); // alternative method
|
|
||||||
g_lua.bindClassMemberFunction<House>("setTile", &House::setTile);
|
g_lua.bindClassMemberFunction<House>("setTile", &House::setTile);
|
||||||
g_lua.bindClassMemberFunction<House>("addTile", &House::addDoor); // alternative method
|
g_lua.bindClassMemberFunction<House>("addTile", &House::setTile); // alternative method
|
||||||
|
g_lua.bindClassMemberFunction<House>("setEntry", &House::setEntry);
|
||||||
|
|
||||||
g_lua.registerClass<Town>();
|
g_lua.registerClass<Town>();
|
||||||
g_lua.bindClassStaticFunction<Town>("create", []{ return TownPtr(new Town); });
|
g_lua.bindClassStaticFunction<Town>("create", []{ return TownPtr(new Town); });
|
||||||
|
@ -296,14 +295,14 @@ void OTClient::registerLuaFunctions()
|
||||||
g_lua.bindClassMemberFunction<Town>("getPos", &Town::getPos);
|
g_lua.bindClassMemberFunction<Town>("getPos", &Town::getPos);
|
||||||
g_lua.bindClassMemberFunction<Town>("getTemplePos", &Town::getPos); // alternative method
|
g_lua.bindClassMemberFunction<Town>("getTemplePos", &Town::getPos); // alternative method
|
||||||
|
|
||||||
g_lua.registerClass<MonsterType>();
|
g_lua.registerClass<CreatureType>();
|
||||||
g_lua.bindClassStaticFunction<MonsterType>("create", []{ return MonsterTypePtr(new MonsterType); });
|
g_lua.bindClassStaticFunction<CreatureType>("create", []{ return CreatureTypePtr(new CreatureType); });
|
||||||
g_lua.bindClassMemberFunction<MonsterType>("setPos", &MonsterType::setPos);
|
g_lua.bindClassMemberFunction<CreatureType>("setPos", &CreatureType::setPos);
|
||||||
g_lua.bindClassMemberFunction<MonsterType>("setName", &MonsterType::setName);
|
g_lua.bindClassMemberFunction<CreatureType>("setName", &CreatureType::setName);
|
||||||
g_lua.bindClassMemberFunction<MonsterType>("setOutfit", &MonsterType::setOutfit);
|
g_lua.bindClassMemberFunction<CreatureType>("setOutfit", &CreatureType::setOutfit);
|
||||||
g_lua.bindClassMemberFunction<MonsterType>("getPos", &MonsterType::getPos);
|
g_lua.bindClassMemberFunction<CreatureType>("getPos", &CreatureType::getPos);
|
||||||
g_lua.bindClassMemberFunction<MonsterType>("getName", &MonsterType::getName);
|
g_lua.bindClassMemberFunction<CreatureType>("getName", &CreatureType::getName);
|
||||||
g_lua.bindClassMemberFunction<MonsterType>("getOutfit", &MonsterType::getOutfit);
|
g_lua.bindClassMemberFunction<CreatureType>("getOutfit", &CreatureType::getOutfit);
|
||||||
|
|
||||||
g_lua.registerClass<Creature, Thing>();
|
g_lua.registerClass<Creature, Thing>();
|
||||||
g_lua.bindClassStaticFunction<Creature>("create", []{ return CreaturePtr(new Creature); });
|
g_lua.bindClassStaticFunction<Creature>("create", []{ return CreaturePtr(new Creature); });
|
||||||
|
|
|
@ -205,13 +205,10 @@ void Map::loadOtbm(const std::string& fileName)
|
||||||
m_containers.push_back(mapContainer);
|
m_containers.push_back(mapContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(house) {
|
if(house && item->isMoveable()) {
|
||||||
if(item->isMoveable()) {
|
g_logger.warning(stdext::format("Movable item found in house: %d at pos %d %d %d - escaping...", item->getId(),
|
||||||
g_logger.warning(stdext::format("Movable item found in house: %d at pos %d %d %d - escaping...", item->getId(),
|
px, py, pz));
|
||||||
px, py, pz));
|
item.reset();
|
||||||
item.reset();
|
|
||||||
} else if(item->isDoor())
|
|
||||||
house->addDoor(item->getDoorId(), pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addThing(item, pos);
|
addThing(item, pos);
|
||||||
|
@ -232,7 +229,8 @@ void Map::loadOtbm(const std::string& fileName)
|
||||||
if(!(town = m_towns.getTown(townId))) {
|
if(!(town = m_towns.getTown(townId))) {
|
||||||
town = TownPtr(new Town(townId, townName, townCoords));
|
town = TownPtr(new Town(townId, townName, townCoords));
|
||||||
m_towns.addTown(town);
|
m_towns.addTown(town);
|
||||||
} // a map editor cannot be that dumb and write duplicate towns
|
}
|
||||||
|
g_logger.debug(stdext::format("new town %ld %s", townId, townName));
|
||||||
}
|
}
|
||||||
} else if(mapDataType == OTBM_WAYPOINTS && headerVersion > 1) {
|
} else if(mapDataType == OTBM_WAYPOINTS && headerVersion > 1) {
|
||||||
for(const BinaryTreePtr &nodeWaypoint : nodeMapData->getChildren()) {
|
for(const BinaryTreePtr &nodeWaypoint : nodeMapData->getChildren()) {
|
||||||
|
@ -373,8 +371,8 @@ void Map::saveOtbm(const std::string &fileName)
|
||||||
|
|
||||||
void Map::loadSpawns(const std::string &fileName)
|
void Map::loadSpawns(const std::string &fileName)
|
||||||
{
|
{
|
||||||
if(!m_monsters.isLoaded())
|
if(!m_creatures.isLoaded())
|
||||||
stdext::throw_exception("cannot load spawns; monsters aren't loaded.");
|
stdext::throw_exception("cannot load spawns; monsters/nps aren't loaded.");
|
||||||
|
|
||||||
TiXmlDocument doc;
|
TiXmlDocument doc;
|
||||||
doc.Parse(g_resources.loadFile(fileName).c_str());
|
doc.Parse(g_resources.loadFile(fileName).c_str());
|
||||||
|
@ -394,10 +392,8 @@ void Map::loadSpawns(const std::string &fileName)
|
||||||
if(mType->ValueStr() != "monster" && mType->ValueStr() != "npc")
|
if(mType->ValueStr() != "monster" && mType->ValueStr() != "npc")
|
||||||
stdext::throw_exception(stdext::format("invalid spawn-subnode %s", mType->ValueStr()));
|
stdext::throw_exception(stdext::format("invalid spawn-subnode %s", mType->ValueStr()));
|
||||||
|
|
||||||
if(mType->ValueStr() == "npc") // escape npc's for now...
|
|
||||||
continue;
|
|
||||||
std::string mName = mType->Attribute("name");
|
std::string mName = mType->Attribute("name");
|
||||||
MonsterTypePtr m = getMonster(mName);
|
CreatureTypePtr m = getCreature(mName);
|
||||||
if (!m)
|
if (!m)
|
||||||
stdext::throw_exception(stdext::format("unkown monster '%s'", stdext::trim(stdext::tolower(mName))));
|
stdext::throw_exception(stdext::format("unkown monster '%s'", stdext::trim(stdext::tolower(mName))));
|
||||||
|
|
||||||
|
@ -560,7 +556,7 @@ void Map::clean()
|
||||||
// This is a fix to a segfault on exit.
|
// This is a fix to a segfault on exit.
|
||||||
m_towns.clear();
|
m_towns.clear();
|
||||||
m_houses.clear();
|
m_houses.clear();
|
||||||
m_monsters.clear();
|
m_creatures.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Map::cleanDynamicThings()
|
void Map::cleanDynamicThings()
|
||||||
|
@ -1030,8 +1026,3 @@ std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
MonsterTypePtr Map::getMonster(const std::string& name)
|
|
||||||
{
|
|
||||||
return m_monsters.getMonster(name);
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include "creature.h"
|
#include "creature.h"
|
||||||
#include "houses.h"
|
#include "houses.h"
|
||||||
#include "towns.h"
|
#include "towns.h"
|
||||||
#include "monsters.h"
|
#include "creatures.h"
|
||||||
#include "animatedtext.h"
|
#include "animatedtext.h"
|
||||||
#include <framework/core/clock.h>
|
#include <framework/core/clock.h>
|
||||||
|
|
||||||
|
@ -119,8 +119,9 @@ public:
|
||||||
void loadSpawns(const std::string& fileName);
|
void loadSpawns(const std::string& fileName);
|
||||||
void saveSpawns(const std::string&) { }
|
void saveSpawns(const std::string&) { }
|
||||||
|
|
||||||
void loadMonsters(const std::string& fileName) { m_monsters.loadMonsters(fileName); }
|
void loadMonsters(const std::string& fileName) { m_creatures.loadMonsters(fileName); }
|
||||||
void loadSingleMonster(const std::string& file) { m_monsters.loadSingleMonster(file); }
|
void loadSingleCreature(const std::string& file) { m_creatures.loadSingleCreature(file); }
|
||||||
|
void loadNpcs(const std::string& folder) { m_creatures.loadNpcs(folder); }
|
||||||
|
|
||||||
void clean();
|
void clean();
|
||||||
void cleanDynamicThings();
|
void cleanDynamicThings();
|
||||||
|
@ -151,7 +152,7 @@ public:
|
||||||
// town/house/monster related
|
// town/house/monster related
|
||||||
TownPtr getTown(uint32 tid) { return m_towns.getTown(tid); }
|
TownPtr getTown(uint32 tid) { return m_towns.getTown(tid); }
|
||||||
HousePtr getHouse(uint32 hid) { return m_houses.getHouse(hid); }
|
HousePtr getHouse(uint32 hid) { return m_houses.getHouse(hid); }
|
||||||
MonsterTypePtr getMonster(const std::string &name);
|
CreatureTypePtr getCreature(const std::string &name) { return m_creatures.getCreature(name); }
|
||||||
|
|
||||||
void setLight(const Light& light) { m_light = light; }
|
void setLight(const Light& light) { m_light = light; }
|
||||||
void setCentralPosition(const Position& centralPosition);
|
void setCentralPosition(const Position& centralPosition);
|
||||||
|
@ -173,7 +174,7 @@ public:
|
||||||
std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> findPath(const Position& start, const Position& goal, int maxSteps);
|
std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> findPath(const Position& start, const Position& goal, int maxSteps);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<Position, TilePtr, PositionHasher> m_tiles;
|
TileMap m_tiles;
|
||||||
std::map<uint32, CreaturePtr> m_knownCreatures;
|
std::map<uint32, CreaturePtr> m_knownCreatures;
|
||||||
std::array<std::vector<MissilePtr>, Otc::MAX_Z+1> m_floorMissiles;
|
std::array<std::vector<MissilePtr>, Otc::MAX_Z+1> m_floorMissiles;
|
||||||
std::vector<AnimatedTextPtr> m_animatedTexts;
|
std::vector<AnimatedTextPtr> m_animatedTexts;
|
||||||
|
@ -189,7 +190,7 @@ private:
|
||||||
|
|
||||||
Houses m_houses;
|
Houses m_houses;
|
||||||
Towns m_towns;
|
Towns m_towns;
|
||||||
Monsters m_monsters;
|
Creatures m_creatures;
|
||||||
|
|
||||||
uint16 m_width, m_height;
|
uint16 m_width, m_height;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "monsters.h"
|
|
||||||
#include "creature.h"
|
|
||||||
|
|
||||||
#include <framework/xml/tinyxml.h>
|
|
||||||
#include <framework/core/resourcemanager.h>
|
|
||||||
|
|
||||||
void Monsters::loadMonsters(const std::string& file)
|
|
||||||
{
|
|
||||||
TiXmlDocument doc;
|
|
||||||
doc.Parse(g_resources.loadFile(file).c_str());
|
|
||||||
if(doc.Error())
|
|
||||||
stdext::throw_exception(stdext::format("cannot open monsters file '%s': '%s'", file, doc.ErrorDesc()));
|
|
||||||
|
|
||||||
TiXmlElement* root = doc.FirstChildElement();
|
|
||||||
if(!root || root->ValueStr() != "monsters")
|
|
||||||
stdext::throw_exception("malformed monsters xml file");
|
|
||||||
|
|
||||||
for(TiXmlElement* monster = root->FirstChildElement(); monster; monster = monster->NextSiblingElement()) {
|
|
||||||
MonsterTypePtr newMonster(new MonsterType(stdext::trim(stdext::tolower(monster->Attribute("name")))));
|
|
||||||
std::string fname = file.substr(0, file.find_last_of('/')) + '/' + monster->Attribute("file");
|
|
||||||
|
|
||||||
if(fname.substr(fname.length() - 4) != ".xml")
|
|
||||||
fname += ".xml";
|
|
||||||
|
|
||||||
loadSingleMonster(fname, newMonster);
|
|
||||||
}
|
|
||||||
|
|
||||||
doc.Clear();
|
|
||||||
m_loaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Monsters::loadSingleMonster(const std::string& file, const MonsterTypePtr& m)
|
|
||||||
{
|
|
||||||
if (!m || std::find(m_monsters.begin(), m_monsters.end(), m) != m_monsters.end())
|
|
||||||
stdext::throw_exception("reloading monsters is not supported yet.");
|
|
||||||
|
|
||||||
TiXmlDocument doc;
|
|
||||||
doc.Parse(g_resources.loadFile(file).c_str());
|
|
||||||
if(doc.Error())
|
|
||||||
stdext::throw_exception(stdext::format("cannot load single monster file '%s': '%s'", file, doc.ErrorDesc()));
|
|
||||||
|
|
||||||
TiXmlElement* root = doc.FirstChildElement();
|
|
||||||
if(!root || root->ValueStr() != "monster")
|
|
||||||
stdext::throw_exception(stdext::format("malformed monster xml file: %s", file));
|
|
||||||
|
|
||||||
for(TiXmlElement* attrib = root->FirstChildElement(); attrib; attrib = attrib->NextSiblingElement()) {
|
|
||||||
if(attrib->ValueStr() == "look") {
|
|
||||||
Outfit out;
|
|
||||||
|
|
||||||
int type = 0;
|
|
||||||
if(!attrib->Attribute("type").empty())
|
|
||||||
type = attrib->readType<int>("type");
|
|
||||||
else
|
|
||||||
type = attrib->readType<int>("typeex");
|
|
||||||
out.setId(type);
|
|
||||||
{
|
|
||||||
out.setHead(attrib->readType<int>(("head")));
|
|
||||||
out.setBody(attrib->readType<int>(("body")));
|
|
||||||
out.setLegs(attrib->readType<int>(("legs")));
|
|
||||||
out.setFeet(attrib->readType<int>(("feet")));
|
|
||||||
out.setAddons(attrib->readType<int>(("addons")));
|
|
||||||
out.setMount(attrib->readType<int>(("mount")));
|
|
||||||
}
|
|
||||||
m->setOutfit(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_monsters.push_back(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
MonsterTypePtr Monsters::getMonster(const std::string& name)
|
|
||||||
{
|
|
||||||
auto it = std::find_if(m_monsters.begin(), m_monsters.end(),
|
|
||||||
[=] (const MonsterTypePtr& m) -> bool { return m->getName() == stdext::trim(stdext::tolower(name)); });
|
|
||||||
return it != m_monsters.end() ? *it : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
MonsterTypePtr Monsters::getMonsterByPos(const Position& pos)
|
|
||||||
{
|
|
||||||
auto it = std::find_if(m_monsters.begin(), m_monsters.end(),
|
|
||||||
[=] (const MonsterTypePtr& m) -> bool { return m->getPos() == pos; });
|
|
||||||
return it != m_monsters.end() ? *it : nullptr;
|
|
||||||
}
|
|
|
@ -124,6 +124,7 @@ void ThingTypeManager::loadOtb(const std::string& file)
|
||||||
|
|
||||||
void ThingTypeManager::loadXml(const std::string& file)
|
void ThingTypeManager::loadXml(const std::string& file)
|
||||||
{
|
{
|
||||||
|
/// Read XML
|
||||||
TiXmlDocument doc;
|
TiXmlDocument doc;
|
||||||
doc.Parse(g_resources.loadFile(file).c_str());
|
doc.Parse(g_resources.loadFile(file).c_str());
|
||||||
if(doc.Error())
|
if(doc.Error())
|
||||||
|
|
|
@ -30,8 +30,6 @@
|
||||||
ThingTypeOtb::ThingTypeOtb()
|
ThingTypeOtb::ThingTypeOtb()
|
||||||
{
|
{
|
||||||
m_category = OtbInvalidCateogry;
|
m_category = OtbInvalidCateogry;
|
||||||
m_serverId = 0;
|
|
||||||
m_clientId = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThingTypeOtb::unserialize(const BinaryTreePtr& node)
|
void ThingTypeOtb::unserialize(const BinaryTreePtr& node)
|
||||||
|
@ -51,23 +49,33 @@ void ThingTypeOtb::unserialize(const BinaryTreePtr& node)
|
||||||
uint16 len = node->getU16();
|
uint16 len = node->getU16();
|
||||||
switch(attr) {
|
switch(attr) {
|
||||||
case OtbAttribServerId: {
|
case OtbAttribServerId: {
|
||||||
m_serverId = node->getU16();
|
uint16 serverId = node->getU16();
|
||||||
if(m_serverId > 20000 && m_serverId < 20100) {
|
if(serverId > 20000 && serverId < 20100) {
|
||||||
m_serverId -= 20000;
|
serverId -= 20000;
|
||||||
} else if(lastId > 99 && lastId != m_serverId - 1) {
|
} else if(lastId > 99 && lastId != serverId - 1) {
|
||||||
static ThingTypeOtbPtr dummyType(g_things.getNullOtbType());
|
static ThingTypeOtbPtr dummyType(g_things.getNullOtbType());
|
||||||
while(lastId != m_serverId - 1) {
|
while(lastId != serverId - 1) {
|
||||||
dummyType->setServerId(++lastId);
|
dummyType->setServerId(++lastId);
|
||||||
g_things.addOtbType(dummyType);
|
g_things.addOtbType(dummyType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(len == 2);
|
assert(len == 2);
|
||||||
|
setServerId(serverId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OtbAttribClientId:
|
case OtbAttribClientId: {
|
||||||
m_clientId = node->getU16();
|
setClientId(node->getU16());
|
||||||
assert(len == 2);
|
assert(len == 2);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case OtbAttribName: {
|
||||||
|
setName(node->getString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OtbAttribDesc: {
|
||||||
|
setDesc(node->getString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
node->skip(len); // skip attribute
|
node->skip(len); // skip attribute
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <framework/core/declarations.h>
|
#include <framework/core/declarations.h>
|
||||||
#include <framework/luaengine/luaobject.h>
|
#include <framework/luaengine/luaobject.h>
|
||||||
#include <framework/xml/tinyxml.h>
|
#include <framework/xml/tinyxml.h>
|
||||||
|
#include <framework/util/attribstorage.h>
|
||||||
|
|
||||||
|
|
||||||
enum OtbCategory {
|
enum OtbCategory {
|
||||||
|
@ -88,23 +89,24 @@ public:
|
||||||
|
|
||||||
void unserialize(const BinaryTreePtr& node);
|
void unserialize(const BinaryTreePtr& node);
|
||||||
|
|
||||||
uint16 getServerId() { return m_serverId; }
|
uint16 getServerId() { return m_attribs.get<uint16>(OtbAttribServerId); }
|
||||||
uint16 getClientId() { return m_clientId; }
|
uint16 getClientId() { return m_attribs.get<uint16>(OtbAttribClientId); }
|
||||||
OtbCategory getCategory() { return m_category; }
|
OtbCategory getCategory() { return m_category; }
|
||||||
|
std::string getName() { return m_attribs.get<std::string>(OtbAttribName); }
|
||||||
|
std::string getDesc() { return m_attribs.get<std::string>(OtbAttribDesc); }
|
||||||
|
|
||||||
bool isNull() { return m_null; }
|
bool isNull() { return m_null; }
|
||||||
|
|
||||||
void setServerId(uint16 serverId) { m_serverId = serverId; }
|
void setClientId(uint16 clientId) { m_attribs.set(OtbAttribClientId, clientId); }
|
||||||
void setName(const std::string& name) { m_name = name; }
|
void setServerId(uint16 serverId) { m_attribs.set(OtbAttribServerId, serverId); }
|
||||||
void setDesc(const std::string& desc) { m_desc = desc; }
|
void setName(const std::string& name) { m_attribs.set(OtbAttribName, name); }
|
||||||
|
void setDesc(const std::string& desc) { m_attribs.set(OtbAttribDesc, desc); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint16 m_serverId;
|
|
||||||
uint16 m_clientId;
|
|
||||||
|
|
||||||
std::string m_name, m_desc;
|
|
||||||
OtbCategory m_category;
|
OtbCategory m_category;
|
||||||
Boolean<true> m_null;
|
Boolean<true> m_null;
|
||||||
|
|
||||||
|
AttribStorage m_attribs;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue