/**
 *	Mode melee
 */

#Extends "Modes/ShootMania/Base/ModeBase.Script.txt"

#Const	CompatibleMapTypes	"MeleeArena"
#Const	Version							"2016-04-15"
#Const	ScriptName					"Melee.Script.txt"

#Include "MathLib" as MathLib
#Include "TextLib" as TextLib
#Include "Libs/Nadeo/Message.Script.txt" as Message
#Include "Libs/Nadeo/Interface.Script.txt" as Interface
#Include "Libs/Nadeo/ShootMania/SM.Script.txt" as SM
#Include "Libs/Nadeo/ShootMania/Score.Script.txt" as Score
#Include "Libs/Nadeo/ShootMania/SpawnScreen.Script.txt" as SpawnScreen

// ---------------------------------- //
// Settings
// ---------------------------------- //
#Setting S_TimeLimit	600 as _("Time limit")		///< Time limit on a map
#Setting S_PointLimit	25	as _("Points limit")	///< Points limit on a map
#Setting S_HudModulePath "Nadeo/ShootMania/Melee/Hud.Module.Gbx" as "<hidden>"

#Const C_NbBots 0	///< Number of bots

#Const Description _("TYPE: Free for all\nOBJECTIVE: Hit your opponents to score points. The first player to reach the points limit or the player with the highest score at the end of the time limit wins the map.")

// ---------------------------------- //
// Globales variables
// ---------------------------------- //
declare Ident[]	G_SpawnsList;		///< Id of all the landmarks with a player spawn of the map
declare Ident	G_LatestSpawnId;	///< Id of the last landmark used

// ---------------------------------- //
// Extend
// ---------------------------------- //

***LogVersion***
***
MB_LogVersion(ScriptName, Version);
MB_LogVersion(SM::GetScriptName(), SM::GetScriptVersion());
MB_LogVersion(Score::GetScriptName(), Score::GetScriptVersion());
MB_LogVersion(Message::GetScriptName(), Message::GetScriptVersion());
MB_LogVersion(Interface::GetScriptName(), Interface::GetScriptVersion());
MB_LogVersion(SpawnScreen::GetScriptName(), SpawnScreen::GetScriptVersion());
***

// ---------------------------------- //
// Set rules
// ---------------------------------- //
***Rules***
***
declare ModeName = "Melee";
declare ModeRules = TextLib::Compose(_("$<%11. $>Hit your opponents to score points.\n$<%12. $>The first player to reach the points limit or the player with the highest score at the end of the time limit wins the map."), "$"^SpawnScreen::GetModeColor());
SpawnScreen::ResetRulesSection();
SpawnScreen::AddSubsection(_("Type"), _("Free for all"), 0.);
SpawnScreen::AddSubsection(_("Objectives"), ModeRules, 20.);
SpawnScreen::CreatePrettyRules(ModeName);
ModeStatusMessage = _("TYPE: Free for all\nOBJECTIVE: Hit a maximum of players to win the game.");
***

// ---------------------------------- //
// Server start
// ---------------------------------- //
***StartServer***
***
// ---------------------------------- //
// Set mode options
UseClans = False;

// ---------------------------------- //
// Create the rules
---Rules---

// ---------------------------------- //
// Initialize UI
SpawnScreen::CreateScores("Score.RoundPoints");
SpawnScreen::CreateMapInfo();
Interface::CreateRank();

// ---------------------------------- //
// Create the scores table
/*ST2::SetStyle("LibST_SMBaseSolo");
ST2::SetStyle("LibST_SMBasePoints");
MB_SetScoresTableStyleFromXml(S_ScoresTableStylePath);
ST2::Build("SM");*/
Hud_Load(S_HudModulePath);
if (Hud != Null && Hud.ScoresTable != Null) {
  Hud.ScoresTable.Scores_Sort(CModulePlaygroundScoresTable::EScoreSortOrder::SMPoints);
}
***

***InitMap***
***
declare PrevPointsLimit = -1;
***

// ---------------------------------- //
// Map start
// ---------------------------------- //
***StartMap***
***
Users_SetNbFakeUsers(C_NbBots, 0);
G_SpawnsList.clear();
G_LatestSpawnId = NullId;
Score::MatchBegin();
Score::RoundBegin();

SM::SetupDefaultVisibility();

// ---------------------------------- //
// Init bases
foreach (Base in MapBases) {
	Base.Clan = 0;
	Base.IsActive = True;
}

// ---------------------------------- //
// Init scores
MB_Sleep(1); ///< Allow the scores array to be sorted
foreach (Score in Scores) {
	declare Integer LastPoint for Score;
	LastPoint = 0;
}
declare LeadId = NullId;
if (Scores.existskey(0)) LeadId = Scores[0].User.Id;

// ---------------------------------- //
// New map sound
UIManager.UIAll.SendNotice("", CUIConfig::ENoticeLevel::MatchInfo, Null, CUIConfig::EAvatarVariant::Default, CUIConfig::EUISound::StartRound, 0);

StartTime = Now;
if (S_TimeLimit > 0) EndTime = StartTime + (S_TimeLimit * 1000);
else EndTime = -1;
UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;
declare CurrentTimeLimit = S_TimeLimit;
***

// ---------------------------------- //
// Play loop
// ---------------------------------- //
***PlayLoop***
***
foreach (Event, PendingEvents) {
	// ---------------------------------- //
	// On armor empty
	if (Event.Type == CSmModeEvent::EType::OnArmorEmpty) {
		if (Event.Shooter == Event.Victim || Event.Shooter == Null) {
			Score::RemovePoints(Event.Victim, 1);
		}
		XmlRpc::OnArmorEmpty(Event);
		PassOn(Event);
	} 
	// ---------------------------------- //
	// On hit
	else if (Event.Type == CSmModeEvent::EType::OnHit) {
		if (Event.Victim == Null) { //< Hit on shield
			PassOn(Event);
		} else if (Event.Shooter == Event.Victim) {
			Discard(Event);
		} else {
			declare Points = Event.Damage / 100;
			Score::AddPoints(Event.Shooter, Points);
			Event.ShooterPoints = Points;
			
			// ---------------------------------- //
			// Play sound and notice if someone is close to win
			if (Event.Shooter != Null && Event.Shooter.Score != Null) {	
				declare LastPoint for Event.Shooter.Score = 0;
				declare Gap = S_PointLimit - Event.Shooter.Score.RoundPoints;
				if (Gap > 0 && Gap <= 3) {
					declare Variant = 3 - Gap;
					declare Msg = "";
					if (Gap > 1) Msg = TextLib::Compose(_("$<%1$> is %2 points from victory!"), Event.Shooter.User.Name, TextLib::ToText(Gap));
					else Msg = TextLib::Compose(_("$<%1$> is 1 point from victory!"), Event.Shooter.User.Name);
					Message::SendBigMessage(Msg, 3000, 2, CUIConfig::EUISound::TieBreakPoint, Variant);
				} else if (Gap <= 0) {
					Message::SendBigMessage(
						TextLib::Compose(_("$<%1$> gets the final hit!"), Event.Shooter.User.Name),
						3000, 3, CUIConfig::EUISound::VictoryPoint, 0
					);
				} else {
					declare SoundGap = S_PointLimit / 5;
					if (SoundGap < 5) SoundGap = 5;
					if (Event.Shooter.Score.RoundPoints / SoundGap > LastPoint) {
						LastPoint = Event.Shooter.Score.RoundPoints / SoundGap;
						declare Msg = TextLib::Compose(
							"$666%1 : $fff%2 / %3", _("Score"), TextLib::ToText(Event.Shooter.Score.RoundPoints), TextLib::ToText(S_PointLimit)
						);
						declare Variant = ((Event.Shooter.Score.RoundPoints / SoundGap) - 1);
						Message::SendBigMessage(Event.Shooter, Msg, 3000, 0, CUIConfig::EUISound::ScoreProgress, Variant);
					}
				}
			}
			XmlRpc::OnHit(Event);
			PassOn(Event);
		}
	} 
	// ---------------------------------- //
	// On player request respawn
	else if (Event.Type == CSmModeEvent::EType::OnPlayerRequestRespawn) {
		Score::RemovePoints(Event.Player, 1);
		XmlRpc::OnPlayerRequestRespawn(Event);
		PassOn(Event);
	} 
	// ---------------------------------- //
	// Others
	else {
		PassOn(Event);
	}
}	

// ---------------------------------- //
// Spawn players
foreach (Player in Players) {
	if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned && !Player.RequestsSpectate) {
		MeleeSpawnPlayer(Player);
	}
}

// ---------------------------------- //
// Play sound and notice if someone is taking the lead
if (Scores.existskey(0) && Scores[0].User.Id != LeadId) {
	LeadId = Scores[0].User.Id;
	Message::SendBigMessage(TextLib::Compose(_("$<%1$> takes the lead!"), Scores[0].User.Name), 3000, 1, CUIConfig::EUISound::PhaseChange, 1);
}

// ---------------------------------- //
// Server info change
if (PrevPointsLimit != S_PointLimit) {
	PrevPointsLimit = S_PointLimit;
	//ST2::SetFooterText(TextLib::Compose("%1 "^S_PointLimit, _("Points limit : ")));
	if (Hud != Null) Hud.ScoresTable.SetFooterText(TextLib::Compose("%1 "^S_PointLimit, _("Points limit : ")));
}

// ---------------------------------- //
// Update time limit
if (CurrentTimeLimit != S_TimeLimit) {
	CurrentTimeLimit = S_TimeLimit;
	if (S_TimeLimit > 0) EndTime = StartTime + (S_TimeLimit * 1000);
	else EndTime = -1;
}

Message::Loop();

// ---------------------------------- //
// victory conditions
declare IsMatchOver = False;
if (S_TimeLimit > 0 && Now > EndTime) IsMatchOver = True;
if (S_PointLimit > 0) {
	foreach (Player in Players) {
		if (Player.Score != Null && Player.Score.RoundPoints >= S_PointLimit) IsMatchOver = True;
	}
}

if (IsMatchOver) MB_StopMap = True;
***

// ---------------------------------- //
// Map end
// ---------------------------------- //
***EndMap***
***
EndTime = -1;
Score::RoundEnd();
Score::MatchEnd(True);

// ---------------------------------- //
// End match sequence
declare CUser Winner <=> Null;
declare MaxPoints = 0;
foreach (Score in Scores) {
	if (Score.Points > MaxPoints) {
		MaxPoints = Score.Points;
		Winner <=> Score.User;
	} else if (Score.Points == MaxPoints) {
		Winner <=> Null;
	}
}
foreach (Player in Players) {
	if (Player.User != Winner) UnspawnPlayer(Player);
}
XmlRpc::ScoresReady();

MB_Sleep(1000);
StartTime = -1;
Message::CleanBigMessages();
UIManager.UIAll.BigMessageSound = CUIConfig::EUISound::EndRound;
UIManager.UIAll.BigMessageSoundVariant = 0;
if (Winner != Null) {
	UIManager.UIAll.BigMessage = TextLib::Compose(_("$<%1$> wins the match!"), Winner.Name);
} else {
	UIManager.UIAll.BigMessage = _("|Match|Draw");
}
MB_Sleep(2000);
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
MB_Sleep(5000);

UIManager.UIAll.UISequence = CUIConfig::EUISequence::Podium;
while(!UIManager.UIAll.UISequenceIsCompleted) {
	MB_Yield();
}

UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal;
UIManager.UIAll.BigMessage = "";
***

// ---------------------------------- //
// Server end
// ---------------------------------- //
***EndServer***
***
Interface::DestroyRank();
SpawnScreen::DestroyRules();
SpawnScreen::DestroyScores();
SpawnScreen::DestroyMapInfo();
***

// ---------------------------------- //
// Functions
// ---------------------------------- //

/* ------------------------------------- */
/** Spawn a player
 *
 * @param	_Player		The player to spawn
 */
Void MeleeSpawnPlayer(CSmPlayer _Player) {
	if (G_SpawnsList.count == 0) {
		foreach (MapLandmark in MapLandmarks_PlayerSpawn) G_SpawnsList.add(MapLandmark.Id);
	}
	
	declare SpawnId = NullId;
	while (True) {
		SpawnId = G_SpawnsList[MathLib::Rand(0, G_SpawnsList.count - 1)];
		if (SpawnId != G_LatestSpawnId) break;
		if (G_SpawnsList.count == 1) break;
	}
	G_LatestSpawnId = SpawnId;
	SM::SpawnPlayer(_Player, 0, MapLandmarks_PlayerSpawn[SpawnId].PlayerSpawn);
	declare Removed = G_SpawnsList.remove(SpawnId);
}