Nooncraft/src/game.cpp

205 lines
5.0 KiB
C++

#include "game.h"
BlockInfo *Game::pointedBlockInfo() const
{
return this->world->blockInfoAt(this->cursorPos);
}
Item Game::handItem() const
{
return this->inventory[27 + this->hotbarCursor].item;
}
void Game::place()
{
ItemStack &stack = this->inventory[27 + this->hotbarCursor];
if(!stack.item.isBlock)
return;
if(!this->world->canPlaceAt(this->cursorPos))
return;
this->world->cellAt(this->cursorPos.x, this->cursorPos.y) =
stack.item.block;
if(--stack.qty == 0)
stack = ItemStack();
}
bool Game::movePlayerBy(WorldCoord dir, bool camera_follow)
{
WorldCoord candidatePos = this->playerPos + dir;
candidatePos = this->world->limits.clampPoint(candidatePos);
if(candidatePos == this->playerPos)
return false;
BlockInfo *info = Nooncraft::getBlockInfo(this->world->cellAt(
candidatePos.x, candidatePos.y));
if(!info || !info->walkable)
return false;
this->playerPos = candidatePos;
if(camera_follow)
this->camera->moveToFollow(this->playerPos);
this->moveCursorBy(dir);
return true;
}
bool Game::moveCursorBy(WorldCoord dir)
{
WorldCoord candidatePos = this->cursorPos + dir;
int reachDistance = 3;
WorldRect reach(
this->playerPos.x - reachDistance,
this->playerPos.x + reachDistance,
this->playerPos.y - reachDistance,
this->playerPos.y + reachDistance);
if(!reach.contains(candidatePos))
return false;
this->cursorPos = this->world->limits.clampPoint(candidatePos);
return true;
}
bool Game::isBreakingBlockWithSuitableTool(bool require_best) const
{
BlockInfo *binfo = this->pointedBlockInfo();
if(!binfo)
return false;
if(binfo->breakability == Breakability::ByAnything)
return !require_best || binfo->toolKind == (ToolKind)0xff;
if(binfo->breakability == Breakability::ByTool) {
Item const &hand = this->handItem();
if(hand.isBlock)
return false;
ItemInfo *iinfo = Nooncraft::getItemInfo(hand);
return iinfo && iinfo->isTool && iinfo->toolKind == binfo->toolKind;
}
return false;
}
int Game::blockBreakingDuration() const
{
BlockInfo *info = this->pointedBlockInfo();
if(!info)
return -1;
/* Determine how many ticks would be needed with the current tool */
// TODO: Query the user's current tool
int requiredTicks = info->baseBreakDurationTicks;
return requiredTicks;
}
int8_t Game::blockBreakingProgress() const
{
return this->damageTicks * 4 / this->blockBreakingDuration();
}
int Game::findEmptyInventorySlot() const
{
/* Prefer hotbar slots */
for(int i = 27; i < 36; i++) {
if(this->inventory[i].isNull())
return i;
}
for(int i = 0; i < 27; i++) {
if(this->inventory[i].isNull())
return i;
}
return -1;
}
int Game::findInventorySlotForItem(Item item) const
{
if(item.isNull())
return -1;
/* First try to find an existing stack in the inventory */
for(int i = 27; i < 36; i++) {
if(this->inventory[i].item == item && !this->inventory[i].isFull())
return i;
}
for(int i = 0; i < 27; i++) {
if(this->inventory[i].item == item && !this->inventory[i].isFull())
return i;
}
/* If not resort to finding an empty slot */
return this->findEmptyInventorySlot();
}
void Game::addItemInSlot(Item i, int slot)
{
if(this->inventory[slot].isNull()) {
this->inventory[slot].item = i;
this->inventory[slot].qty = 1;
}
else {
this->inventory[slot].qty++;
}
}
bool Game::collectBrokenBlock(WorldCoord pos)
{
Block block = this->world->cellAt(pos.x, pos.y);
BlockInfo *info = this->world->blockInfoAt(pos);
if(!info)
return false;
bool suitable = this->isBreakingBlockWithSuitableTool(false);
this->world->trySetCellAt(pos.x, pos.y, BlockID(AIR));
if(!suitable)
return false;
/* Make an item to be picked up */
Item item;
item.isBlock = false;
if(block == BlockID(COAL_ORE)) {
item.itemID = ItemID::COAL;
}
else if(block == BlockID(IRON_ORE)) {
item.itemID = ItemID::IRON_INGOT;
}
else if(block == BlockID(GOLD_ORE)) {
item.itemID = ItemID::GOLD_INGOT;
}
else if(block == BlockID(DIAMOND_ORE)) {
item.itemID = ItemID::DIAMOND;
}
else if(block == BlockID(STONE)) {
item.isBlock = true;
item.block = BlockID(COBBLESTONE);
}
else {
item.isBlock = true;
item.block = block;
}
int slot = this->findInventorySlotForItem(item);
if(slot >= 0)
this->addItemInSlot(item, slot);
return true;
}
void Game::updateBlockBreaking()
{
if(this->damageTicks < 0)
return;
this->damageTicks++;
int requiredTicks = this->blockBreakingDuration();
if(this->damageTicks >= requiredTicks) {
this->collectBrokenBlock(this->cursorPos);
this->damageTicks = -1;
}
}