public override int OnBlockDamaged(WorldBase _world, int _clrIdx, Vector3i _blockPos, BlockValue _blockValue, int _damagePoints, int _entityIdThatDamaged, bool _bUseHarvestTool, bool _bByPassMaxDamage, int _recDepth)
    {
        var chunkCluster = _world.ChunkClusters[_clrIdx];

        if (chunkCluster == null)
        {
            return(0);
        }

        if (isMultiBlock && _blockValue.ischild)
        {
            var parentPos = multiBlockPos.GetParentPos(_blockPos, _blockValue);
            var block     = chunkCluster.GetBlock(parentPos);

            if (block.ischild)
            {
                Debug.Log("Block on position " + parentPos + " should be a parent but is not!");
            }

            return(block.ischild ? 0 : list[block.type].OnBlockDamaged(_world, _clrIdx, parentPos, block, _damagePoints, _entityIdThatDamaged, false, _bByPassMaxDamage, 0));
        }

        var d   = _blockValue.damage;
        var max = list[_blockValue.type].MaxDamage;

        if (d >= max || d + _damagePoints < max)
        {
            return(base.OnBlockDamaged(_world, _clrIdx, _blockPos, _blockValue, _damagePoints, _entityIdThatDamaged, _bUseHarvestTool, _bByPassMaxDamage, _recDepth));
        }

        chunkCluster.InvokeOnBlockDamagedDelegates(_blockPos, _blockValue, _damagePoints, _entityIdThatDamaged);

        if (!Stopped(_blockValue.meta2))
        {
            _blockValue.damage = 0;
            _blockValue.meta2  = (byte)(_blockValue.meta2 | (1 << 1));

            _world.SetBlockRPC(_clrIdx, _blockPos, _blockValue);
            return(0);
        }

        if (!OnBlockDestroyedBy(_world, _clrIdx, _blockPos, _blockValue, _entityIdThatDamaged, _bUseHarvestTool))
        {
            return(max);
        }

        SpawnDestroyParticleEffect(_world, _blockValue, _blockPos, _world.GetLightBrightness(_blockPos + new Vector3i(0, 1, 0)), GetColorForSide(_blockValue, BlockFace.Top), _entityIdThatDamaged);

        if (DowngradeBlock.type == 0)
        {
            _world.SetBlockRPC(_clrIdx, _blockPos, BlockValue.Air);

            //todo: should this be 0? seems a bug to return the old types max damage
            return(list[_blockValue.type].MaxDamage);
        }

        //todo:update to include paint and density?
        var downgrade = DowngradeBlock;

        downgrade.rotation = _blockValue.rotation;
        downgrade.meta     = _blockValue.meta;
        if (list[downgrade.type].shape.IsTerrain())
        {
            _world.SetBlockRPC(_clrIdx, _blockPos, downgrade, list[downgrade.type].Density);
        }
        else
        {
            _world.SetBlockRPC(_clrIdx, _blockPos, downgrade);
        }

        return(list[_blockValue.type].MaxDamage);
    }