global function GamemodeGG_Init

void function GamemodeGG_Init()
{
	SetSpawnpointGamemodeOverride( FFA )

	SetShouldUseRoundWinningKillReplay( true )
	ClassicMP_ForceDisableEpilogue( true )
	SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period
	SetWeaponDropsEnabled( false )
	Riff_ForceTitanAvailability( eTitanAvailability.Never )
	Riff_ForceBoostAvailability( eBoostAvailability.Disabled )

	AddCallback_OnPlayerRespawned( OnPlayerRespawned )
	AddCallback_OnPlayerKilled( OnPlayerKilled )
	AddCallback_OnClientDisconnected( OnPlayerDisconnected )

	AddCallback_GameStateEnter( eGameState.WinnerDetermined, OnWinnerDetermined )

	AddCallback_GGEarnMeterFull( OnGGEarnMeterFilled )
}

void function OnPlayerDisconnected(entity player)
{
	// resetting a player's score when they leave prevents a new player to join the leaver's team and having the previous player's points
	AddTeamScore( player.GetTeam(), -GameRules_GetTeamScore( player.GetTeam() ) )
}

void function OnPlayerRespawned( entity player )
{
	UpdateLoadout( player )
	thread OnPlayerRespawned_Threaded( player )
}

void function OnPlayerRespawned_Threaded( entity player )
{
	// bit of a hack, need to rework earnmeter code to have better support for completely disabling it
	// rn though this just waits for earnmeter code to set the mode before we set it back
	WaitFrame()
	if ( IsValid( player ) )
		PlayerEarnMeter_SetMode( player, eEarnMeterMode.DISABLED )
}

void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
{
	if ( !victim.IsPlayer() || !attacker.IsPlayer() || GetGameState() != eGameState.Playing )
		return

	if ( attacker == victim ) // suicide
	{
		string message = victim.GetPlayerName() + " committed suicide."
		foreach ( entity player in GetPlayerArray() )
			SendHudMessage( player, message, -1, 0.4, 255, 0, 0, 0, 0, 3, 0.15 )

		DeRank( victim )
	}
	else
	{
		if ( DamageInfo_GetDamageSourceIdentifier( damageInfo ) != eDamageSourceId.melee_pilot_emptyhanded )
		{
			float percentageReward = GetCurrentPlaylistVarFloat( "gg_kill_reward", 1.0 )
			string scoreEvent = "KillPilot"

			if ( DamageInfo_GetDamageSourceIdentifier( damageInfo ) == eDamageSourceId.human_execution )
			{
				string message = victim.GetPlayerName() + " got executed."
				foreach ( entity player in GetPlayerArray() )
					SendHudMessage( player, message, -1, 0.4, 255, 0, 0, 0, 0, 3, 0.15 )

				DeRank( victim )

				percentageReward = GetCurrentPlaylistVarFloat( "gg_execution_reward", 1.0 )
				scoreEvent = "Execution"
			}

			Reward( attacker, victim, scoreEvent, percentageReward )
		}
	}

	table<int, bool> alreadyAssisted
	foreach( DamageHistoryStruct attackerInfo in victim.e.recentDamageHistory )
	{
		if ( !IsValid( attackerInfo.attacker ) || !attackerInfo.attacker.IsPlayer() || attackerInfo.attacker == victim )
			continue

		if ( attackerInfo.attacker != attacker && !( attackerInfo.attacker.GetEncodedEHandle() in alreadyAssisted ) )
		{
			if ( attackerInfo.damageSourceId != eDamageSourceId.melee_pilot_emptyhanded ) {
				alreadyAssisted[attackerInfo.attacker.GetEncodedEHandle()] <- true

				Reward( attackerInfo.attacker, victim, "PilotAssist", GetCurrentPlaylistVarFloat( "gg_assist_reward", 0.0 ) )
			}
		}
	}
}

void function Reward( entity player, entity victim, string scoreEvent, float percentageReward )
{
	percentageReward = clamp(percentageReward, 0.0, 1.0)
	Sv_GGEarnMeter_AddPercentage( player, percentageReward )
	Remote_CallFunction_NonReplay( player, "ServerCallback_ScoreEvent", GetScoreEvent(scoreEvent).eventId, 0, eEventDisplayType.MEDAL, victim.GetEncodedEHandle(), 0.0, percentageReward)
}

void function DeRank( entity player )
{
	Sv_GGEarnMeter_SetPercentage( player, 0.0 )

	if ( GameRules_GetTeamScore( player.GetTeam() ) != 0 )
	{
		AddTeamScore( player.GetTeam(), -1 ) // get absolutely fucking destroyed lol
		player.AddToPlayerGameStat( PGS_ASSAULT_SCORE, -1 )
		UpdateLoadout( player )
	}
}

bool function OnGGEarnMeterFilled( entity player )
{
	AddTeamScore( player.GetTeam(), 1 )
	player.AddToPlayerGameStat( PGS_ASSAULT_SCORE, 1 )
	UpdateLoadout( player )

	int currentScore = GameRules_GetTeamScore( player.GetTeam() )
	int maxScore = GetGunGameWeapons().len()

	return currentScore >= maxScore
}

void function UpdateLoadout( entity player )
{
	// todo: honestly, this should be reworked to use PilotLoadoutDefs instead of directly modifying weapons and shit

	int currentWeaponIndex = GameRules_GetTeamScore( player.GetTeam() )
	array<GunGameWeapon> weapons = GetGunGameWeapons()

	if ( currentWeaponIndex >= weapons.len() )
		currentWeaponIndex = weapons.len() - 1

	if ( currentWeaponIndex > weapons.len() - 3 ) // play end of game music for special or last few weapons
		PlayMusicToAll( eMusicPieceID.LEVEL_LAST_MINUTE ) // this *shouldn't* overlap if done multiple times

	GunGameWeapon weapon = weapons[ currentWeaponIndex ]

	foreach ( entity weapon in player.GetMainWeapons() )
		player.TakeWeaponNow( weapon.GetWeaponClassName() )

	foreach ( entity weapon in player.GetOffhandWeapons() )
		player.TakeWeaponNow( weapon.GetWeaponClassName() )

	if ( weapon.offhandSlot != -1 )
	{
		player.GiveOffhandWeapon( weapon.weapon, weapon.offhandSlot, weapon.mods )

		player.GiveOffhandWeapon( "melee_pilot_emptyhanded", OFFHAND_MELEE, [ "allow_as_primary" ] )
		player.SetActiveWeaponByName( "melee_pilot_emptyhanded" ) // Let the player use the melee in the primary slot, and then force equip it. Effectively gives them an empty hand.
	}
	else
	{
		player.GiveWeapon( weapon.weapon, weapon.mods )

		player.GiveOffhandWeapon( "melee_pilot_emptyhanded", OFFHAND_MELEE )
	}
}

void function OnWinnerDetermined()
{
	SetRespawnsEnabled( false )
	SetKillcamsEnabled( false )
}
