
/*
 * Copyright 2011-2022 Cuberite Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "Globals.h"

#include "Silverfish.h"

#include "../World.h"
#include "../Chunk.h"
#include "../Blocks/BlockHandler.h"
#include "../Blocks/BlockInfested.h"





cSilverfish::cSilverfish() :
	Super("Silverfish", mtSilverfish, "entity.silverfish.hurt", "entity.silverfish.death", "entity.silverfish.ambient", 0.4f, 0.3f)
{
}





bool cSilverfish::DoTakeDamage(TakeDamageInfo &a_TDI)
{
	// Call on our brethren to attack!
	// TODO: stop this if /gamerule mobGriefing is set to false

	// If the entity didn't take any damage, bail:
	if (!Super::DoTakeDamage(a_TDI))
	{
		return false;
	}

	// Or, conversely took lethal damage, bail:
	if (m_Health <= 0)
	{
		return true;
	}

	if (a_TDI.Attacker == nullptr)
	{
		if ((a_TDI.DamageType != dtPoison) && (a_TDI.DamageType != dtPotionOfHarming))
		{
			// Bail if attacker doesn't exist and it wasn't a splash potion:
			return true;
		}
	}
	else if (!a_TDI.Attacker->IsPlayer())
	{
		// Bail if it wasn't a player attack:
		return true;
	}

	auto & Random = GetRandomProvider();

	// Tries to spawn another Silverfish, returning if the search should continue.
	auto CheckInfested = [this, &Random, Position = GetPosition().Floor()](const Vector3i Offset) mutable
	{
		const auto Block = Position + Offset;
		if (m_World->GetBlock(Block) == E_BLOCK_SILVERFISH_EGG)
		{
			m_World->DigBlock(Block);
			return Random.RandBool();
		}
		return false;
	};

	// Search the faces of an increasingly large cube (so the positions closest get looked at first)
	// of min 3, max 10, for infested blocks and spawn additional reinforcements:
	for (int CubeSideLength = 3; CubeSideLength <= 10; CubeSideLength += 2)
	{
		const int HalfSide = CubeSideLength / 2;

		for (int OffsetX = -HalfSide; OffsetX <= HalfSide; OffsetX++)
		{
			for (int OffsetZ = -HalfSide; OffsetZ <= HalfSide; OffsetZ++)
			{
				if (CheckInfested({ OffsetX, +HalfSide, OffsetZ }) || CheckInfested({ OffsetX, -HalfSide, OffsetZ }))
				{
					return true;
				}
			}
		}

		for (int OffsetX = -HalfSide; OffsetX <= HalfSide; OffsetX++)
		{
			for (int OffsetY = -HalfSide + 1; OffsetY <= HalfSide - 1; OffsetY++)
			{
				if (CheckInfested({ OffsetX, OffsetY, +HalfSide }) || CheckInfested({ OffsetX, OffsetY, -HalfSide }))
				{
					return true;
				}
			}
		}

		for (int OffsetZ = -HalfSide + 1; OffsetZ <= HalfSide - 1; OffsetZ++)
		{
			for (int OffsetY = -HalfSide + 1; OffsetY <= HalfSide - 1; OffsetY++)
			{
				if (CheckInfested({ +HalfSide, OffsetY, OffsetZ }) || CheckInfested({ -HalfSide, OffsetY, OffsetZ }))
				{
					return true;
				}
			}
		}
	}

	return true;
}
