#include "FireBasedProcessingBlockComponent.h"
#include "Block.h"
#include "ItemFilter.h"
#include "Recipie.h"
#include "RecipieList.h"
#include "RecipieLoader.h"
FireBasedProcessingBlockComponent::FireBasedProcessingBlockComponent()
: BlockComponent(),
burnLight(0),
ticksNeeded(0),
currentRecipie(0),
maxFuelBuffer(0),
fuelBuffer(0),
burning(0)
{}
FireBasedProcessingBlockComponent::~FireBasedProcessingBlockComponent() {}
bool FireBasedProcessingBlockComponent::findRecipie()
{
bool changed = 0;
RecipieList* zRecipies
= Game::INSTANCE->zRecipies()->zRecipieList(recipieGroup.getText());
if (zRecipies)
{
zBlock->lock();
Recipie* recipie = zRecipies->zFirstRecipie(this);
if (recipie)
{
recipie->consumeInputs(this);
currentRecipie = recipie;
ticksNeeded = recipie->getTicksNeeded();
changed = 1;
}
zBlock->unlock();
}
return changed;
}
bool FireBasedProcessingBlockComponent::consumeFuel()
{
bool changed = 0;
zBlock->lock();
ItemSlot* fireStartingSlot = 0;
ItemSlot* fuelSlot = 0;
for (ItemSlot* slot : *zBlock)
{
if (slot->zStack())
{
if (!burning && !fireStartingSlot
&& slot->getName().istGleich(fireStartingInventorySlotName)
&& fireStartingItemFilter->matchItem(slot->zStack()->zItem()))
{
fireStartingSlot = slot;
}
if (!fuelSlot && slot->getName().istGleich(fuelInventorySlotName)
&& fuelItemFilter->matchItem(slot->zStack()->zItem()))
{
fuelSlot = slot;
}
if (fuelSlot && fireStartingSlot)
{
break;
}
}
}
if (burning)
{
if (fuelSlot)
{
ItemStack* fuelStack
= zBlock->takeItemsOut(fuelSlot, 1, Direction::NO_DIRECTION);
if (fuelStack)
{
// TODO: check if item is burnable and how much fuel it provides
fuelBuffer += 1000;
maxFuelBuffer = fuelBuffer;
fuelStack->release();
changed = 1;
}
else
{
if (fuelBuffer == 0)
{
burning = false;
}
}
}
else
{
if (fuelBuffer == 0)
{
burning = false;
}
}
}
else
{
if (fuelSlot && fireStartingSlot)
{
ItemStack* fuelStack
= zBlock->takeItemsOut(fuelSlot, 1, Direction::NO_DIRECTION);
ItemStack* fireStartingStack = zBlock->takeItemsOut(
fireStartingSlot, 1, Direction::NO_DIRECTION);
if (fuelStack && fireStartingStack)
{
// TODO: check if item is burnable and how much fuel it provides
fuelBuffer += 1000;
maxFuelBuffer = fuelBuffer;
burning = true;
fuelStack->release();
fireStartingStack->release();
changed = 1;
}
}
}
zBlock->unlock();
return changed;
}
void FireBasedProcessingBlockComponent::createProgressMessage(
NetworkMessage* message) const
{
char* msg = new char[8];
*(int*)msg = currentRecipie ? currentRecipie->getTicksNeeded() : 0;
*(int*)(msg + 4)
= currentRecipie ? currentRecipie->getTicksNeeded() - ticksNeeded : 0;
message->setMessage(msg, 8);
}
void FireBasedProcessingBlockComponent::createFuelMessage(
NetworkMessage* message) const
{
char* msg = new char[8];
*(int*)msg = maxFuelBuffer;
*(int*)(msg + 4) = fuelBuffer;
message->setMessage(msg, 8);
}
void FireBasedProcessingBlockComponent::initialize(Block* zBlock)
{
this->zBlock = zBlock;
}
bool FireBasedProcessingBlockComponent::tick(int numTicks)
{
bool active = 0;
bool progressChanged = 0;
bool fuelChanged = 0;
while (numTicks > 0)
{
if (!fuelBuffer)
{
fuelChanged |= consumeFuel();
}
if (!burning)
{
break;
}
if (!currentRecipie)
{
progressChanged |= findRecipie();
}
bool processed = false;
if (currentRecipie)
{
int possibleTicks
= fuelBuffer / currentRecipie->getFuelPerTickNeeded();
if (!possibleTicks)
{
fuelChanged |= consumeFuel();
possibleTicks
= fuelBuffer / currentRecipie->getFuelPerTickNeeded();
}
if (possibleTicks >= numTicks)
{
if (numTicks >= ticksNeeded)
{
numTicks -= ticksNeeded;
fuelBuffer
-= currentRecipie->getFuelPerTickNeeded() * ticksNeeded;
zBlock->lock();
currentRecipie->produceOutputs(this);
zBlock->unlock();
ticksNeeded = 0;
currentRecipie = 0;
}
else
{
ticksNeeded -= numTicks;
fuelBuffer
-= currentRecipie->getFuelPerTickNeeded() * numTicks;
numTicks = 0;
}
progressChanged = 1;
fuelChanged = 1;
processed = true;
}
else
{
if (possibleTicks >= ticksNeeded)
{
numTicks -= ticksNeeded;
fuelBuffer
-= currentRecipie->getFuelPerTickNeeded() * ticksNeeded;
zBlock->lock();
currentRecipie->produceOutputs(this);
zBlock->unlock();
ticksNeeded = 0;
currentRecipie = 0;
processed = true;
fuelChanged = 1;
progressChanged = 1;
}
else
{
numTicks -= possibleTicks;
fuelBuffer -= currentRecipie->getFuelPerTickNeeded()
* possibleTicks;
ticksNeeded -= possibleTicks;
processed = possibleTicks > 0;
progressChanged = possibleTicks > 0;
fuelChanged = possibleTicks > 0;
}
}
}
if (!processed)
{
// burning without recipie
if (fuelBuffer >= numTicks)
{
fuelBuffer -= numTicks;
numTicks = 0;
fuelChanged = 1;
}
else
{
fuelChanged = fuelBuffer > 0;
numTicks -= fuelBuffer;
fuelBuffer = 0;
}
}
active = 1;
}
if (fuelChanged)
{
NetworkMessage* fuelMessage = new NetworkMessage();
createFuelMessage(fuelMessage);
fuelObservable.notifyObservers(fuelMessage);
}
if (progressChanged)
{
NetworkMessage* progressMessage = new NetworkMessage();
createProgressMessage(progressMessage);
progressObservable.notifyObservers(progressMessage);
}
return active;
}
void FireBasedProcessingBlockComponent::api(Framework::StreamReader* zRequest,
NetworkMessage* zResponse,
Entity* zSource)
{
char type;
zRequest->lese(&type, 1);
switch (type)
{
case 0: // subscribe to fuel
{
char idLen;
zRequest->lese(&idLen, 1);
char* id = new char[idLen + 1];
zRequest->lese(id, idLen);
id[(int)idLen] = 0;
int processor;
zRequest->lese((char*)&processor, 4);
fuelObservable.addObserver(zSource, id, processor);
createFuelMessage(zResponse);
zResponse->addressUIElement(id, processor);
break;
}
case 1: // subscribe to progress
{
char idLen;
zRequest->lese(&idLen, 1);
char* id = new char[idLen + 1];
zRequest->lese(id, idLen);
id[(int)idLen] = 0;
int processor;
zRequest->lese((char*)&processor, 4);
progressObservable.addObserver(zSource, id, processor);
createProgressMessage(zResponse);
zResponse->addressUIElement(id, processor);
break;
}
case 2: // unsubscribe to fuel
{
char idLen;
zRequest->lese(&idLen, 1);
char* id = new char[idLen + 1];
zRequest->lese(id, idLen);
id[(int)idLen] = 0;
int processor;
zRequest->lese((char*)&processor, 4);
fuelObservable.removeObserver(zSource, id, processor);
break;
}
case 3: // unsubscribe to progress
{
char idLen;
zRequest->lese(&idLen, 1);
char* id = new char[idLen + 1];
zRequest->lese(id, idLen);
id[(int)idLen] = 0;
int processor;
zRequest->lese((char*)&processor, 4);
progressObservable.removeObserver(zSource, id, processor);
break;
}
}
}
Framework::XML::Element*
FireBasedProcessingBlockComponent::getTooltipUIML() const
{
if (currentRecipie)
{
Framework::XML::Element* result = new Framework::XML::Element(
"");
Framework::Text content;
content.append() << "Processing: ";
int first = 1;
for (const ItemInfo& output : currentRecipie->getOutput())
{
if (!first)
{
content.append() << ", ";
}
content.append()
<< output.count << " "
<< Game::INSTANCE->zItemType(output.type)->getTooltipUIML();
first = 0;
}
content.append()
<< " ("
<< (int)((ticksNeeded / currentRecipie->getTicksNeeded()) * 100)
<< "%)";
result->setText(content);
}
return 0;
}
bool FireBasedProcessingBlockComponent::isAllAvailable(
Framework::RCArray& inputs)
{
return zBlock->isAllAvailable(inputs, inputInventorySlotName);
}
bool FireBasedProcessingBlockComponent::hasFreeSpace(
const Item* zItem, int amount)
{
int addable = zBlock->numberOfAddableItems(
zItem, NO_DIRECTION, outputInventorySlotName);
return addable >= amount;
}
void FireBasedProcessingBlockComponent::consume(
Framework::RCArray& inputs)
{
zBlock->consume(inputs, inputInventorySlotName);
}
void FireBasedProcessingBlockComponent::addCraftingResult(ItemStack* zStack)
{
TargetSlotNameItemFilter filter(outputInventorySlotName);
zBlock->unsaveAddItem(zStack, NO_DIRECTION, &filter);
}
Framework::Vec3
FireBasedProcessingBlockComponent::getStorageLocation() const
{
return zBlock->getLocation();
}
int FireBasedProcessingBlockComponent::getStorageDimensionId() const
{
return zBlock->getDimensionId();
}
void FireBasedProcessingBlockComponent::loadComponent(
Framework::StreamReader* zReader)
{
zReader->lese((char*)&ticksNeeded, 4);
zReader->lese((char*)&maxFuelBuffer, 4);
zReader->lese((char*)&fuelBuffer, 4);
zReader->lese((char*)&burning, 1);
int index = 0;
zReader->lese((char*)&index, 4);
if (index >= 0)
{
// TODO: add unique recipie ids to enshure correct loading after
// recipies were changed, added or removed
RecipieList* recipies
= Game::INSTANCE->zRecipies()->zRecipieList(recipieGroup);
if (recipies && index < recipies->getRecipieCount())
{
currentRecipie = recipies->zRecipie(index);
}
}
}
void FireBasedProcessingBlockComponent::saveComponent(
Framework::StreamWriter* zWriter) const
{
zWriter->schreibe((char*)&ticksNeeded, 4);
zWriter->schreibe((char*)&maxFuelBuffer, 4);
zWriter->schreibe((char*)&fuelBuffer, 4);
zWriter->schreibe((char*)&burning, 1);
int index = -1;
if (currentRecipie)
{
// TODO: add unique recipie ids to enshure correct loading after
// recipies were changed, added or removed
RecipieList* recipies
= Game::INSTANCE->zRecipies()->zRecipieList(recipieGroup);
if (recipies)
{
index = recipies->getRecipieIndex(currentRecipie);
}
}
zWriter->schreibe((char*)&index, 4);
}
bool FireBasedProcessingBlockComponent::isLightSource() const
{
return 1;
}
int FireBasedProcessingBlockComponent::getLightColor() const
{
return burning ? burnLight : 0;
}
FireBasedProcessingBlockComponentFactory::
FireBasedProcessingBlockComponentFactory()
: SubTypeFactory()
{}
FireBasedProcessingBlockComponent*
FireBasedProcessingBlockComponentFactory::fromJson(
Framework::JSON::JSONObject* zJson) const
{
FireBasedProcessingBlockComponent* component
= new FireBasedProcessingBlockComponent();
if (zJson->hasValue("fireStartingItemFilter"))
{
component->fireStartingItemFilter
= Game::INSTANCE->zTypeRegistry()->fromJson(
zJson->zValue("fireStartingItemFilter"));
}
else
{
component->fireStartingItemFilter = new AnyItemFilter();
}
if (zJson->hasValue("fuelItemFilter"))
{
component->fuelItemFilter
= Game::INSTANCE->zTypeRegistry()->fromJson(
zJson->zValue("fuelItemFilter"));
}
else
{
component->fuelItemFilter = new AnyItemFilter();
}
component->recipieGroup
= zJson->zValue("recipieGroup")->asString()->getString();
component->fuelInventorySlotName
= zJson->zValue("fuelInventorySlotName")->asString()->getString();
component->fireStartingInventorySlotName
= zJson->zValue("fireStartingInventorySlotName")
->asString()
->getString();
component->inputInventorySlotName
= zJson->zValue("inputInventorySlotName")->asString()->getString();
component->outputInventorySlotName
= zJson->zValue("outputInventorySlotName")->asString()->getString();
component->burnLight
= (int)zJson->zValue("lightColor")->asString()->getString();
return component;
}
Framework::JSON::JSONObject*
FireBasedProcessingBlockComponentFactory::toJsonObject(
FireBasedProcessingBlockComponent* zObject) const
{
Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
result->addValue("fireStartingItemFilter",
Game::INSTANCE->zTypeRegistry()->toJson(
zObject->fireStartingItemFilter));
result->addValue("fuelItemFilter",
Game::INSTANCE->zTypeRegistry()->toJson(zObject->fuelItemFilter));
result->addValue("recipieGroup",
new Framework::JSON::JSONString(zObject->recipieGroup.getText()));
result->addValue("fuelInventorySlotName",
new Framework::JSON::JSONString(
zObject->fuelInventorySlotName.getText()));
result->addValue("fireStartingInventorySlotName",
new Framework::JSON::JSONString(
zObject->fireStartingInventorySlotName.getText()));
result->addValue("inputInventorySlotName",
new Framework::JSON::JSONString(
zObject->inputInventorySlotName.getText()));
result->addValue("outputInventorySlotName",
new Framework::JSON::JSONString(
zObject->outputInventorySlotName.getText()));
Framework::Text color = "0x";
color.appendHex(zObject->burnLight);
result->addValue("lightColor", new Framework::JSON::JSONString(color));
return result;
}
JSONObjectValidationBuilder*
FireBasedProcessingBlockComponentFactory::addToValidator(
JSONObjectValidationBuilder* builder) const
{
return builder
->withRequiredAttribute("fireStartingItemFilter",
Game::INSTANCE->zTypeRegistry()->getValidator())
->withRequiredAttribute("fuelItemFilter",
Game::INSTANCE->zTypeRegistry()->getValidator())
->withRequiredString("recipieGroup")
->finishString()
->withRequiredString("fuelInventorySlotName")
->finishString()
->withRequiredString("fireStartingInventorySlotName")
->finishString()
->withRequiredString("inputInventorySlotName")
->finishString()
->withRequiredString("outputInventorySlotName")
->finishString()
->withRequiredString("lightColor")
->whichStartsWithMatch("0x")
->withDefault("0x0")
->finishString();
return builder;
}
const char* FireBasedProcessingBlockComponentFactory::getTypeToken() const
{
return "fireBasedProcessing";
}