1
0
cuberite-2a/src/BlockType.cpp
Mattes D 01b8ed5295
Pulled the BlockID and BlockInfo headers from Globals.h. (#4591)
The BlockID.h file was removed from Globals.h and renamed to BlockType.h (main change)
The BlockInfo.h file was removed from Globals.h (main change)
The ENUM_BLOCK_ID and ENUM_ITEM_ID enum names were replaced with ENUM_BLOCK_TYPE and ENUM_ITEM_TYPE (cosmetics)
The various enums, such as eDimension, eDamageType and eExplosionSource were moved from BlockType.h to Defines.h, together with the helper functions for converting between them and strings (StringToDimension et al.) (minor)
Many inline functions were moved from headers to their respective cpp files, so that BlockType.h could be included only into the cpp file, rather than the header.
That broke our tests a bit, since they pick bits and pieces out of the main code and provide stubs for the rest; they had to be re-stubbed and re-verified.
eMonsterType values are no longer tied to E_ITEM_SPAWN_EGG_META_* values
2020-04-03 08:57:01 +02:00

310 lines
5.5 KiB
C++

// BlockType.cpp
// Implements the helper functions for converting Block Type string to int etc.
#include "Globals.h"
#include "IniFile.h"
#include "Item.h"
#include "Mobs/Monster.h"
class cBlockIDMap
{
// Making the map case-insensitive:
struct Comparator
{
bool operator ()(const AString & a_Item1, const AString & a_Item2) const
{
return (NoCaseCompare(a_Item1, a_Item2) > 0);
}
} ;
typedef std::map<AString, std::pair<short, short>, Comparator> ItemMap;
public:
static bool m_bHasRunInit;
cBlockIDMap(void)
{
// Dont load items.ini on construct, this will search the wrong path when running as a service.
}
void init()
{
m_bHasRunInit = true;
cIniFile Ini;
if (!Ini.ReadFile("items.ini"))
{
return;
}
int KeyID = Ini.FindKey("Items");
if (KeyID == cIniFile::noID)
{
return;
}
int NumValues = Ini.GetNumValues(KeyID);
for (int i = 0; i < NumValues; i++)
{
AString Name = Ini.GetValueName(KeyID, i);
if (Name.empty())
{
continue;
}
AString Value = Ini.GetValue(KeyID, i);
AddToMap(Name, Value);
} // for i - Ini.Values[]
}
int Resolve(const AString & a_ItemName)
{
ItemMap::iterator itr = m_Map.find(a_ItemName);
if (itr == m_Map.end())
{
return -1;
}
return itr->second.first;
}
bool ResolveItem(const AString & a_ItemName, cItem & a_Item)
{
// Split into parts divided by either ':' or '^'
AStringVector Split = StringSplitAndTrim(a_ItemName, ":^");
if (Split.empty())
{
return false;
}
ItemMap::iterator itr = m_Map.find(Split[0]);
if (itr != m_Map.end())
{
// Resolved as a string, assign the type and the default damage / count
a_Item.m_ItemType = itr->second.first;
a_Item.m_ItemDamage = itr->second.second;
if (a_Item.m_ItemDamage == -1)
{
a_Item.m_ItemDamage = 0;
}
}
else
{
// Not a resolvable string, try pure numbers: "45:6", "45^6" etc.
if (!StringToInteger(Split[0], a_Item.m_ItemType))
{
// Parsing the number failed
return false;
}
}
// Parse the damage, if present:
if (Split.size() < 2)
{
// Not present, set the item as valid and return success:
a_Item.m_ItemCount = 1;
return true;
}
if (!StringToInteger(Split[1], a_Item.m_ItemDamage))
{
// Parsing the number failed
return false;
}
a_Item.m_ItemCount = 1;
return true;
}
AString Desolve(short a_ItemType, short a_ItemDamage)
{
// First try an exact match, both ItemType and ItemDamage ("birchplanks=5:2"):
for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr)
{
if ((itr->second.first == a_ItemType) && (itr->second.second == a_ItemDamage))
{
return itr->first;
}
} // for itr - m_Map[]
// There is no exact match, try matching ItemType only ("planks=5"):
if (a_ItemDamage == 0)
{
for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr)
{
if ((itr->second.first == a_ItemType) && (itr->second.second == -1))
{
return itr->first;
}
} // for itr - m_Map[]
}
// No match at all, synthesize a string ("5:1"):
AString res;
if (a_ItemDamage == -1)
{
Printf(res, "%d", a_ItemType);
}
else
{
Printf(res, "%d:%d", a_ItemType, a_ItemDamage);
}
return res;
}
protected:
ItemMap m_Map;
void AddToMap(const AString & a_Name, const AString & a_Value)
{
AStringVector Split = StringSplit(a_Value, ":");
if (Split.size() == 1)
{
Split = StringSplit(a_Value, "^");
}
if (Split.empty())
{
return;
}
short ItemType;
if (!StringToInteger(Split[0], ItemType))
{
ASSERT(!"Invalid item type");
}
short ItemDamage = -1;
if (Split.size() > 1 && !StringToInteger(Split[1], ItemDamage))
{
ASSERT(!"Invalid item damage");
}
m_Map[a_Name] = std::make_pair(ItemType, ItemDamage);
}
} ;
bool cBlockIDMap::m_bHasRunInit = false;
static cBlockIDMap gsBlockIDMap;
/*
// Quick self-test:
class Tester
{
public:
Tester(void)
{
cItem Item;
gsBlockIDMap.ResolveItem("charcoal", Item);
AString Charcoal = gsBlockIDMap.Desolve(Item.m_ItemType, Item.m_ItemDamage);
ASSERT(Charcoal == "charcoal");
}
} test;
//*/
int BlockStringToType(const AString & a_BlockTypeString)
{
int res = atoi(a_BlockTypeString.c_str());
if ((res != 0) || (a_BlockTypeString.compare("0") == 0))
{
// It was a valid number, return that
return res;
}
if (!gsBlockIDMap.m_bHasRunInit)
{
gsBlockIDMap.init();
}
return gsBlockIDMap.Resolve(TrimString(a_BlockTypeString));
}
bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item)
{
AString ItemName = TrimString(a_ItemTypeString);
if (ItemName.substr(0, 10) == "minecraft:")
{
ItemName = ItemName.substr(10);
}
if (!gsBlockIDMap.m_bHasRunInit)
{
gsBlockIDMap.init();
}
return gsBlockIDMap.ResolveItem(ItemName, a_Item);
}
AString ItemToString(const cItem & a_Item)
{
if (!gsBlockIDMap.m_bHasRunInit)
{
gsBlockIDMap.init();
}
return gsBlockIDMap.Desolve(a_Item.m_ItemType, a_Item.m_ItemDamage);
}
AString ItemTypeToString(short a_ItemType)
{
if (!gsBlockIDMap.m_bHasRunInit)
{
gsBlockIDMap.init();
}
return gsBlockIDMap.Desolve(a_ItemType, -1);
}
AString ItemToFullString(const cItem & a_Item)
{
AString res;
Printf(res, "%s:%d * %d", ItemToString(a_Item).c_str(), a_Item.m_ItemDamage, a_Item.m_ItemCount);
return res;
}
cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default)
{
AString ItemStr = a_IniFile.GetValueSet(a_Section, a_Key, a_Default);
cItem res;
if (!StringToItem(ItemStr, res))
{
res.Empty();
}
return res;
}