Part 21: Objects - Part 1: In the editor
Downloads
Storing the objects
class CMap
{
[...]
std::vector<CObjectStack> mObjectsStacks;
};
We will need a few functions to handle those stacks - add a new one to the list, remove one, find if a stack
//---------------------------------------------------------------------------------------------
int CMap::findObjectsStackIndex(CVec2 pos)
{
for (size_t i = 0; i < mObjectsStacks.size(); ++i)
if (mObjectsStacks[i].mPos == pos)
return i;
return -1;
}
//---------------------------------------------------------------------------------------------
CObjectStack* CMap::findObjectsStack(CVec2 pos)
{
int i = findObjectsStackIndex(pos);
if (i != -1)
return &mObjectsStacks[i];
else
return NULL;
}
//---------------------------------------------------------------------------------------------
CObjectStack* CMap::addObjectsStack(CVec2 pos)
{
int i = findObjectsStackIndex(pos);
if (i != -1)
{
return &mObjectsStacks[i];
}
else
{
CObjectStack newStack;
newStack.mPos = pos;
mObjectsStacks.push_back(newStack);
return &mObjectsStacks[mObjectsStacks.size() - 1];
}
}
//---------------------------------------------------------------------------------------------
void CMap::removeObjectsStack(CVec2 pos)
{
int i = findObjectsStackIndex(pos);
if (i != -1)
mObjectsStacks.erase(mObjectsStacks.begin() + i);
}
In the map file, these stacks will be saved after the tiles and walls datas:
void CMap::save(char* fileName)
{
FILE* handle = fopen(fileName, "wb");
if (handle != NULL)
{
// header, version, revision
[...]
// map size
[...]
// map data
[...]
// objects
uint32_t numStacks = mObjectsStacks.size();
fwrite(&numStacks, 4, 1, handle);
for (size_t i = 0; i < numStacks; ++i)
mObjectsStacks[i].save(handle);
fclose(handle);
}
}
Obviously the code is similar for the loading.
The stack class
class CObjectStack
{
public:
CObjectStack();
virtual ~CObjectStack();
void addObject(CObject object);
CObject& getObject(int index);
void removeObject(int index);
size_t getSize();
CObjectStack& operator=(const CObjectStack& rhs);
void load(FILE* handle);
void save(FILE* handle);
CVec2 mPos;
private:
std::vector<CObject> mObjects;
};
It holds its position in the map - mPos - and the list of its objects - mObjects.
//---------------------------------------------------------------------------------------------
void CObjectStack::addObject(CObject object)
{
mObjects.push_back(object);
}
//---------------------------------------------------------------------------------------------
CObject& CObjectStack::getObject(int index)
{
return mObjects[index];
}
//---------------------------------------------------------------------------------------------
void CObjectStack::removeObject(int index)
{
mObjects.erase(mObjects.begin() + index);
}
//---------------------------------------------------------------------------------------------
size_t CObjectStack::getSize()
{
return mObjects.size();
}
//---------------------------------------------------------------------------------------------
CObjectStack& CObjectStack::operator=(const CObjectStack& rhs)
{
mPos = rhs.mPos;
mObjects.resize(rhs.mObjects.size());
for (size_t i = 0; i < mObjects.size(); ++i)
mObjects[i] = rhs.mObjects[i];
return *this;
}
//---------------------------------------------------------------------------------------------
void CObjectStack::load(FILE* handle)
{
mObjects.clear();
fread(&mPos.x, sizeof(int32_t), 1, handle);
fread(&mPos.y, sizeof(int32_t), 1, handle);
uint32_t size;
fread(&size, sizeof(uint32_t), 1, handle);
for (uint32_t i = 0; i < size; ++i)
{
CObject object;
object.load(handle);
mObjects.push_back(object);
}
}
//---------------------------------------------------------------------------------------------
void CObjectStack::save(FILE* handle)
{
fwrite(&mPos.x, sizeof(int32_t), 1, handle);
fwrite(&mPos.y, sizeof(int32_t), 1, handle);
uint32_t size = mObjects.size();
fwrite(&size, sizeof(uint32_t), 1, handle);
for (size_t i = 0; i < mObjects.size(); ++i)
mObjects[i].save(handle);
}
The object class
class CObject
{
public:
CObject();
virtual ~CObject();
void setType(uint8_t type);
uint8_t getType();
CObject& operator=(const CObject& rhs);
void load(FILE* handle);
void save(FILE* handle);
private:
uint8_t mType;
};
It only hold a type but I made a class that looks like the ones for the tiles or the walls,
//---------------------------------------------------------------------------------------------
CObject::CObject()
{
mType = 0;
}
//---------------------------------------------------------------------------------------------
CObject::~CObject()
{
}
//---------------------------------------------------------------------------------------------
void CObject::setType(uint8_t type)
{
mType = type;
}
//---------------------------------------------------------------------------------------------
uint8_t CObject::getType()
{
return mType;
}
//---------------------------------------------------------------------------------------------
CObject& CObject::operator=(const CObject& rhs)
{
setType(rhs.mType);
return *this;
}
//---------------------------------------------------------------------------------------------
void CObject::load(FILE* handle)
{
uint8_t type;
fread(&type, 1, 1, handle);
setType(type);
}
//---------------------------------------------------------------------------------------------
void CObject::save(FILE* handle)
{
fwrite(&mType, 1, 1, handle);
}
Displaying the stacks in the editor
QImage CEditor::displayMap(CMap& map, CVec2 mousePos, ETabIndex tabIndex)
{
[...]
if (map.isLoaded() == true)
{
// draw tiles
[...]
// draw walls
[...]
// draw objects
for (int y = 0; y < map.mSize.y * 2; ++y)
for (int x = 0; x < map.mSize.x * 2; ++x)
{
CVec2 pos(x, y);
if (map.findObjectsStack(pos) != NULL)
{
QImage objImage = fileCache.getImage("tiles/Object.png");
CVec2 pos2 = pos * (TILE_SIZE / 2);
graph2D.drawImage(&image, pos2, objImage);
}
}
}
[...]
}
We simply loop through all of the possible positions in the map, and display a dot if we found a stack
The objects list
<?xml version="1.0" encoding="utf-8"?>
<items>
<item name="[None]"/>
<item name="Eye Of Time"/>
<item name="Stormring"/>
<item name="Torch"/>
[...]
</items>
The game will use a different database as some object names in the game differs frome the ones we use in the editor
The addObject tool
addObjectTool::addObjectTool(QUndoCommand *parent)
: QUndoCommand(parent)
{
}
bool addObjectTool::init(CVec2 pos, int type)
{
mPos = pos / (TILE_SIZE / 2);
mType = type;
return true;
}
void addObjectTool::undo()
{
CObjectStack* stack = map.findObjectsStack(mPos);
if (stack != NULL)
{
size_t size = stack->getSize();
stack->removeObject(size - 1);
if (size == 1)
map.removeObjectsStack(mPos);
}
bridge.updateQMLImage();
}
void addObjectTool::redo()
{
CObject newObject;
newObject.setType(mType);
CObjectStack* stack = map.addObjectsStack(mPos);
stack->addObject(newObject);
bridge.updateQMLImage();
}
Note that in the undo() function, we check if the stack is empty to remove it completely from the stacks list in CMap.
Selection of a stack
void CEditor::addObjParamsItems()
{
CVec2 pos = mSelectStart / (TILE_SIZE / 2);
CObjectStack* stack = map.findObjectsStack(pos);
size_t size = 0;
if (stack != NULL)
{
for (size_t i = 0; i < stack->getSize(); ++i)
{
addButton(objSelGrid, "delete", &mObjParamItems, i);
addComboBox(objSelGrid, bridge.itemsList, &mObjParamItems, i, stack->getObject(i).getType());
}
size = stack->getSize();
}
addButton(objSelGrid, "add", &mObjParamItems, size);
}
Finally I also had to make a lot of changes to "clipboard.cpp" and to the cut and paste tools to handle all the cases of