/*!
 * @file Creature.cpp
 * @brief ゲーム中のクリーチャーを定義する
 * @date 2013/12/10
 * @author Deskull
 * 2013 Sikabane Works.
 */

#include "stdafx.h"
#include "Creature.h"

namespace Deeangband
{

SOUL Creature::soulLevel[CREATURE_MAX_LEVEL] =
{
//  0    // ロスト域
	50,	 // Lv1デフォルト基準ソウル
	100, // Lv2到達ソウル
	130, // :
	180,
	260,
	360,
	480,
	630,
	880,
	1280, // Lv10到達ソウル
	1700,
	2200,
	2800,
	3600,
	4600,
	6100,
	8800,
	10600,
	12600,
	15000, // Lv20到達ソウル
	18000,
	24000,
	35000,
	50000,
	75000,
	100000,
	150000,
	200000,
	275000,
	350000, // Lv30到達ソウル
	450000,
	550000,
	700000,
	1000000,
	1500000,
	2100000,
	2700000,
	3600000,
	4700000,
	6000000, // Lv40到達ソウル
	7500000,
	9000000,
	11500000,
	14500000,
	19000000,
	26000000,
	33000000,
	45000000,
	60000000,
	90000000, // Lv50到達ソウル
	150000000,
	230000000,
	400000000,
	620000000,
	880000000,
	1150000000,
	1500000000,
	3200000000,
	6400000000,
	11100000000, // Lv60到達ソウル
};

Creature::Creature(Species *speciesPtr, ID id, TAG tag, GameWorld *gameWorld) : GameElement(id, tag, gameWorld)
{
	SetDammyData();
	SetSpeciesData(speciesPtr);
	return;
}

Creature::Creature(ID id, TAG tag, GameWorld *gameWorld) : GameElement(id, tag, gameWorld)
{
	SetDammyData();
	return;
}

Creature::Creature(void)
{
	SetDammyData();
}

Creature::~Creature(void)
{
}

void Creature::SetSpeciesData(Species *speciesPtr)
{
	std::normal_distribution<int> distHeight(speciesPtr->GetAverageHeight(), speciesPtr->GetAverageHeight() / 20);
	std::normal_distribution<int> distWeight(speciesPtr->GetAverageWeight(), speciesPtr->GetAverageWeight() / 20);
	this->name = speciesPtr->GetName();
	this->currentSoul = this->maxSoul = speciesPtr->GetBaseSoul();
	this->currentFeed = speciesPtr->GetBaseFeed() * 3 / 4;
	this->maxFeed = speciesPtr->GetBaseFeed();
	this->weight = distWeight(Dice::mt);
	this->height = distHeight(Dice::mt);
	this->campID = CAMP_FLOOR_NATIVE;
}

void Creature::SetDammyData(void)
{
	this->name = "ダミー";
	this->currentMp = this->maxMp = this->maxMaxMp = 10;
	this->currentSoul = this->maxSoul = Creature::soulLevel[0];
	this->currentDiscipilne.SetPoint(0, 0, 0, 0, 0);
	this->height = 160;
	this->weight = 50;
	this->divineLevel = -1;
	savings.Set(10, 10, 10);
	this->currentFeed = this->maxFeed = 20000;

	currentStatus.Set(8, 8, 8, 8, 8, 8);
	maxStatus.Set(8, 8, 8, 8, 8, 8);
	maxMaxStatus.Set(20, 20, 20, 20, 20, 20);

	this->calcHPTable();
	this->currentHp = this->maxHp = this->GetNorMaxHP();

	this->skillExpList.clear();

	this->turn = 0;
	this->campID = CAMP_FLOOR_NATIVE;

	this->floorID = -1;
	this->position.Set(0, 0);

	this->itemList.clear();
	this->skillExpList.clear();
}

bool Creature::IsAlive(void)
{
	return this->currentHp > 0 && this->currentSoul > 0;
}

bool Creature::TakeEffect(Effect *effectPtr, POWER amount)
{
	if(effectPtr) this->calcHP(-amount);
	return false;
}

void Creature::die(void)
{
}

int Creature::move(ID floorID, int x, int y)
{
	this->floorID = floorID;
	this->position.Set(x, y);
	return 0;
}

int Creature::move(ID floorID, Coordinates coord)
{
	this->move(floorID, coord.GetX(), coord.GetY());
	return 0;
}

void Creature::calcHPTable(void)
{
	int lev;
	HP hitDice = this->GetSize();
	HP minLine = hitDice * (CREATURE_MAX_LEVEL + 4) * 70 / 100;
	HP maxLine = hitDice * (CREATURE_MAX_LEVEL + 4) * 130 / 100;

	do
	{
		this->hpTable[0] = hitDice + Dice::Cast(3, hitDice);
		for(lev = 0; lev < CREATURE_MAX_LEVEL - 1; lev++)
		{
			this->hpTable[lev + 1] = this->hpTable[lev] + Dice::Rand1(hitDice);
		}
	}
	while(this->hpTable[CREATURE_MAX_LEVEL - 1] < minLine && this->hpTable[CREATURE_MAX_LEVEL - 1] > maxLine);
}


void Creature::calcHP(HP amount)
{
	this->currentHp += amount;
	if(this->currentHp > this->maxHp) this->currentHp = this->maxHp;
	if(this->currentHp < 1) this->die();
}

void Creature::calcMP(MP amount)
{
	this->currentMp += amount;
	if(this->currentMp > this->maxMp) this->currentMp = this->maxMp;
	if(this->currentMp < 0) this->currentMp = 0;
}

void Creature::setHP(HP amount)
{
	this->currentHp = amount;
	if(this->currentHp < this->maxHp) this->currentHp = this->maxHp;
	if(this->currentHp < 1) this->die();
}

void Creature::setMP(MP amount)
{
	this->currentMp += amount;
	if(this->currentMp > this->maxMp) this->currentMp = this->maxMp;
	if(this->currentMp < 0) this->currentMp = 0;
}

NAME Creature::GetName(void)
{
	return this->name;
}

LEVEL Creature::GetLevel(void)
{
	LEVEL level;
	if(this->currentSoul <= 0) return 0;
	if(this->currentSoul < Creature::soulLevel[1]) return 1;
	for(level = 2; level < CREATURE_MAX_LEVEL; level++)
	{
		if(Creature::soulLevel[level - 1] > this->currentSoul) return level;
	}
	return level;
}

HP Creature::GetCurHP(void)
{
	return this->currentHp;
}

HP Creature::GetMaxHP(void)
{
	return this->maxHp;
}

HP Creature::GetNorMaxHP(void)
{
	return this->hpTable[this->GetLevel()];
}

MP Creature::GetCurMP(void)
{
	return this->currentMp;
}

MP Creature::GetMaxMP(void)
{
	return this->maxMp;
}

MP Creature::GetNorMaxMP(void)
{
	return this->maxMaxMp;
}

AC Creature::GetArmorSaving(void)
{
	return this->savings.GetArmor();
}

EV Creature::GetEvasionSaving(void)
{
	return this->savings.GetEvasion();
}

VO Creature::GetVolitionSaving(void)
{
	return this->savings.GetVolition();
}

SOUL Creature::GetCurrentSoul(void)
{
	return this->currentSoul;
}

SOUL Creature::GetMaxSoul(void)
{
	return this->maxSoul;
}

BASE_STATUS Creature::GetCurrentStatus(CREATURE_STATUS stat)
{
	return currentStatus.Get(stat);
}

BASE_STATUS Creature::GetMaxStatus(CREATURE_STATUS stat)
{
	return maxStatus.Get(stat);
}

BASE_STATUS Creature::GetMaxMAXStatus(CREATURE_STATUS stat)
{
	return maxMaxStatus.Get(stat);
}

DISCIPLINE_POINT Creature::GetDiscipilnePoint(DISCIPLINE_TYPE typ)
{
	return this->currentDiscipilne.GetPoint(typ);
}

DISCIPLINE_POINT Creature::GetDiscipilneRank(DISCIPLINE_TYPE typ)
{
	return this->currentDiscipilne.GetRank(typ);
}

HEIGHT Creature::GetHeight(void)
{
	return this->height;
}

WEIGHT Creature::GetWeight(void)
{
	return this->weight;
}

BODY_SIZE Creature::GetSize(void)
{
	return (BODY_SIZE)(sqrt((double)this->height * (double)this->weight) / 10.0f);
}

void Creature::GainSoul(SOUL amount)
{
	SOUL diff;
	if(this->currentSoul < this->maxSoul)
	{
		diff = this->maxSoul - this->currentSoul;
		if(diff < amount)
		{
			this->maxSoul += diff / 20;
			this->currentSoul = this->maxSoul;
			amount -= diff;
		}
		else
		{
			this->maxSoul += amount / 20;
			this->currentSoul += amount;
			amount = 0;
		}
	}
	this->currentSoul += amount;
	this->maxSoul += amount;
}

void Creature::LostSoul(SOUL amount)
{
	this->currentSoul -= amount;
	this->maxSoul -= amount / 20;
	if(this->currentSoul < 1) this->die();
}

LEVEL Creature::GetDivineLevel(void)
{
	return this->divineLevel;
}

FEED Creature::GetCurrentFeedPoint(void)
{
	return this->currentFeed;
}
	
FEED Creature::GetMaxFeedPoint(void)
{
	return this->maxFeed;
}

void Creature::TurnProcess(void)
{
}

void Creature::TimeProcess(void)
{
	this->currentFeed -= 10;
}

TURN Creature::GetTurn(void)
{
	return this->turn;
}

SKILL_EXP Creature::GetSkillExp(TAG tag)
{
	if(this->skillExpList.count(tag)) return this->skillExpList[tag];
	else return 0;
}

ID Creature::GetCampID(void)
{
	return this->campID;
}

void Creature::XMLSave(std::string filename)
{
	Creature creature = *this;
	std::ofstream ofs(filename);
    assert(ofs);
    boost::archive::xml_oarchive oa(ofs);
	oa << BOOST_SERIALIZATION_NVP(creature);
}

}