205 lines
5.0 KiB
C++
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;
|
|
}
|
|
}
|