#include "BasicTool.h"

#include <numeric>

#include "Block.h"
#include "Dimension.h"
#include "Entity.h"
#include "Game.h"

#undef max

XPBasedLevelUpRule::XPBasedLevelUpRule()
    : ItemSkillLevelUpRule(),
      xpIncrease(0.0),
      xpMultiplier(2.0),
      levelIncrease(1.0),
      levelMultiplier(1.0),
      maxLevel(std::numeric_limits<float>::max())
{}

void XPBasedLevelUpRule::applyOn(ItemSkill* zSkill)
{
    if (zSkill->getXp() >= zSkill->getMaxXp())
    {
        zSkill->setXp(zSkill->getXp() - zSkill->getMaxXp());
        zSkill->setLevel(MIN(
            (zSkill->getLevel() + levelIncrease) * levelMultiplier, maxLevel));
        zSkill->setMaxXp((zSkill->getMaxXp() + xpIncrease) * xpMultiplier);
    }
}

void XPBasedLevelUpRule::setXpIncrease(float xpIncrease)
{
    this->xpIncrease = xpIncrease;
}

float XPBasedLevelUpRule::getXpIncrease() const
{
    return xpIncrease;
}

void XPBasedLevelUpRule::setXpMultiplier(float xpMultiplier)
{
    this->xpMultiplier = xpMultiplier;
}

float XPBasedLevelUpRule::getXpMultiplier() const
{
    return xpMultiplier;
}

void XPBasedLevelUpRule::setLevelIncrease(float levelIncrease)
{
    this->levelIncrease = levelIncrease;
}

float XPBasedLevelUpRule::getLevelIncrease() const
{
    return levelIncrease;
}

void XPBasedLevelUpRule::setLevelMultiplier(float levelMultiplier)
{
    this->levelMultiplier = levelMultiplier;
}

float XPBasedLevelUpRule::getLevelMultiplier() const
{
    return levelMultiplier;
}

void XPBasedLevelUpRule::setMaxLevel(float maxLevel)
{
    this->maxLevel = maxLevel;
}

float XPBasedLevelUpRule::getMaxLevel() const
{
    return maxLevel;
}

XPBasedLevelUpRuleFactory::XPBasedLevelUpRuleFactory()
    : SubTypeFactory()
{}

XPBasedLevelUpRule* XPBasedLevelUpRuleFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    XPBasedLevelUpRule* result = new XPBasedLevelUpRule();
    if (zJson->hasValue("maxLevel"))
    {
        result->setMaxLevel(
            (float)zJson->zValue("maxLevel")->asNumber()->getNumber());
    }
    result->setXpIncrease(
        (float)zJson->zValue("xpIncrease")->asNumber()->getNumber());
    result->setXpMultiplier(
        (float)zJson->zValue("xpMultiplier")->asNumber()->getNumber());
    result->setLevelIncrease(
        (float)zJson->zValue("levelIncrease")->asNumber()->getNumber());
    result->setLevelMultiplier(
        (float)zJson->zValue("levelMultiplier")->asNumber()->getNumber());
    return result;
}

Framework::JSON::JSONObject* XPBasedLevelUpRuleFactory::toJsonObject(
    XPBasedLevelUpRule* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    result->addValue("xpIncrease",
        new Framework::JSON::JSONNumber(zObject->getXpIncrease()));
    result->addValue("xpMultiplier",
        new Framework::JSON::JSONNumber(zObject->getXpMultiplier()));
    result->addValue("levelIncrease",
        new Framework::JSON::JSONNumber(zObject->getLevelIncrease()));
    result->addValue("levelMultiplier",
        new Framework::JSON::JSONNumber(zObject->getLevelMultiplier()));
    result->addValue(
        "maxLevel", new Framework::JSON::JSONNumber(zObject->getMaxLevel()));
    return result;
}

JSONObjectValidationBuilder* XPBasedLevelUpRuleFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder->withRequiredNumber("xpIncrease")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.0)
        ->finishNumber()
        ->withRequiredNumber("xpMultiplier")
        ->whichIsGreaterThen(0.0)
        ->withDefault(2.0)
        ->finishNumber()
        ->withRequiredNumber("levelIncrease")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(1.0)
        ->finishNumber()
        ->withRequiredNumber("levelMultiplier")
        ->whichIsGreaterThen(0.0)
        ->withDefault(1.0)
        ->finishNumber()
        ->withRequiredNumber("maxLevel")
        ->whichIsOptional()
        ->finishNumber();
}

const char* XPBasedLevelUpRuleFactory::getTypeToken() const
{
    return "xpBased";
}

BasicToolItem::BasicToolItem(
    int itemTypeId, Framework::Text name, float maxHp, float maxDurability)
    : Item(itemTypeId, name),
      headMaterialHardness(0.0),
      rodMaterialHardness(0.0),
      handleMaterialHardness(0.0)
{
    hp = maxHp;
    this->maxHp = maxHp;
    durability = maxDurability;
    this->maxDurability = maxDurability;
    eatable = 0;
    placeable = 0;
    equippable = 0;
    solid = 1;
    usable = 1;
}

void BasicToolItem::setHeadMaterialHardness(float hardness)
{
    headMaterialHardness = hardness;
}

void BasicToolItem::setRodMaterialHardness(float hardness)
{
    rodMaterialHardness = hardness;
}

void BasicToolItem::setHandleMaterialHardness(float hardness)
{
    handleMaterialHardness = hardness;
}

float BasicToolItem::getHeadMaterialHardness() const
{
    return headMaterialHardness;
}

float BasicToolItem::getRodMaterialHardness() const
{
    return rodMaterialHardness;
}

float BasicToolItem::getHandleMaterialHardness() const
{
    return handleMaterialHardness;
}

BasicToolItemType::BasicToolItemType()
    : ItemType(),
      headMaterialHardness(1.f),
      rodMaterialHardness(1.f),
      handleMaterialHardness(1.f),
      baseDurability(10.f),
      baseDurabilityMultiplier(1.f),
      headMaterialDurability(10.f),
      headMaterialDurabilityMultiplier(0.f),
      rodMaterialDurability(10.f),
      rodMaterialDurabilityMultiplier(0.f),
      handleMaterialDurability(10.f),
      handleMaterialDurabilityMultiplier(0.0),
      levelUpRule(0),
      brokenItemTypeName(),
      itemSkillConfigJson(0),
      brokenItemTypeId(-1)
{}

BasicToolItemType::~BasicToolItemType()
{
    if (levelUpRule) levelUpRule->release();
    if (itemSkillConfigJson) itemSkillConfigJson->release();
}

void BasicToolItemType::loadSuperItem(
    Item* zItem, Framework::StreamReader* zReader) const
{
    BasicToolItem* item = dynamic_cast<BasicToolItem*>(zItem);
    float data;
    zReader->lese((char*)&data, 4);
    item->setHeadMaterialHardness(data);
    zReader->lese((char*)&data, 4);
    item->setRodMaterialHardness(data);
    zReader->lese((char*)&data, 4);
    item->setHandleMaterialHardness(data);
    ItemType::loadSuperItem(item, zReader);
}

void BasicToolItemType::saveSuperItem(
    const Item* zItem, Framework::StreamWriter* zWriter) const
{
    const BasicToolItem* item = dynamic_cast<const BasicToolItem*>(zItem);
    float data = item->getHeadMaterialHardness();
    zWriter->schreibe((char*)&data, 4);
    data = item->getRodMaterialHardness();
    zWriter->schreibe((char*)&data, 4);
    data = item->getHandleMaterialHardness();
    zWriter->schreibe((char*)&data, 4);
    ItemType::saveSuperItem(item, zWriter);
}

bool BasicToolItemType::initialize(Game* zGame)
{
    brokenItemTypeId = zGame->getItemTypeId(brokenItemTypeName);
    return brokenItemTypeId >= 0 && ItemType::initialize(zGame);
}

const ItemType* BasicToolItemType::zBrokenItemType() const
{
    return Game::INSTANCE->zItemType(brokenItemTypeId);
}

Item* BasicToolItemType::createItem() const
{
    BasicToolItem* item = new BasicToolItem(getId(),
        getName(),
        1.f,
        (baseDurability + headMaterialDurability * headMaterialHardness
            + rodMaterialDurability * rodMaterialHardness
            + handleMaterialDurability * handleMaterialHardness)
            * (baseDurabilityMultiplier
                + headMaterialDurabilityMultiplier * headMaterialHardness
                + rodMaterialDurabilityMultiplier * rodMaterialHardness
                + handleMaterialDurabilityMultiplier * handleMaterialHardness));
    item->setHandleMaterialHardness(headMaterialHardness);
    item->setRodMaterialHardness(rodMaterialHardness);
    item->setHandleMaterialHardness(handleMaterialHardness);
    return item;
}

void BasicToolItemType::levelUpItemSkill(ItemSkill* zSkill) const
{
    levelUpRule->applyOn(zSkill);
}

void BasicToolItemType::setItemAttribute(
    Item* zItem, Framework::Text name, Framework::JSON::JSONValue* zValue) const
{
    BasicToolItem* item = dynamic_cast<BasicToolItem*>(zItem);
    if (name.istGleich("headMaterialHardness")
        && zValue->getType() == Framework::AbstractType::NUMBER)
    {
        item->setHeadMaterialHardness((float)zValue->asNumber()->getNumber());
    }
    else if (name.istGleich("rodMaterialHardness")
             && zValue->getType() == Framework::AbstractType::NUMBER)
    {
        item->setRodMaterialHardness((float)zValue->asNumber()->getNumber());
    }
    else if (name.istGleich("handleMaterialHardness")
             && zValue->getType() == Framework::AbstractType::NUMBER)
    {
        item->setHandleMaterialHardness((float)zValue->asNumber()->getNumber());
    }
    else
    {
        ItemType::setItemAttribute(item, name, zValue);
    }
    item->setMaxDurability(
        (baseDurability
            + headMaterialDurability * item->getHandleMaterialHardness()
            + rodMaterialDurability * item->getRodMaterialHardness()
            + handleMaterialDurability * item->getHandleMaterialHardness())
        * (baseDurabilityMultiplier
            + headMaterialDurabilityMultiplier * item->getHeadMaterialHardness()
            + rodMaterialDurabilityMultiplier * item->getRodMaterialHardness()
            + handleMaterialDurabilityMultiplier
                  * item->getHandleMaterialHardness()));
}

void BasicToolItemType::addItemAttributes(
    Item* zItem, Framework::JSON::JSONObject* zItemObjet) const
{
    BasicToolItem* item = dynamic_cast<BasicToolItem*>(zItem);
    zItemObjet->addValue("headMaterialHardness",
        new Framework::JSON::JSONNumber(item->getHeadMaterialHardness()));
    zItemObjet->addValue("rodMaterialHardness",
        new Framework::JSON::JSONNumber(item->getRodMaterialHardness()));
    zItemObjet->addValue("handleMaterialHardness",
        new Framework::JSON::JSONNumber(item->getHandleMaterialHardness()));
    ItemType::addItemAttributes(item, zItemObjet);
}

ItemSkill* BasicToolItemType::createDefaultItemSkill() const
{
    return Game::INSTANCE->zTypeRegistry()->fromJson<ItemSkill>(
        itemSkillConfigJson);
}

void BasicToolItemType::setBrokenItemTypeName(
    Framework::Text brokenItemTypeName)
{
    this->brokenItemTypeName = brokenItemTypeName;
}

Framework::Text BasicToolItemType::getBrokenItemTypeName() const
{
    return brokenItemTypeName;
}

void BasicToolItemType::setHeadMaterialHardness(float hardness)
{
    headMaterialHardness = hardness;
}

float BasicToolItemType::getHeadMaterialHardness() const
{
    return headMaterialHardness;
}

void BasicToolItemType::setRodMaterialHardness(float hardness)
{
    rodMaterialHardness = hardness;
}

float BasicToolItemType::getRodMaterialHardness() const
{
    return rodMaterialHardness;
}

void BasicToolItemType::setHandleMaterialHardness(float hardness)
{
    handleMaterialHardness = hardness;
}

float BasicToolItemType::getHandleMaterialHardness() const
{
    return handleMaterialHardness;
}

void BasicToolItemType::setBaseDurability(float durability)
{
    baseDurability = durability;
}

float BasicToolItemType::getBaseDurablility() const
{
    return baseDurability;
}

void BasicToolItemType::setBaseDurabilityMultiplier(float multiplier)
{
    baseDurabilityMultiplier = multiplier;
}

float BasicToolItemType::getBaseDurabilityMultiplier() const
{
    return baseDurabilityMultiplier;
}

void BasicToolItemType::setHeadMaterialDurability(float durability)
{
    headMaterialDurability = durability;
}

float BasicToolItemType::getHeadMaterialDurability() const
{
    return headMaterialDurability;
}

void BasicToolItemType::setHeadMaterialDurabilityMultiplier(float multiplier)
{
    headMaterialDurabilityMultiplier = multiplier;
}

float BasicToolItemType::getHeadMaterialDurabilityMultiplier() const
{
    return headMaterialDurabilityMultiplier;
}

void BasicToolItemType::setRodMaterialDurability(float durability)
{
    rodMaterialDurability = durability;
}

float BasicToolItemType::getRodMaterialDurability() const
{
    return rodMaterialDurability;
}

void BasicToolItemType::setRodMaterialDurabilityMultiplier(float multiplier)
{
    rodMaterialDurabilityMultiplier = multiplier;
}

float BasicToolItemType::getRodMaterialDurabilityMultiplier() const
{
    return rodMaterialDurabilityMultiplier;
}

void BasicToolItemType::setHandleMaterialDurability(float durability)
{
    handleMaterialDurability = durability;
}

float BasicToolItemType::getHandleMaterialDurability() const
{
    return handleMaterialDurability;
}

void BasicToolItemType::setHandleMaterialDurabilityMultiplier(float multiplier)
{
    handleMaterialDurabilityMultiplier = multiplier;
}

float BasicToolItemType::getHandleMaterialDurabilityMultiplier() const
{
    return handleMaterialDurabilityMultiplier;
}

void BasicToolItemType::setLevelUpRule(ItemSkillLevelUpRule* rule)
{
    if (levelUpRule) levelUpRule->release();
    levelUpRule = rule;
}

ItemSkillLevelUpRule* BasicToolItemType::zLevelUpRule() const
{
    return levelUpRule;
}

void BasicToolItemType::setItemSkillConfigJson(
    Framework::JSON::JSONObject* zJson)
{
    if (itemSkillConfigJson) itemSkillConfigJson->release();
    itemSkillConfigJson = zJson;
}

Framework::JSON::JSONObject* BasicToolItemType::getItemSkillConfigJson() const
{
    return dynamic_cast<Framework::JSON::JSONObject*>(
        itemSkillConfigJson->getThis());
}

BasicToolItemTypeFactory::BasicToolItemTypeFactory()
    : ItemTypeFactoryBase()
{}

BasicToolItemType* BasicToolItemTypeFactory::createValue(
    Framework::JSON::JSONObject* zJson) const
{
    return new BasicToolItemType();
}

BasicToolItemType* BasicToolItemTypeFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BasicToolItemType* result = ItemTypeFactoryBase::fromJson(zJson);
    result->setHandleMaterialHardness(
        (float)zJson->zValue("headMaterialHardness")->asNumber()->getNumber());
    result->setRodMaterialHardness(
        (float)zJson->zValue("rodMaterialHardness")->asNumber()->getNumber());
    result->setHandleMaterialHardness(
        (float)zJson->zValue("handleMaterialHardness")
            ->asNumber()
            ->getNumber());
    result->setBaseDurability(
        (float)zJson->zValue("baseDurability")->asNumber()->getNumber());
    result->setBaseDurabilityMultiplier(
        (float)zJson->zValue("baseDurabilityMultiplier")
            ->asNumber()
            ->getNumber());
    result->setHeadMaterialDurability(
        (float)zJson->zValue("headMaterialDurability")
            ->asNumber()
            ->getNumber());
    result->setHeadMaterialDurabilityMultiplier(
        (float)zJson->zValue("headMaterialDurabilityMultiplier")
            ->asNumber()
            ->getNumber());
    result->setRodMaterialDurability(
        (float)zJson->zValue("rodMaterialDurability")->asNumber()->getNumber());
    result->setRodMaterialDurabilityMultiplier(
        (float)zJson->zValue("rodMaterialDurabilityMultiplier")
            ->asNumber()
            ->getNumber());
    result->setHandleMaterialDurability(
        (float)zJson->zValue("handleMaterialDurability")
            ->asNumber()
            ->getNumber());
    result->setHandleMaterialDurabilityMultiplier(
        (float)zJson->zValue("handleMaterialDurabilityMultiplier")
            ->asNumber()
            ->getNumber());
    result->setLevelUpRule(
        Game::INSTANCE->zTypeRegistry()->fromJson<ItemSkillLevelUpRule>(
            zJson->zValue("levelUpRule")));
    result->setBrokenItemTypeName(
        zJson->zValue("brokenItemTypeName")->asString()->getString());
    result->setItemSkillConfigJson(zJson->getValue("itemSkill")->asObject());
    return result;
}

Framework::JSON::JSONObject* BasicToolItemTypeFactory::toJsonObject(
    BasicToolItemType* zObject) const
{
    Framework::JSON::JSONObject* result
        = ItemTypeFactoryBase::toJsonObject(zObject);
    result->addValue("headMaterialHardness",
        new Framework::JSON::JSONNumber(zObject->getHeadMaterialHardness()));
    result->addValue("rodMaterialHardness",
        new Framework::JSON::JSONNumber(zObject->getRodMaterialHardness()));
    result->addValue("handleMaterialHardness",
        new Framework::JSON::JSONNumber(zObject->getHandleMaterialHardness()));
    result->addValue("baseDurability",
        new Framework::JSON::JSONNumber(zObject->getBaseDurablility()));
    result->addValue("baseDurabilityMultiplier",
        new Framework::JSON::JSONNumber(
            zObject->getBaseDurabilityMultiplier()));
    result->addValue("headMaterialDurability",
        new Framework::JSON::JSONNumber(zObject->getHeadMaterialDurability()));
    result->addValue("headMaterialDurabilityMultiplier",
        new Framework::JSON::JSONNumber(
            zObject->getHeadMaterialDurabilityMultiplier()));
    result->addValue("rodMaterialDurability",
        new Framework::JSON::JSONNumber(zObject->getRodMaterialDurability()));
    result->addValue("rodMaterialDurabilityMultiplier",
        new Framework::JSON::JSONNumber(
            zObject->getRodMaterialDurabilityMultiplier()));
    result->addValue("handleMaterialDurability",
        new Framework::JSON::JSONNumber(
            zObject->getHandleMaterialDurability()));
    result->addValue("handleMaterialDurabilityMultiplier",
        new Framework::JSON::JSONNumber(
            zObject->getHandleMaterialDurabilityMultiplier()));
    result->addValue("levelUpRule",
        Game::INSTANCE->zTypeRegistry()->toJson(zObject->zLevelUpRule()));
    result->addValue("brokenItemTypeName",
        new Framework::JSON::JSONString(zObject->zBrokenItemType()->getName()));
    result->addValue("itemSkill", zObject->getItemSkillConfigJson());
    return result;
}

JSONObjectValidationBuilder* BasicToolItemTypeFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return ItemTypeFactoryBase::addToValidator(
        builder->withRequiredNumber("headMaterialHardness")
            ->withDefault(1.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredNumber("rodMaterialHardness")
            ->withDefault(1.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredNumber("handleMaterialHardness")
            ->withDefault(1.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredNumber("baseDurability")
            ->withDefault(10.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredNumber("baseDurabilityMultiplier")
            ->withDefault(1.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredNumber("headMaterialDurability")
            ->withDefault(10.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredNumber("headMaterialDurabilityMultiplier")
            ->withDefault(0.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredNumber("rodMaterialDurability")
            ->withDefault(10.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredNumber("rodMaterialDurabilityMultiplier")
            ->withDefault(0.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredNumber("handleMaterialDurability")
            ->withDefault(10.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredNumber("handleMaterialDurabilityMultiplier")
            ->withDefault(0.0)
            ->whichIsGreaterOrEqual(0.0)
            ->finishNumber()
            ->withRequiredAttribute("brokenItemTypeName",
                Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
                    ItemTypeNameFactory::TYPE_ID))
            ->withRequiredAttribute("levelUpRule",
                Game::INSTANCE->zTypeRegistry()
                    ->getValidator<ItemSkillLevelUpRule>())
            ->withRequiredAttribute("itemSkill",
                Game::INSTANCE->zTypeRegistry()->getValidator<ItemSkill>()));
}

const char* BasicToolItemTypeFactory::getTypeToken() const
{
    return "tool";
}

BlockReplaceItemSkillConfig::BlockReplaceItemSkillConfig()
    : ReferenceCounter(),
      targetBlockFilter(0),
      replacementBlockTypeId(-1),
      cooldownTicks(20),
      staminaCost(0.5f),
      staminaCostDevider(0.8f),
      additionalStaminaCostDeviderPerLevel(0.2f),
      durabilityCost(0.5f),
      durabilityCostDevider(0.89f),
      additionalDurabilityCostDeviderPerLevel(0.02f),
      xpGain(0.5f)
{}

BlockReplaceItemSkillConfig::~BlockReplaceItemSkillConfig()
{
    if (targetBlockFilter) targetBlockFilter->release();
}

void BlockReplaceItemSkillConfig::setTargetBlockFilter(
    BlockFilter* targetBlockFilter)
{
    this->targetBlockFilter = targetBlockFilter;
}

BlockFilter* BlockReplaceItemSkillConfig::zTargetBlockFilter() const
{
    return targetBlockFilter;
}

void BlockReplaceItemSkillConfig::setReplacementBlockTypeId(
    int replacementBlockTypeId)
{
    this->replacementBlockTypeId = replacementBlockTypeId;
}

int BlockReplaceItemSkillConfig::getReplacementBlockTypeId() const
{
    return replacementBlockTypeId;
}

void BlockReplaceItemSkillConfig::setCooldownTicks(int cooldownTicks)
{
    this->cooldownTicks = cooldownTicks;
}

int BlockReplaceItemSkillConfig::getCooldownTicks() const
{
    return cooldownTicks;
}

void BlockReplaceItemSkillConfig::setStaminaCost(float staminaCost)
{
    this->staminaCost = staminaCost;
}

float BlockReplaceItemSkillConfig::getStaminaCost() const
{
    return staminaCost;
}

void BlockReplaceItemSkillConfig::setStaminaCostDevider(
    float staminaCostDevider)
{
    this->staminaCostDevider = staminaCostDevider;
}

float BlockReplaceItemSkillConfig::getStaminaCostDevider() const
{
    return staminaCostDevider;
}

void BlockReplaceItemSkillConfig::setAdditionalStaminaCostDeviderPerLevel(
    float additionalStaminaCostDeviderPerLevel)
{
    this->additionalStaminaCostDeviderPerLevel
        = additionalStaminaCostDeviderPerLevel;
}

float BlockReplaceItemSkillConfig::getAdditionalStaminaCostDeviderPerLevel()
    const
{
    return additionalStaminaCostDeviderPerLevel;
}

void BlockReplaceItemSkillConfig::setDurabilityCost(float durabilityCost)
{
    this->durabilityCost = durabilityCost;
}

float BlockReplaceItemSkillConfig::getDurabilityCost() const
{
    return durabilityCost;
}

void BlockReplaceItemSkillConfig::setDurabilityCostDevider(
    float durabilityCostDevider)
{
    this->durabilityCostDevider = durabilityCostDevider;
}

float BlockReplaceItemSkillConfig::getDurabilityCostDevider() const
{
    return durabilityCostDevider;
}

void BlockReplaceItemSkillConfig::setAdditionalDurabilityCostDeviderPerLevel(
    float additionalDurabilityCostDeviderPerLevel)
{
    this->additionalDurabilityCostDeviderPerLevel
        = additionalDurabilityCostDeviderPerLevel;
}

float BlockReplaceItemSkillConfig::getAdditionalDurabilityCostDeviderPerLevel()
    const
{
    return additionalDurabilityCostDeviderPerLevel;
}

void BlockReplaceItemSkillConfig::setXpGain(float xpGain)
{
    this->xpGain = xpGain;
}

float BlockReplaceItemSkillConfig::getXpGain() const
{
    return xpGain;
}

BlockReplaceItemSkillConfigFactory::BlockReplaceItemSkillConfigFactory()
    : ObjectTypeFactory()
{}

BlockReplaceItemSkillConfig* BlockReplaceItemSkillConfigFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BlockReplaceItemSkillConfig* result = new BlockReplaceItemSkillConfig();
    result->setTargetBlockFilter(
        Game::INSTANCE->zTypeRegistry()->fromJson<BlockFilter>(
            zJson->asObject()->zValue("targetFilter")));
    result->setCooldownTicks((int)zJson->asObject()
            ->zValue("cooldownTicks")
            ->asNumber()
            ->getNumber());
    result->setReplacementBlockTypeId(
        Game::INSTANCE->getBlockTypeId(zJson->asObject()
                ->zValue("replacementBlockType")
                ->asString()
                ->getString()));
    result->setCooldownTicks((int)zJson->asObject()
            ->zValue("cooldownTicks")
            ->asNumber()
            ->getNumber());
    result->setStaminaCost((float)zJson->asObject()
            ->zValue("staminaCost")
            ->asNumber()
            ->getNumber());
    result->setStaminaCostDevider((float)zJson->asObject()
            ->zValue("staminaCostDevider")
            ->asNumber()
            ->getNumber());
    result->setAdditionalStaminaCostDeviderPerLevel((float)zJson->asObject()
            ->zValue("additionalStaminaCostDeviderPerLevel")
            ->asNumber()
            ->getNumber());
    result->setDurabilityCost((float)zJson->asObject()
            ->zValue("durabilityCost")
            ->asNumber()
            ->getNumber());
    result->setDurabilityCostDevider((float)zJson->asObject()
            ->zValue("durabilityCostDevider")
            ->asNumber()
            ->getNumber());
    result->setAdditionalDurabilityCostDeviderPerLevel((float)zJson->asObject()
            ->zValue("additionalDurabilityCostDeviderPerLevel")
            ->asNumber()
            ->getNumber());
    result->setXpGain(
        (float)zJson->asObject()->zValue("xpGain")->asNumber()->getNumber());
    return result;
}

Framework::JSON::JSONObject* BlockReplaceItemSkillConfigFactory::toJsonObject(
    BlockReplaceItemSkillConfig* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    result->addValue("targetFilter",
        Game::INSTANCE->zTypeRegistry()->toJson(zObject->zTargetBlockFilter()));
    result->addValue("replacementBlockType",
        new Framework::JSON::JSONString(
            Game::INSTANCE->zBlockType(zObject->getReplacementBlockTypeId())
                ->getName()));
    result->addValue("cooldownTicks",
        new Framework::JSON::JSONNumber(zObject->getCooldownTicks()));
    result->addValue("staminaCost",
        new Framework::JSON::JSONNumber(zObject->getStaminaCost()));
    result->addValue("staminaCostDevider",
        new Framework::JSON::JSONNumber(zObject->getStaminaCostDevider()));
    result->addValue("additionalStaminaCostDeviderPerLevel",
        new Framework::JSON::JSONNumber(
            zObject->getAdditionalStaminaCostDeviderPerLevel()));
    result->addValue("durabilityCost",
        new Framework::JSON::JSONNumber(zObject->getDurabilityCost()));
    result->addValue("durabilityCostDevider",
        new Framework::JSON::JSONNumber(zObject->getDurabilityCostDevider()));
    result->addValue("additionalDurabilityCostDeviderPerLevel",
        new Framework::JSON::JSONNumber(
            zObject->getAdditionalDurabilityCostDeviderPerLevel()));
    result->addValue(
        "xpGain", new Framework::JSON::JSONNumber(zObject->getXpGain()));
    return result;
}

JSONObjectValidationBuilder* BlockReplaceItemSkillConfigFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder
        ->withRequiredAttribute("targetFilter",
            Game::INSTANCE->zTypeRegistry()->getValidator<BlockFilter>())
        ->withRequiredAttribute("replacementBlockType",
            Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
                BlockTypeNameFactory::TYPE_ID))
        ->withRequiredNumber("cooldownTicks")
        ->whichIsGreaterOrEqual(0)
        ->withDefault(20)
        ->finishNumber()
        ->withRequiredNumber("staminaCost")
        ->whichIsGreaterOrEqual(0)
        ->withDefault(0.5)
        ->finishNumber()
        ->withRequiredNumber("staminaCostDevider")
        ->whichIsGreaterOrEqual(0)
        ->withDefault(0.8)
        ->finishNumber()
        ->withRequiredNumber("additionalStaminaCostDeviderPerLevel")
        ->whichIsGreaterOrEqual(0)
        ->withDefault(0.2)
        ->finishNumber()
        ->withRequiredNumber("durabilityCost")
        ->whichIsGreaterOrEqual(0)
        ->withDefault(0.5)
        ->finishNumber()
        ->withRequiredNumber("durabilityCostDevider")
        ->whichIsGreaterOrEqual(0)
        ->withDefault(0.98)
        ->finishNumber()
        ->withRequiredNumber("additionalDurabilityCostDeviderPerLevel")
        ->whichIsGreaterOrEqual(0)
        ->withDefault(0.02)
        ->finishNumber()
        ->withRequiredNumber("xpGain")
        ->whichIsGreaterOrEqual(0)
        ->withDefault(0.5)
        ->finishNumber();
}

BlockReplaceItemSkill::BlockReplaceItemSkill()
    : ItemSkill(),
      invalidUseConfig(0),
      configs(),
      cooldownTicks(20)
{}

BlockReplaceItemSkill::~BlockReplaceItemSkill()
{
    if (invalidUseConfig) invalidUseConfig->release();
}

void BlockReplaceItemSkill::load(Framework::StreamReader* zReader)
{
    zReader->lese((char*)&cooldownTicks, 4);
    ItemSkill::load(zReader);
}

void BlockReplaceItemSkill::save(Framework::StreamWriter* zWriter)
{
    zWriter->schreibe((char*)&cooldownTicks, 4);
    ItemSkill::save(zWriter);
}

bool BlockReplaceItemSkill::use(Entity* zActor, Item* zUsedItem, Block* zTarget)
{
    if (cooldownTicks > 0)
    {
        cooldownTicks--;
        return false;
    }
    BlockReplaceItemSkillConfig* usedConfig = 0;
    for (BlockReplaceItemSkillConfig* config : configs)
    {
        if (config->zTargetBlockFilter()->test(zTarget))
        {
            usedConfig = config;
            break;
        }
    }
    bool invalid = 0;
    if (usedConfig == 0)
    {
        usedConfig = invalidUseConfig;
        invalid = 1;
    }
    float staminaDevider
        = usedConfig->getStaminaCostDevider()
        + usedConfig->getAdditionalStaminaCostDeviderPerLevel() * getLevel();
    float staminaCost = usedConfig->getStaminaCost();
    if (staminaDevider != 0)
    {
        staminaCost /= staminaDevider;
    }
    if (zActor->getStamina() < staminaCost)
    {
        return false;
    }
    float durabilityDevider
        = usedConfig->getDurabilityCostDevider()
        + usedConfig->getAdditionalDurabilityCostDeviderPerLevel() * getLevel();
    float durabilityCost = usedConfig->getDurabilityCost();
    if (durabilityDevider != 0)
    {
        durabilityCost /= durabilityDevider;
    }
    zUsedItem->setDurability(zUsedItem->getDurability() - durabilityCost);
    zActor->setStamina(zActor->getStamina() - staminaCost);
    cooldownTicks = usedConfig->getCooldownTicks();
    setXp(getXp() + usedConfig->getXpGain());
    if (!invalid)
    {
        Framework::Vec3<int> pos = zTarget->getPos();
        int dimension = zTarget->getDimensionId();
        Game::INSTANCE->doLater([pos, dimension, usedConfig]() {
            Game::INSTANCE->zDimension(dimension)->placeBlock(
                pos, usedConfig->getReplacementBlockTypeId());
        });
    }
    return true;
}

bool BlockReplaceItemSkill::use(
    Entity* zActor, Item* zUsedItem, Entity* zTarget)
{
    return false;
}

void BlockReplaceItemSkill::setInvalidUseConfig(
    BlockReplaceItemSkillConfig* config)
{
    if (invalidUseConfig) invalidUseConfig->release();
    invalidUseConfig = config;
}

BlockReplaceItemSkillConfig* BlockReplaceItemSkill::zInvalidUseConfig() const
{
    return invalidUseConfig;
}

void BlockReplaceItemSkill::addConfig(BlockReplaceItemSkillConfig* config)
{
    configs.add(config);
}

const Framework::RCArray<BlockReplaceItemSkillConfig>&
BlockReplaceItemSkill::getConfigs() const
{
    return configs;
}

void BlockReplaceItemSkill::setCooldownTicks(int cooldownTicks)
{
    this->cooldownTicks = cooldownTicks;
}

int BlockReplaceItemSkill::getCooldownTicks() const
{
    return cooldownTicks;
}

BlockReplaceItemSkillFactory::BlockReplaceItemSkillFactory()
    : ItemSkillFactoryBase()
{}

BlockReplaceItemSkill* BlockReplaceItemSkillFactory::createValue(
    Framework::JSON::JSONObject* zJson) const
{
    return new BlockReplaceItemSkill();
}

BlockReplaceItemSkill* BlockReplaceItemSkillFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    BlockReplaceItemSkill* result = ItemSkillFactoryBase::fromJson(zJson);
    BlockReplaceItemSkillConfig* invalidUseConfig
        = new BlockReplaceItemSkillConfig();
    invalidUseConfig->setStaminaCost(
        (float)zJson->zValue("invalidStaminaCost")->asNumber()->getNumber());
    invalidUseConfig->setStaminaCostDevider(
        (float)zJson->zValue("invalidStaminaCostDevider")
            ->asNumber()
            ->getNumber());
    invalidUseConfig->setAdditionalStaminaCostDeviderPerLevel(
        (float)zJson->zValue("invalidAdditionalStaminaCostDeviderPerLevel")
            ->asNumber()
            ->getNumber());
    invalidUseConfig->setDurabilityCost(
        (float)zJson->zValue("invalidDurabilityCost")->asNumber()->getNumber());
    invalidUseConfig->setDurabilityCostDevider(
        (float)zJson->zValue("invalidDurabilityCostDevider")
            ->asNumber()
            ->getNumber());
    invalidUseConfig->setAdditionalDurabilityCostDeviderPerLevel(
        (float)zJson->zValue("invalidAdditionalDurabilityCostDeviderPerLevel")
            ->asNumber()
            ->getNumber());
    invalidUseConfig->setCooldownTicks(
        (int)zJson->zValue("invalidCooldownTicks")->asNumber()->getNumber());
    invalidUseConfig->setXpGain(0.f);
    result->setInvalidUseConfig(invalidUseConfig);
    Framework::RCArray<BlockReplaceItemSkillConfig> configs;
    for (Framework::JSON::JSONValue* value :
        *zJson->zValue("configs")->asArray())
    {
        result->addConfig(Game::INSTANCE->zTypeRegistry()
                ->fromJson<BlockReplaceItemSkillConfig>(value));
    }
    return result;
}

Framework::JSON::JSONObject* BlockReplaceItemSkillFactory::toJsonObject(
    BlockReplaceItemSkill* zObject) const
{
    Framework::JSON::JSONObject* result
        = ItemSkillFactoryBase::toJsonObject(zObject);
    result->addValue("invalidStaminaCost",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getStaminaCost()));
    result->addValue("invalidStaminaCostDevider",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getStaminaCostDevider()));
    result->addValue("invalidAdditionalStaminaCostDeviderPerLevel",
        new Framework::JSON::JSONNumber(zObject->zInvalidUseConfig()
                ->getAdditionalStaminaCostDeviderPerLevel()));
    result->addValue("invalidDurabilityCost",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getDurabilityCost()));
    result->addValue("invalidDurabilityCostDevider",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getDurabilityCostDevider()));
    result->addValue("invalidAdditionalDurabilityCostDeviderPerLevel",
        new Framework::JSON::JSONNumber(zObject->zInvalidUseConfig()
                ->getAdditionalDurabilityCostDeviderPerLevel()));
    result->addValue("invalidCooldownTicks",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getCooldownTicks()));
    Framework::JSON::JSONArray* configs = new Framework::JSON::JSONArray();
    for (BlockReplaceItemSkillConfig* config : zObject->getConfigs())
    {
        configs->addValue(Game::INSTANCE->zTypeRegistry()->toJson(config));
    }
    result->addValue("configs", configs);
    return result;
}

JSONObjectValidationBuilder* BlockReplaceItemSkillFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return ItemSkillFactoryBase::addToValidator(
        builder->withRequiredNumber("invalidStaminaCost")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.5)
            ->finishNumber()
            ->withRequiredNumber("invalidStaminaCostDevider")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.8)
            ->finishNumber()
            ->withRequiredNumber("invalidAdditionalStaminaCostDeviderPerLevel")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.2)
            ->finishNumber()
            ->withRequiredNumber("invalidDurabilityCost")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.5)
            ->finishNumber()
            ->withRequiredNumber("invalidDurabilityCostDevider")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.98)
            ->finishNumber()
            ->withRequiredNumber(
                "invalidAdditionalDurabilityCostDeviderPerLevel")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.02)
            ->finishNumber()
            ->withRequiredNumber("invalidCooldownTicks")
            ->withDefault(20)
            ->finishNumber()
            ->withRequiredArray("configs")
            ->addAcceptedTypeInArray(Game::INSTANCE->zTypeRegistry()
                    ->getValidator<BlockReplaceItemSkillConfig>())
            ->finishArray());
}

const char* BlockReplaceItemSkillFactory::getTypeToken() const
{
    return "replaceBlock";
}

DamagingItemSkillConfig::DamagingItemSkillConfig()
    : ReferenceCounter(),
      targetBlockFilter(0),
      damage(2.f),
      damagePerHeadHardness(1.f),
      baseDamageMultiplier(1.f),
      damageMultiplierPerHeadHardness(0.f),
      damagePerLevel(0.3f),
      damageMultiplierPerLevel(0.0f),
      damageDevider(1.0f),
      damageDeviderPerHardness(1.0f),
      staminaCost(0.00001f),
      staminaCostPerDamage(0.00001f),
      staminaCostPerHardness(0.001f),
      staminaCostDevider(0.9f),
      staminaCostDeviderPerLevel(0.1f),
      durabilityCost(0.1f),
      durabilityCostPerDamage(0.01f),
      durabilityCostPerHardness(0.01f),
      durabilityCostDevider(0.8f),
      additionalDurabilityCostDeviderPerLevel(0.2f),
      xpGainPerDamage(0.05f)
{}

DamagingItemSkillConfig::~DamagingItemSkillConfig()
{
    if (targetBlockFilter) targetBlockFilter->release();
}

void DamagingItemSkillConfig::setTargetBlockFilter(
    BlockFilter* targetBlockFilter)
{
    if (this->targetBlockFilter) this->targetBlockFilter->release();
    this->targetBlockFilter = targetBlockFilter;
}

BlockFilter* DamagingItemSkillConfig::zTargetBlockFilter() const
{
    return targetBlockFilter;
}

void DamagingItemSkillConfig::setDamage(float damage)
{
    this->damage = damage;
}

float DamagingItemSkillConfig::getDamage() const
{
    return damage;
}

void DamagingItemSkillConfig::setDamagePerHeadHardness(
    float damagePerHeadHardness)
{
    this->damagePerHeadHardness = damagePerHeadHardness;
}

float DamagingItemSkillConfig::getDamagePerHeadHardness() const
{
    return damageDeviderPerHardness;
}

void DamagingItemSkillConfig::setBaseDamageMultiplier(
    float baseDamageMultiplier)
{
    this->baseDamageMultiplier = baseDamageMultiplier;
}

float DamagingItemSkillConfig::getBaseDamageMultiplier() const
{
    return baseDamageMultiplier;
}

void DamagingItemSkillConfig::setDamageMultiplierPerHeadHardness(
    float damageMupliplierPerHeadHardness)
{
    this->damageMultiplierPerHeadHardness = damageMupliplierPerHeadHardness;
}

float DamagingItemSkillConfig::getDamageMultiplierPerHeadHardness() const
{
    return damageMultiplierPerHeadHardness;
}

void DamagingItemSkillConfig::setDamagePerLevel(float damagePerLevel)
{
    this->damagePerLevel = damagePerLevel;
}

float DamagingItemSkillConfig::getDamagePerLevel() const
{
    return damagePerLevel;
}

void DamagingItemSkillConfig::setDamageMultiplierPerLevel(
    float damageMultiplierPerLevel)
{
    this->damageMultiplierPerLevel = damageMultiplierPerLevel;
}

float DamagingItemSkillConfig::getDamageMultiplierPerLevel() const
{
    return damageMultiplierPerLevel;
}

void DamagingItemSkillConfig::setDamageDevider(float damageDevider)
{
    this->damageDevider = damageDevider;
}

float DamagingItemSkillConfig::getDamageDevider() const
{
    return damageDevider;
}

void DamagingItemSkillConfig::setDamageDeviderPerHardness(
    float damageDeviderPerHardness)
{
    this->damageDeviderPerHardness = damageDeviderPerHardness;
}

float DamagingItemSkillConfig::getDamageDeviderPerHardness() const
{
    return damageDeviderPerHardness;
}

void DamagingItemSkillConfig::setStaminaCost(float staminaCost)
{
    this->staminaCost = staminaCost;
}

float DamagingItemSkillConfig::getStaminaCost() const
{
    return staminaCost;
}

void DamagingItemSkillConfig::setStaminaCostPerDamage(
    float staminaCostPerDamage)
{
    this->staminaCostPerDamage = staminaCostPerDamage;
}

float DamagingItemSkillConfig::getStaminaCostPerDamage() const
{
    return staminaCostPerDamage;
}

void DamagingItemSkillConfig::setStaminaCostPerHardness(
    float staminaCostPerHardness)
{
    this->staminaCostPerHardness = staminaCostPerHardness;
}

float DamagingItemSkillConfig::getStaminaCostPerHardness() const
{
    return staminaCostPerHardness;
}

void DamagingItemSkillConfig::setStaminaCostDevider(float staminaCostDevider)
{
    this->staminaCostDevider = staminaCostDevider;
}

float DamagingItemSkillConfig::getStaminaCostDevider() const
{
    return staminaCostDevider;
}

void DamagingItemSkillConfig::setStaminaCostDeviderPerLevel(
    float staminaCostDeviderPerLevel)
{
    this->staminaCostDeviderPerLevel = staminaCostDeviderPerLevel;
}

float DamagingItemSkillConfig::getStaminaCostDeviderPerLevel() const
{
    return staminaCostDeviderPerLevel;
}

void DamagingItemSkillConfig::setDurabilityCost(float durabilityCost)
{
    this->durabilityCost = durabilityCost;
}

float DamagingItemSkillConfig::getDurabilityCost() const
{
    return durabilityCost;
}

void DamagingItemSkillConfig::setDurabilityCostPerDamage(
    float durabilityCostPerDamage)
{
    this->durabilityCostPerDamage = durabilityCostPerDamage;
}

float DamagingItemSkillConfig::getDurabilityCostPerDamage() const
{
    return durabilityCostPerDamage;
}

void DamagingItemSkillConfig::setDurabilityCostPerHardness(
    float durabilityCostPerHardness)
{
    this->durabilityCostPerHardness = durabilityCostPerHardness;
}

float DamagingItemSkillConfig::getDurabilityCostPerHardness() const
{
    return durabilityCostPerHardness;
}

void DamagingItemSkillConfig::setDurabilityCostDevider(
    float durabilityCostDevider)
{
    this->durabilityCostDevider = durabilityCostDevider;
}

float DamagingItemSkillConfig::getDurabilityCostDevider() const
{
    return durabilityCostDevider;
}

void DamagingItemSkillConfig::setAdditionalDurabilityCostDeviderPerLevel(
    float additionalDurabilityCostDeviderPerLevel)
{
    this->additionalDurabilityCostDeviderPerLevel
        = additionalDurabilityCostDeviderPerLevel;
}

float DamagingItemSkillConfig::getAdditionalDurabilityCostDeviderPerLevel()
    const
{
    return additionalDurabilityCostDeviderPerLevel;
}

void DamagingItemSkillConfig::setXpGainPerDamage(float xpGainPerDamage)
{
    this->xpGainPerDamage = xpGainPerDamage;
}

float DamagingItemSkillConfig::getXpGainPerDamage() const
{
    return xpGainPerDamage;
}

DamagingItemSkillConfigFactory::DamagingItemSkillConfigFactory()
    : ObjectTypeFactory()
{}

DamagingItemSkillConfig* DamagingItemSkillConfigFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    DamagingItemSkillConfig* result = new DamagingItemSkillConfig();
    result->setTargetBlockFilter(
        Game::INSTANCE->zTypeRegistry()->fromJson<BlockFilter>(
            zJson->zValue("targetFilter")));
    result->setDamage(
        (float)zJson->asObject()->zValue("damage")->asNumber()->getNumber());
    result->setDamagePerHeadHardness((float)zJson->asObject()
            ->zValue("damagePerHeadHardness")
            ->asNumber()
            ->getNumber());
    result->setBaseDamageMultiplier((float)zJson->asObject()
            ->zValue("baseDamageMultiplier")
            ->asNumber()
            ->getNumber());
    result->setDamageMultiplierPerHeadHardness((float)zJson->asObject()
            ->zValue("damageMultiplierPerHeadHardness")
            ->asNumber()
            ->getNumber());
    result->setDamagePerLevel((float)zJson->asObject()
            ->zValue("damagePerLevel")
            ->asNumber()
            ->getNumber());
    result->setDamageMultiplierPerLevel((float)zJson->asObject()
            ->zValue("damageMultiplierPerLevel")
            ->asNumber()
            ->getNumber());
    result->setDamageDevider((float)zJson->asObject()
            ->zValue("damageDevider")
            ->asNumber()
            ->getNumber());
    result->setDamageDeviderPerHardness((float)zJson->asObject()
            ->zValue("damageDeviderPerHardness")
            ->asNumber()
            ->getNumber());
    result->setStaminaCost((float)zJson->asObject()
            ->zValue("staminaCost")
            ->asNumber()
            ->getNumber());
    result->setStaminaCostPerDamage((float)zJson->asObject()
            ->zValue("staminaCostPerDamage")
            ->asNumber()
            ->getNumber());
    result->setStaminaCostPerHardness((float)zJson->asObject()
            ->zValue("staminaCostPerHardness")
            ->asNumber()
            ->getNumber());
    result->setStaminaCostDevider((float)zJson->asObject()
            ->zValue("staminaCostDevider")
            ->asNumber()
            ->getNumber());
    result->setStaminaCostDeviderPerLevel((float)zJson->asObject()
            ->zValue("staminaCostDeviderPerLevel")
            ->asNumber()
            ->getNumber());
    result->setDurabilityCost((float)zJson->asObject()
            ->zValue("durabilityCost")
            ->asNumber()
            ->getNumber());
    result->setDurabilityCostPerDamage((float)zJson->asObject()
            ->zValue("durabilityCostPerDamage")
            ->asNumber()
            ->getNumber());
    result->setDurabilityCostPerHardness((float)zJson->asObject()
            ->zValue("durabilityCostPerHardness")
            ->asNumber()
            ->getNumber());
    result->setDurabilityCostDevider((float)zJson->asObject()
            ->zValue("durabilityCostDevider")
            ->asNumber()
            ->getNumber());
    result->setAdditionalDurabilityCostDeviderPerLevel((float)zJson->asObject()
            ->zValue("additionalDurabilityCostDeviderPerLevel")
            ->asNumber()
            ->getNumber());
    result->setXpGainPerDamage((float)zJson->asObject()
            ->zValue("xpGainPerDamage")
            ->asNumber()
            ->getNumber());
    return result;
}

Framework::JSON::JSONObject* DamagingItemSkillConfigFactory::toJsonObject(
    DamagingItemSkillConfig* zObject) const
{
    Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
    result->addValue("targetFilter",
        Game::INSTANCE->zTypeRegistry()->toJson(zObject->zTargetBlockFilter()));
    result->addValue(
        "damage", new Framework::JSON::JSONNumber(zObject->getDamage()));
    result->addValue("damagePerHeadHardness",
        new Framework::JSON::JSONNumber(zObject->getDamagePerHeadHardness()));
    result->addValue("baseDamageMultiplier",
        new Framework::JSON::JSONNumber(zObject->getBaseDamageMultiplier()));
    result->addValue("damageMultiplierPerHeadHardness",
        new Framework::JSON::JSONNumber(
            zObject->getDamageMultiplierPerHeadHardness()));
    result->addValue("damagePerLevel",
        new Framework::JSON::JSONNumber(zObject->getDamagePerLevel()));
    result->addValue("damageMultiplierPerLevel",
        new Framework::JSON::JSONNumber(
            zObject->getDamageMultiplierPerLevel()));
    result->addValue("damageDevider",
        new Framework::JSON::JSONNumber(zObject->getDamageDevider()));
    result->addValue("damageDeviderPerHardness",
        new Framework::JSON::JSONNumber(
            zObject->getDamageDeviderPerHardness()));
    result->addValue("staminaCost",
        new Framework::JSON::JSONNumber(zObject->getStaminaCost()));
    result->addValue("staminaCostPerDamage",
        new Framework::JSON::JSONNumber(zObject->getStaminaCostPerDamage()));
    result->addValue("staminaCostPerHardness",
        new Framework::JSON::JSONNumber(zObject->getStaminaCostPerHardness()));
    result->addValue("staminaCostDevider",
        new Framework::JSON::JSONNumber(zObject->getStaminaCostDevider()));
    result->addValue("staminaCostDeviderPerLevel",
        new Framework::JSON::JSONNumber(
            zObject->getStaminaCostDeviderPerLevel()));
    result->addValue("durabilityCost",
        new Framework::JSON::JSONNumber(zObject->getDurabilityCost()));
    result->addValue("durabilityCostPerDamage",
        new Framework::JSON::JSONNumber(zObject->getDurabilityCostPerDamage()));
    result->addValue("durabilityCostPerHardness",
        new Framework::JSON::JSONNumber(
            zObject->getDurabilityCostPerHardness()));
    result->addValue("durabilityCostDevider",
        new Framework::JSON::JSONNumber(zObject->getDurabilityCostDevider()));
    result->addValue("additionalDurabilityCostDeviderPerLevel",
        new Framework::JSON::JSONNumber(
            zObject->getAdditionalDurabilityCostDeviderPerLevel()));
    result->addValue("xpGainPerDamage",
        new Framework::JSON::JSONNumber(zObject->getXpGainPerDamage()));
    return result;
}

JSONObjectValidationBuilder* DamagingItemSkillConfigFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return builder
        ->withRequiredAttribute("targetFilter",
            Game::INSTANCE->zTypeRegistry()->getValidator<BlockFilter>())
        ->withRequiredNumber("damage")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(2.0)
        ->finishNumber()
        ->withRequiredNumber("damagePerHeadHardness")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(1.0)
        ->finishNumber()
        ->withRequiredNumber("baseDamageMultiplier")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(1.0)
        ->finishNumber()
        ->withRequiredNumber("damageMultiplierPerHeadHardness")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.0)
        ->finishNumber()
        ->withRequiredNumber("damagePerLevel")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.3)
        ->finishNumber()
        ->withRequiredNumber("damageMultiplierPerLevel")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.0)
        ->finishNumber()
        ->withRequiredNumber("damageDevider")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(1.0)
        ->finishNumber()
        ->withRequiredNumber("damageDeviderPerHardness")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(1.0)
        ->finishNumber()
        ->withRequiredNumber("staminaCost")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.0001)
        ->finishNumber()
        ->withRequiredNumber("staminaCostPerDamage")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.00001)
        ->finishNumber()
        ->withRequiredNumber("staminaCostPerHardness")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.001)
        ->finishNumber()
        ->withRequiredNumber("staminaCostDevider")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.9)
        ->finishNumber()
        ->withRequiredNumber("staminaCostDeviderPerLevel")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.1)
        ->finishNumber()
        ->withRequiredNumber("durabilityCost")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.1)
        ->finishNumber()
        ->withRequiredNumber("durabilityCostPerDamage")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.01)
        ->finishNumber()
        ->withRequiredNumber("durabilityCostPerHardness")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.01)
        ->finishNumber()
        ->withRequiredNumber("durabilityCostDevider")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.8)
        ->finishNumber()
        ->withRequiredNumber("additionalDurabilityCostDeviderPerLevel")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.2)
        ->finishNumber()
        ->withRequiredNumber("xpGainPerDamage")
        ->whichIsGreaterOrEqual(0.0)
        ->withDefault(0.05)
        ->finishNumber();
}

DamagingItemSkill::DamagingItemSkill()
    : ItemSkill(),
      invalidUseConfig(0)
{}

DamagingItemSkill::~DamagingItemSkill()
{
    if (invalidUseConfig) invalidUseConfig->release();
}

bool DamagingItemSkill::use(Entity* zActor, Item* zUsedItem, Block* zTarget)
{
    BasicToolItem* tool = dynamic_cast<BasicToolItem*>(zUsedItem);
    DamagingItemSkillConfig* usedConfig = 0;
    for (DamagingItemSkillConfig* config : configs)
    {
        if (config->zTargetBlockFilter()->test(zTarget))
        {
            usedConfig = config;
            break;
        }
    }
    bool invalid = 0;
    if (usedConfig == 0)
    {
        usedConfig = invalidUseConfig;
        invalid = 1;
    }
    float damage = usedConfig->getDamage()
                 + usedConfig->getDamagePerHeadHardness()
                       * (tool ? tool->getHeadMaterialHardness() : 0)
                 + usedConfig->getDamagePerLevel() * getLevel();
    float damageMultiplier
        = usedConfig->getBaseDamageMultiplier()
        + usedConfig->getDamageMultiplierPerLevel() * getLevel()
        + usedConfig->getDamageMultiplierPerHeadHardness()
              * (tool ? tool->getHeadMaterialHardness() : 0);
    if (damageMultiplier != 0)
    {
        damage *= damageMultiplier;
    }
    float damageDevider
        = usedConfig->getDamageDevider()
        + usedConfig->getDamageDeviderPerHardness() * zTarget->getHardness();
    if (damageDevider != 0)
    {
        damage /= damageDevider;
    }
    float staminaCost
        = usedConfig->getStaminaCost()
        + usedConfig->getStaminaCostPerDamage() * damage
        + usedConfig->getStaminaCostPerHardness() * zTarget->getHardness();
    float staminaDevider
        = usedConfig->getStaminaCostDevider()
        + usedConfig->getStaminaCostDeviderPerLevel() * getLevel();
    if (staminaDevider != 0)
    {
        staminaCost /= staminaDevider;
    }
    if (zActor->getStamina() < staminaCost)
    {
        return false;
    }
    float durabilityCost
        = usedConfig->getDurabilityCost()
        + usedConfig->getDurabilityCostPerDamage() * damage
        + usedConfig->getDurabilityCostPerHardness() * zTarget->getHardness();
    float durabilityDevider
        = usedConfig->getDurabilityCostDevider()
        + usedConfig->getAdditionalDurabilityCostDeviderPerLevel() * getLevel();
    if (durabilityDevider != 0)
    {
        durabilityCost /= durabilityDevider;
    }
    if (zUsedItem)
    {
        zUsedItem->setDurability(zUsedItem->getDurability() - durabilityCost);
    }
    zActor->setStamina(zActor->getStamina() - staminaCost);
    setXp(getXp() + usedConfig->getXpGainPerDamage() * damage);
    zTarget->setHP(zActor, zUsedItem, this, zTarget->getHP() - damage);
    return true;
}

bool DamagingItemSkill::use(Entity* zActor, Item* zUsedItem, Entity* zTarget)
{
    return false;
}

void DamagingItemSkill::setInvalidUseConfig(DamagingItemSkillConfig* config)
{
    if (invalidUseConfig) invalidUseConfig->release();
    invalidUseConfig = config;
}

DamagingItemSkillConfig* DamagingItemSkill::zInvalidUseConfig() const
{
    return invalidUseConfig;
}

void DamagingItemSkill::addConfig(DamagingItemSkillConfig* config)
{
    configs.add(config);
}

const Framework::RCArray<DamagingItemSkillConfig>&
DamagingItemSkill::getConfigs() const
{
    return configs;
}

DamagingItemSkillFactory::DamagingItemSkillFactory()
    : ItemSkillFactoryBase()
{}

DamagingItemSkill* DamagingItemSkillFactory::createValue(
    Framework::JSON::JSONObject* zJson) const
{
    return new DamagingItemSkill();
}

DamagingItemSkill* DamagingItemSkillFactory::fromJson(
    Framework::JSON::JSONObject* zJson) const
{
    DamagingItemSkill* result = ItemSkillFactoryBase::fromJson(zJson);
    for (Framework::JSON::JSONValue* configValue :
        *zJson->zValue("configs")->asArray())
    {
        result->addConfig(
            Game::INSTANCE->zTypeRegistry()->fromJson<DamagingItemSkillConfig>(
                configValue));
    }
    DamagingItemSkillConfig* invalidUseConfig = new DamagingItemSkillConfig();
    invalidUseConfig->setDamage(0.f);
    invalidUseConfig->setDamageDeviderPerHardness(0.f);
    invalidUseConfig->setBaseDamageMultiplier(0.f);
    invalidUseConfig->setDamageMultiplierPerHeadHardness(0.f);
    invalidUseConfig->setDamagePerLevel(0.f);
    invalidUseConfig->setDamageMultiplierPerLevel(0.f);
    invalidUseConfig->setDamageDevider(1.f);
    invalidUseConfig->setDamageDeviderPerHardness(0.f);
    invalidUseConfig->setStaminaCost(
        (float)zJson->zValue("invalidStaminaCost")->asNumber()->getNumber());
    invalidUseConfig->setStaminaCostPerDamage(0.f);
    invalidUseConfig->setStaminaCostPerHardness(
        (float)zJson->zValue("invalidStaminaCostPerHardness")
            ->asNumber()
            ->getNumber());
    invalidUseConfig->setStaminaCostDevider(
        (float)zJson->zValue("invalidStaminaCostDevider")
            ->asNumber()
            ->getNumber());
    invalidUseConfig->setStaminaCostDeviderPerLevel(
        (float)zJson->zValue("invalidStaminaCostDeviderPerLevel")
            ->asNumber()
            ->getNumber());
    invalidUseConfig->setDurabilityCost(
        (float)zJson->zValue("invalidDurabilityCost")->asNumber()->getNumber());
    invalidUseConfig->setDurabilityCostPerHardness(
        (float)zJson->zValue("invalidDurabilityCostPerHardness")
            ->asNumber()
            ->getNumber());
    invalidUseConfig->setDurabilityCostDevider(
        (float)zJson->zValue("invalidDurabilityCostDevider")
            ->asNumber()
            ->getNumber());
    invalidUseConfig->setAdditionalDurabilityCostDeviderPerLevel(
        (float)zJson->zValue("invalidDurabilityCostDeviderPerLevel")
            ->asNumber()
            ->getNumber());
    result->setInvalidUseConfig(invalidUseConfig);
    return result;
}

Framework::JSON::JSONObject* DamagingItemSkillFactory::toJsonObject(
    DamagingItemSkill* zObject) const
{
    Framework::JSON::JSONObject* result
        = ItemSkillFactoryBase::toJsonObject(zObject);
    result->addValue(
        "maxXp", new Framework::JSON::JSONNumber(zObject->getMaxXp()));
    result->addValue("invalidStaminaCost",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getStaminaCost()));
    result->addValue("invalidStaminaCostPerHardness",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getStaminaCostPerHardness()));
    result->addValue("invalidStaminaCostDevider",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getStaminaCostDevider()));
    result->addValue("invalidStaminaCostDeviderPerLevel",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getStaminaCostDeviderPerLevel()));
    result->addValue("invalidDurabilityCost",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getDurabilityCost()));
    result->addValue("invalidDurabilityCostPerHardness",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getDurabilityCostPerHardness()));
    result->addValue("invalidDurabilityCostDevider",
        new Framework::JSON::JSONNumber(
            zObject->zInvalidUseConfig()->getDurabilityCostDevider()));
    result->addValue("invalidDurabilityCostDeviderPerLevel",
        new Framework::JSON::JSONNumber(zObject->zInvalidUseConfig()
                ->getAdditionalDurabilityCostDeviderPerLevel()));
    Framework::JSON::JSONArray* configs = new Framework::JSON::JSONArray();
    for (DamagingItemSkillConfig* config : zObject->getConfigs())
    {
        configs->addValue(Game::INSTANCE->zTypeRegistry()->toJson(config));
    }
    result->addValue("configs", configs);
    return result;
}

JSONObjectValidationBuilder* DamagingItemSkillFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    return ItemSkillFactoryBase::addToValidator(
        builder->withRequiredNumber("invalidStaminaCost")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.5)
            ->finishNumber()
            ->withRequiredNumber("invalidStaminaCostPerHardness")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.001)
            ->finishNumber()
            ->withRequiredNumber("invalidStaminaCostDevider")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.8)
            ->finishNumber()
            ->withRequiredNumber("invalidAdditionalStaminaCostDeviderPerLevel")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.2)
            ->finishNumber()
            ->withRequiredNumber("invalidDurabilityCost")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.5)
            ->finishNumber()
            ->withRequiredNumber("invalidDurabilityCostPerHardness")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.01)
            ->finishNumber()
            ->withRequiredNumber("invalidDurabilityCostDevider")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.98)
            ->finishNumber()
            ->withRequiredNumber(
                "invalidAdditionalDurabilityCostDeviderPerLevel")
            ->whichIsGreaterOrEqual(0.0)
            ->withDefault(0.02)
            ->finishNumber()
            ->withRequiredArray("configs")
            ->addAcceptedTypeInArray(Game::INSTANCE->zTypeRegistry()
                    ->getValidator<DamagingItemSkillConfig>())
            ->finishArray());
}

const char* DamagingItemSkillFactory::getTypeToken() const
{
    return "damaging";
}