ignore this
This commit is contained in:
parent
f289db3a9e
commit
f74b013da2
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include "declarations.h"
|
#include "declarations.h"
|
||||||
#include <framework/util/databuffer.h>
|
#include <framework/util/databuffer.h>
|
||||||
|
#include <otclient/position.h>
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
BINARYTREE_ESCAPE_CHAR = 0xFD,
|
BINARYTREE_ESCAPE_CHAR = 0xFD,
|
||||||
|
@ -48,6 +49,8 @@ public:
|
||||||
uint32 getU32();
|
uint32 getU32();
|
||||||
uint64 getU64();
|
uint64 getU64();
|
||||||
std::string getString();
|
std::string getString();
|
||||||
|
Position getPosition() { return Position(getU16(), getU16(), getU8()); }
|
||||||
|
Point getPoint() { return Point(getU8(), getU8()); }
|
||||||
|
|
||||||
BinaryTreeVec getChildren();
|
BinaryTreeVec getChildren();
|
||||||
bool canRead() { unserialize(); return m_pos < m_buffer.size(); }
|
bool canRead() { unserialize(); return m_pos < m_buffer.size(); }
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <otclient/position.h>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "cast.h"
|
#include "cast.h"
|
||||||
|
@ -180,6 +181,11 @@ inline std::string ip_to_string(uint32 ip) {
|
||||||
return std::string(host);
|
return std::string(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string pos_to_string(const Position& p)
|
||||||
|
{
|
||||||
|
return format("{x = %h, y = %h, z = %hh}", p.x, p.y, p.z);
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert utf8 characters to latin1
|
/// Convert utf8 characters to latin1
|
||||||
inline char utf8CharToLatin1(uchar *utf8, int *read) {
|
inline char utf8CharToLatin1(uchar *utf8, int *read) {
|
||||||
char c = '?';
|
char c = '?';
|
||||||
|
|
|
@ -173,7 +173,7 @@ enum TiXmlEncoding
|
||||||
TIXML_ENCODING_LEGACY
|
TIXML_ENCODING_LEGACY
|
||||||
};
|
};
|
||||||
|
|
||||||
const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
|
constexpr TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
|
||||||
|
|
||||||
/** TiXmlBase is a base class for every class in TinyXml.
|
/** TiXmlBase is a base class for every class in TinyXml.
|
||||||
It does little except to establish that TinyXml classes
|
It does little except to establish that TinyXml classes
|
||||||
|
|
|
@ -62,7 +62,7 @@ void Creatures::loadNpcs(const std::string &folder)
|
||||||
|
|
||||||
for(boost::filesystem::directory_iterator it(npcPath), end; it != end; ++it) {
|
for(boost::filesystem::directory_iterator it(npcPath), end; it != end; ++it) {
|
||||||
std::string f = it->path().string();
|
std::string f = it->path().string();
|
||||||
if(boost::filesystem::is_directory(it->status()) && ((f.size() > 4 ? f.substr(f.size() - 4) : "") != ".xml"))
|
if(boost::filesystem::is_directory(it->status()) || ((f.size() > 4 ? f.substr(f.size() - 4) : "") != ".xml"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
loadCreatureBuffer(g_resources.loadFile(f));
|
loadCreatureBuffer(g_resources.loadFile(f));
|
||||||
|
@ -78,15 +78,18 @@ void Creatures::loadCreatureBuffer(const std::string &buffer)
|
||||||
|
|
||||||
TiXmlElement* root = doc.FirstChildElement();
|
TiXmlElement* root = doc.FirstChildElement();
|
||||||
if(!root || root->ValueStr() != "npc")
|
if(!root || root->ValueStr() != "npc")
|
||||||
stdext::throw_exception(("invalid root tag name"));
|
stdext::throw_exception("invalid root tag name");
|
||||||
|
|
||||||
for(TiXmlElement* attrib = root->FirstChildElement(); attrib; attrib = attrib->NextSiblingElement()) {
|
for(TiXmlElement* attrib = root->FirstChildElement(); attrib; attrib = attrib->NextSiblingElement()) {
|
||||||
if(attrib->ValueStr() != "npc" && attrib->ValueStr() != "monster")
|
if(attrib->ValueStr() != "npc" && attrib->ValueStr() != "monster")
|
||||||
stdext::throw_exception(stdext::format("invalid attribute '%s'", attrib->ValueStr()));
|
stdext::throw_exception(stdext::format("invalid attribute '%s'", attrib->ValueStr()));
|
||||||
|
|
||||||
CreatureTypePtr newType(nullptr);
|
CreatureTypePtr newType(nullptr);
|
||||||
m_loadCreatureBuffer(attrib, newType);
|
if(m_loadCreatureBuffer(attrib, newType))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doc.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Creatures::m_loadCreatureBuffer(TiXmlElement* attrib, CreatureTypePtr& m)
|
bool Creatures::m_loadCreatureBuffer(TiXmlElement* attrib, CreatureTypePtr& m)
|
||||||
|
@ -98,13 +101,17 @@ bool Creatures::m_loadCreatureBuffer(TiXmlElement* attrib, CreatureTypePtr& m)
|
||||||
Outfit out;
|
Outfit out;
|
||||||
|
|
||||||
int32 type;
|
int32 type;
|
||||||
|
bool isTypeEx=false;
|
||||||
if(!attrib->Attribute("type").empty())
|
if(!attrib->Attribute("type").empty())
|
||||||
type = attrib->readType<int32>("type");
|
type = attrib->readType<int32>("type");
|
||||||
else
|
else {
|
||||||
type = attrib->readType<int32>("typeex");
|
type = attrib->readType<int32>("typeex");
|
||||||
|
isTypeEx = true;
|
||||||
|
}
|
||||||
|
|
||||||
out.setId(type);
|
out.setId(type);
|
||||||
{
|
|
||||||
|
if(!isTypeEx) {
|
||||||
out.setHead(attrib->readType<int>(("head")));
|
out.setHead(attrib->readType<int>(("head")));
|
||||||
out.setBody(attrib->readType<int>(("body")));
|
out.setBody(attrib->readType<int>(("body")));
|
||||||
out.setLegs(attrib->readType<int>(("legs")));
|
out.setLegs(attrib->readType<int>(("legs")));
|
||||||
|
|
|
@ -20,34 +20,40 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MONSTERS_H
|
#ifndef CREATURES_H
|
||||||
#define MONSTERS_H
|
#define CREATURES_H
|
||||||
|
|
||||||
#include "declarations.h"
|
#include "declarations.h"
|
||||||
#include <framework/luaengine/luaobject.h>
|
#include <framework/luaengine/luaobject.h>
|
||||||
|
#include <framework/util/attribstorage.h>
|
||||||
#include "outfit.h"
|
#include "outfit.h"
|
||||||
|
|
||||||
|
enum CreatureAttributes : unsigned char
|
||||||
|
{
|
||||||
|
CreatureAttribPos,
|
||||||
|
CreatureAttribName,
|
||||||
|
CreatureAttribOutfit,
|
||||||
|
CreatureAttribSpawnTime
|
||||||
|
};
|
||||||
|
|
||||||
class CreatureType : public LuaObject
|
class CreatureType : public LuaObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CreatureType() { }
|
CreatureType() { }
|
||||||
CreatureType(const std::string& name)
|
CreatureType(const std::string& name) { setName(name); }
|
||||||
: m_name(name) { }
|
|
||||||
|
|
||||||
void setPos(const Position& pos) { m_pos = pos; }
|
void setPos(const Position& pos) { m_attribs.set(CreatureAttribPos, pos); }
|
||||||
void setName(const std::string& name) { m_name = name; }
|
void setName(const std::string& name) { m_attribs.set(CreatureAttribName, name); }
|
||||||
void setOutfit(const Outfit& o) { m_outfit = o; }
|
void setOutfit(const Outfit& o) { m_attribs.set(CreatureAttribOutfit, o); }
|
||||||
void setSpawnTime(int spawnTime) { m_spawnTime = spawnTime; }
|
void setSpawnTime(int spawnTime) { m_attribs.set(CreatureAttribSpawnTime, spawnTime); }
|
||||||
|
|
||||||
std::string getName() { return m_name; }
|
std::string getName() { return m_attribs.get<std::string>(CreatureAttribName); }
|
||||||
Position getPos() { return m_pos; }
|
Position getPos() { return m_attribs.get<Position>(CreatureAttribPos); }
|
||||||
Outfit getOutfit() { return m_outfit; }
|
Outfit getOutfit() { return m_attribs.get<Outfit>(CreatureAttribOutfit); }
|
||||||
|
int getSpawnTime() { return m_attribs.get<int>(CreatureAttribSpawnTime); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Position m_pos;
|
AttribStorage m_attribs;
|
||||||
std::string m_name;
|
|
||||||
Outfit m_outfit;
|
|
||||||
int m_spawnTime;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Creatures
|
class Creatures
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include <framework/luaengine/luaobject.h>
|
#include <framework/luaengine/luaobject.h>
|
||||||
#include <framework/util/attribstorage.h>
|
#include <framework/util/attribstorage.h>
|
||||||
|
|
||||||
enum HouseAttributes
|
enum HouseAttributes : unsigned char
|
||||||
{
|
{
|
||||||
HouseAttribId,
|
HouseAttribId,
|
||||||
HouseAttribName,
|
HouseAttribName,
|
||||||
|
@ -73,6 +73,7 @@ private:
|
||||||
|
|
||||||
class Houses {
|
class Houses {
|
||||||
public:
|
public:
|
||||||
|
void clear() { m_houses.clear(); }
|
||||||
void addHouse(const HousePtr& house);
|
void addHouse(const HousePtr& house);
|
||||||
void removeHouse(uint32 houseId);
|
void removeHouse(uint32 houseId);
|
||||||
void load(const std::string& fileName);
|
void load(const std::string& fileName);
|
||||||
|
@ -80,9 +81,6 @@ public:
|
||||||
HouseList houseList() const { return m_houses; }
|
HouseList houseList() const { return m_houses; }
|
||||||
HousePtr getHouse(uint32 houseId);
|
HousePtr getHouse(uint32 houseId);
|
||||||
|
|
||||||
// Fix to segfault on exit.
|
|
||||||
void clear() { m_houses.clear(); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HouseList m_houses;
|
HouseList m_houses;
|
||||||
|
|
||||||
|
|
|
@ -300,9 +300,11 @@ void OTClient::registerLuaFunctions()
|
||||||
g_lua.bindClassMemberFunction<CreatureType>("setPos", &CreatureType::setPos);
|
g_lua.bindClassMemberFunction<CreatureType>("setPos", &CreatureType::setPos);
|
||||||
g_lua.bindClassMemberFunction<CreatureType>("setName", &CreatureType::setName);
|
g_lua.bindClassMemberFunction<CreatureType>("setName", &CreatureType::setName);
|
||||||
g_lua.bindClassMemberFunction<CreatureType>("setOutfit", &CreatureType::setOutfit);
|
g_lua.bindClassMemberFunction<CreatureType>("setOutfit", &CreatureType::setOutfit);
|
||||||
|
g_lua.bindClassMemberFunction<CreatureType>("setSpawnTime", &CreatureType::setSpawnTime);
|
||||||
g_lua.bindClassMemberFunction<CreatureType>("getPos", &CreatureType::getPos);
|
g_lua.bindClassMemberFunction<CreatureType>("getPos", &CreatureType::getPos);
|
||||||
g_lua.bindClassMemberFunction<CreatureType>("getName", &CreatureType::getName);
|
g_lua.bindClassMemberFunction<CreatureType>("getName", &CreatureType::getName);
|
||||||
g_lua.bindClassMemberFunction<CreatureType>("getOutfit", &CreatureType::getOutfit);
|
g_lua.bindClassMemberFunction<CreatureType>("getOutfit", &CreatureType::getOutfit);
|
||||||
|
g_lua.bindClassMemberFunction<CreatureType>("getSpawnTime", &CreatureType::getSpawnTime);
|
||||||
|
|
||||||
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); });
|
||||||
|
|
|
@ -129,8 +129,7 @@ void Map::loadOtbm(const std::string& fileName)
|
||||||
for(const BinaryTreePtr &nodeMapData : node->getChildren()) {
|
for(const BinaryTreePtr &nodeMapData : node->getChildren()) {
|
||||||
uint8 mapDataType = nodeMapData->getU8();
|
uint8 mapDataType = nodeMapData->getU8();
|
||||||
if(mapDataType == OTBM_TILE_AREA) {
|
if(mapDataType == OTBM_TILE_AREA) {
|
||||||
uint16 baseX = nodeMapData->getU16(), baseY = nodeMapData->getU16();
|
Position basePos = nodeMapData->getPosition();
|
||||||
uint8 pz = nodeMapData->getU8();
|
|
||||||
|
|
||||||
for(const BinaryTreePtr &nodeTile : nodeMapData->getChildren()) {
|
for(const BinaryTreePtr &nodeTile : nodeMapData->getChildren()) {
|
||||||
uint8 type = nodeTile->getU8();
|
uint8 type = nodeTile->getU8();
|
||||||
|
@ -139,9 +138,7 @@ void Map::loadOtbm(const std::string& fileName)
|
||||||
|
|
||||||
HousePtr house = nullptr;
|
HousePtr house = nullptr;
|
||||||
uint32 flags = TILESTATE_NONE;
|
uint32 flags = TILESTATE_NONE;
|
||||||
|
Position pos = basePos + nodeTile->getPoint();
|
||||||
uint16 px = baseX + nodeTile->getU8(), py = baseY + nodeTile->getU8();
|
|
||||||
Position pos(px, py, pz);
|
|
||||||
|
|
||||||
if(type == OTBM_HOUSETILE) {
|
if(type == OTBM_HOUSETILE) {
|
||||||
uint32 hId = nodeTile->getU32();
|
uint32 hId = nodeTile->getU32();
|
||||||
|
@ -178,8 +175,8 @@ void Map::loadOtbm(const std::string& fileName)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
stdext::throw_exception(stdext::format("invalid tile attribute %d at pos %d, %d, %d",
|
stdext::throw_exception(stdext::format("invalid tile attribute %d at pos %s",
|
||||||
(int)tileAttr, px, py, pz));
|
(int)tileAttr, stdext::pos_to_string(pos)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,8 +203,8 @@ void Map::loadOtbm(const std::string& fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(house && item->isMoveable()) {
|
if(house && 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 %s - escaping...", item->getId(),
|
||||||
px, py, pz));
|
stdext::pos_to_string(pos)));
|
||||||
item.reset();
|
item.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,12 +222,11 @@ void Map::loadOtbm(const std::string& fileName)
|
||||||
|
|
||||||
uint32 townId = nodeTown->getU32();
|
uint32 townId = nodeTown->getU32();
|
||||||
std::string townName = nodeTown->getString();
|
std::string townName = nodeTown->getString();
|
||||||
Position townCoords(nodeTown->getU16(), nodeTown->getU16(), nodeTown->getU8());
|
Position townCoords = nodeTown->getPosition();
|
||||||
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);
|
||||||
}
|
}
|
||||||
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()) {
|
||||||
|
@ -238,15 +234,14 @@ void Map::loadOtbm(const std::string& fileName)
|
||||||
stdext::throw_exception("invalid waypoint node.");
|
stdext::throw_exception("invalid waypoint node.");
|
||||||
|
|
||||||
std::string name = nodeWaypoint->getString();
|
std::string name = nodeWaypoint->getString();
|
||||||
Position waypointPos(nodeWaypoint->getU16(), nodeWaypoint->getU16(), nodeWaypoint->getU8());
|
Position waypointPos = nodeWaypoint->getPosition();
|
||||||
if(waypointPos.isValid() && !name.empty() && m_waypoints.find(waypointPos) == m_waypoints.end())
|
if(waypointPos.isValid() && !name.empty() && m_waypoints.find(waypointPos) == m_waypoints.end())
|
||||||
m_waypoints.insert(std::make_pair(waypointPos, name));
|
m_waypoints.insert(std::make_pair(waypointPos, name));
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
stdext::throw_exception("Unknown map data node");
|
stdext::throw_exception(stdext::format("Unknown map data node %d", (int)mapDataType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int numItems = 0;
|
int numItems = 0;
|
||||||
for(const auto& it : m_tiles)
|
for(const auto& it : m_tiles)
|
||||||
numItems += it.second->getThingCount();
|
numItems += it.second->getThingCount();
|
||||||
|
@ -327,7 +322,7 @@ void Map::saveOtbm(const std::string &fileName)
|
||||||
|
|
||||||
Position pos(-1, -1, -1);
|
Position pos(-1, -1, -1);
|
||||||
Boolean<true> first;
|
Boolean<true> first;
|
||||||
for (auto& pair : m_tiles) {
|
for (const auto& pair : m_tiles) {
|
||||||
TilePtr tile = pair.second;
|
TilePtr tile = pair.second;
|
||||||
if(!tile || tile->isEmpty())
|
if(!tile || tile->isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
@ -397,15 +392,12 @@ void Map::loadSpawns(const std::string &fileName)
|
||||||
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))));
|
||||||
|
|
||||||
Point off = mType->readPoint();
|
m->setPos(centerPos + mType->readPoint());
|
||||||
Position mPos(centerPos.x + off.x, centerPos.y + off.y, centerPos.z);
|
|
||||||
m->setPos(mPos);
|
|
||||||
m->setSpawnTime(mType->readType<int>("spawntime"));
|
m->setSpawnTime(mType->readType<int>("spawntime"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.Clear();
|
doc.Clear();
|
||||||
g_logger.debug("Loaded spawns");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Map::loadOtcm(const std::string& fileName)
|
bool Map::loadOtcm(const std::string& fileName)
|
||||||
|
@ -439,7 +431,6 @@ bool Map::loadOtcm(const std::string& fileName)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
stdext::throw_exception("otcm version not supported");
|
stdext::throw_exception("otcm version not supported");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fin->seek(start);
|
fin->seek(start);
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
#include "towns.h"
|
#include "towns.h"
|
||||||
#include "creatures.h"
|
#include "creatures.h"
|
||||||
#include "animatedtext.h"
|
#include "animatedtext.h"
|
||||||
|
|
||||||
#include <framework/core/clock.h>
|
#include <framework/core/clock.h>
|
||||||
|
#include <framework/util/attribstorage.h>
|
||||||
|
|
||||||
enum OTBM_AttrTypes_t
|
enum OTBM_AttrTypes_t
|
||||||
{
|
{
|
||||||
|
@ -187,6 +189,7 @@ private:
|
||||||
Position m_centralPosition;
|
Position m_centralPosition;
|
||||||
Rect m_tilesRect;
|
Rect m_tilesRect;
|
||||||
|
|
||||||
|
AttribStorage m_attribs;
|
||||||
std::string m_description, m_spawnFile, m_houseFile;
|
std::string m_description, m_spawnFile, m_houseFile;
|
||||||
|
|
||||||
Houses m_houses;
|
Houses m_houses;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include "const.h"
|
#include "const.h"
|
||||||
#include <framework/stdext/types.h>
|
#include <framework/stdext/types.h>
|
||||||
|
#include <framework/const.h>
|
||||||
#include <framework/util/point.h>
|
#include <framework/util/point.h>
|
||||||
|
|
||||||
class Position
|
class Position
|
||||||
|
@ -161,6 +162,9 @@ public:
|
||||||
Position& operator+=(const Position& other) { x+=other.x; y+=other.y; z +=other.z; return *this; }
|
Position& operator+=(const Position& other) { x+=other.x; y+=other.y; z +=other.z; return *this; }
|
||||||
Position operator-(const Position& other) const { return Position(x - other.x, y - other.y, z - other.z); }
|
Position operator-(const Position& other) const { return Position(x - other.x, y - other.y, z - other.z); }
|
||||||
Position& operator-=(const Position& other) { x-=other.x; y-=other.y; z-=other.z; return *this; }
|
Position& operator-=(const Position& other) { x-=other.x; y-=other.y; z-=other.z; return *this; }
|
||||||
|
// Point conversion(s)
|
||||||
|
Position operator+(const Point& other) const { return Position(x + other.x, y + other.y, z); }
|
||||||
|
Position& operator+=(const Point& other) { x += other.x; y += other.y; return *this; }
|
||||||
|
|
||||||
Position& operator=(const Position& other) { x = other.x; y = other.y; z = other.z; return *this; }
|
Position& operator=(const Position& other) { x = other.x; y = other.y; z = other.z; return *this; }
|
||||||
bool operator==(const Position& other) const { return other.x == x && other.y == y && other.z == z; }
|
bool operator==(const Position& other) const { return other.x == x && other.y == y && other.z == z; }
|
||||||
|
|
Loading…
Reference in New Issue