/*
**	Command & Conquer Generals Zero Hour(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

////////////////////////////////////////////////////////////////////////////////
//																																						//
//  (c) 2001-2003 Electronic Arts Inc.																				//
//																																						//
////////////////////////////////////////////////////////////////////////////////

// FILE: DamageFX.cpp ///////////////////////////////////////////////////////////////////////////////
// Author: Steven Johnson, November 2001
// Desc:   DamageFX descriptions
///////////////////////////////////////////////////////////////////////////////////////////////////

// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////

#include "PreRTS.h"	// This must go first in EVERY cpp file in the GameEngine

#include "Common/INI.h"
#include "Common/ThingFactory.h"
#include "Common/ThingTemplate.h"
#include "Common/DamageFX.h"
#include "Common/GameAudio.h"

#include "GameClient/FXList.h"
#include "GameLogic/Damage.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#include "GameClient/GameClient.h"
#include "GameClient/InGameUI.h"

///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

DamageFXStore *TheDamageFXStore = NULL;					///< the DamageFX store definition

///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE CLASSES ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

//-------------------------------------------------------------------------------------------------
DamageFX::DamageFX()
{
	// not necessary.
	//clear();
}

//-------------------------------------------------------------------------------------------------
void DamageFX::clear()
{
	for (Int dt = 0; dt < DAMAGE_NUM_TYPES; ++dt)
	{
		for (Int v = LEVEL_FIRST; v <= LEVEL_LAST; ++v)
		{
			m_dfx[dt][v].clear();
		}
	}
}

//-------------------------------------------------------------------------------------------------
UnsignedInt DamageFX::getDamageFXThrottleTime(DamageType t, const Object* source) const 
{ 
	return m_dfx[t][source ? source->getVeterancyLevel() : LEVEL_REGULAR].m_damageFXThrottleTime; 
}

//-------------------------------------------------------------------------------------------------
void DamageFX::doDamageFX(DamageType t, Real damageAmount, const Object* source, const Object* victim) const
{ 
	ConstFXListPtr fx = getDamageFXList(t, damageAmount, source);
	// since the victim is receiving the damage, it's the "primary" object.
	// the source is the "secondary" object -- unused by most fx, but could be
	// useful in some cases.
	FXList::doFXObj(fx, victim, source);
}

//-------------------------------------------------------------------------------------------------
ConstFXListPtr DamageFX::getDamageFXList(DamageType t, Real damageAmount, const Object* source) const
{ 
	/*
		if damage is zero, never do damage fx. this is by design, since "zero" damage can happen
		with some special weapons, like the battleship, which is a "faux" weapon that never does damage.
		if you really need to change this for some reason, consider carefully... (srj)
	*/
	if (damageAmount == 0.0f)
		return NULL;

	const DFX& dfx = m_dfx[t][source ? source->getVeterancyLevel() : LEVEL_REGULAR];
	ConstFXListPtr fx = 
		damageAmount >= dfx.m_amountForMajorFX ? 
		dfx.m_majorDamageFXList : 
		dfx.m_minorDamageFXList;

	return fx;
}

//-------------------------------------------------------------------------------------------------
const FieldParse* DamageFX::getFieldParse() const
{
	static const FieldParse myFieldParse[] = 
	{
		{ "AmountForMajorFX",						parseAmount,			NULL, 0 },
		{ "MajorFX",										parseMajorFXList,	NULL, 0 },
		{ "MinorFX",										parseMinorFXList,	NULL, 0 },
		{ "ThrottleTime",								parseTime,				NULL, 0 },
		{ "VeterancyAmountForMajorFX",	parseAmount,			TheVeterancyNames, 0 },
		{ "VeterancyMajorFX",						parseMajorFXList,	TheVeterancyNames, 0 },
		{ "VeterancyMinorFX",						parseMinorFXList,	TheVeterancyNames, 0 },
		{ "VeterancyThrottleTime",			parseTime,				TheVeterancyNames, 0 },
		{ 0, 0, 0,0 }
	};
	return myFieldParse;
}

//-------------------------------------------------------------------------------------------Static
static void parseCommonStuff(
	INI* ini, 
	ConstCharPtrArray names, 
	VeterancyLevel& vetFirst, 
	VeterancyLevel& vetLast, 
	DamageType& damageFirst, 
	DamageType& damageLast
)
{
	if (names)
	{
		vetFirst = (VeterancyLevel)INI::scanIndexList(ini->getNextToken(), names);
		vetLast = vetFirst;
	}
	else
	{
		vetFirst = LEVEL_FIRST;
		vetLast = LEVEL_LAST;
	}

	const char* damageName = ini->getNextToken();
	if (stricmp(damageName, "Default") == 0)
	{
		damageFirst = (DamageType)0;
		damageLast = (DamageType)(DAMAGE_NUM_TYPES - 1);
	}
	else
	{
		damageFirst = (DamageType)DamageTypeFlags::getSingleBitFromName(damageName);
		damageLast = damageFirst;
	}
}

//-------------------------------------------------------------------------------------------Static
/*static*/ void DamageFX::parseAmount( INI* ini, void* instance, void* /*store*/, const void* userData )
{
	DamageFX* self = (DamageFX*)instance;
	ConstCharPtrArray names = (ConstCharPtrArray)userData;

	VeterancyLevel vetFirst, vetLast;
	DamageType damageFirst, damageLast;
	parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);

	Real amt = INI::scanReal(ini->getNextToken());

	for (Int dt = damageFirst; dt <= damageLast; ++dt)
	{
		for (Int v = vetFirst; v <= vetLast; ++v)
		{
			self->m_dfx[dt][v].m_amountForMajorFX = amt;
		}
	}
}

//-------------------------------------------------------------------------------------------Static
/*static*/ void DamageFX::parseMajorFXList( INI* ini, void* instance, void* /*store*/, const void* userData )
{
	DamageFX* self = (DamageFX*)instance;
	ConstCharPtrArray names = (ConstCharPtrArray)userData;

	VeterancyLevel vetFirst, vetLast;
	DamageType damageFirst, damageLast;
	parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);

	ConstFXListPtr fx;
	INI::parseFXList(ini, NULL, &fx, NULL);

	for (Int dt = damageFirst; dt <= damageLast; ++dt)
	{
		for (Int v = vetFirst; v <= vetLast; ++v)
		{
			self->m_dfx[dt][v].m_majorDamageFXList = fx;
		}
	}
}

//-------------------------------------------------------------------------------------------Static
/*static*/ void DamageFX::parseMinorFXList( INI* ini, void* instance, void* /*store*/, const void* userData )
{
	DamageFX* self = (DamageFX*)instance;
	ConstCharPtrArray names = (ConstCharPtrArray)userData;

	VeterancyLevel vetFirst, vetLast;
	DamageType damageFirst, damageLast;
	parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);

	ConstFXListPtr fx;
	INI::parseFXList(ini, NULL, &fx, NULL);

	for (Int dt = damageFirst; dt <= damageLast; ++dt)
	{
		for (Int v = vetFirst; v <= vetLast; ++v)
		{
			self->m_dfx[dt][v].m_minorDamageFXList = fx;
		}
	}
}

//-------------------------------------------------------------------------------------------Static
/*static*/ void DamageFX::parseTime( INI* ini, void* instance, void* /*store*/, const void* userData )
{
	DamageFX* self = (DamageFX*)instance;
	ConstCharPtrArray names = (ConstCharPtrArray)userData;

	VeterancyLevel vetFirst, vetLast;
	DamageType damageFirst, damageLast;
	parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);

	UnsignedInt t;
	INI::parseDurationUnsignedInt(ini, NULL, &t, NULL);

	for (Int dt = damageFirst; dt <= damageLast; ++dt)
	{
		for (Int v = vetFirst; v <= vetLast; ++v)
		{
			self->m_dfx[dt][v].m_damageFXThrottleTime = t;
		}
	}
}

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
DamageFXStore::DamageFXStore()
{
	m_dfxmap.clear();
}

//-------------------------------------------------------------------------------------------------
DamageFXStore::~DamageFXStore()
{
	m_dfxmap.clear();
}

//-------------------------------------------------------------------------------------------------
const DamageFX *DamageFXStore::findDamageFX(AsciiString name) const
{
	NameKeyType namekey = TheNameKeyGenerator->nameToKey(name);
  DamageFXMap::const_iterator it = m_dfxmap.find(namekey);
  if (it == m_dfxmap.end()) 
	{
		return NULL;
	}
	else
	{
		return &(*it).second;
	}
}

//-------------------------------------------------------------------------------------------------
void DamageFXStore::init()
{
}

//-------------------------------------------------------------------------------------------------
void DamageFXStore::reset()
{
} 

//-------------------------------------------------------------------------------------------------
void DamageFXStore::update()
{
}

//-------------------------------------------------------------------------------------------------
/*static */ void DamageFXStore::parseDamageFXDefinition(INI* ini)
{

	const char *c = ini->getNextToken();
	NameKeyType key = TheNameKeyGenerator->nameToKey(c);
	DamageFX& dfx = TheDamageFXStore->m_dfxmap[key];
	dfx.clear();
	ini->initFromINI(&dfx, dfx.getFieldParse());
}