untyped
global function PowerUps_Init

struct {
	array<entity> powerupSpawns
} file

void function PowerUps_Init()
{
	SH_PowerUp_Init()
		
	AddSpawnCallbackEditorClass( "script_ref", "script_power_up_other", AddPowerupSpawn )
	AddCallback_OnTouchHealthKit( "item_powerup", OnPowerupCollected )
	AddCallback_GameStateEnter( eGameState.Prematch, RespawnPowerups )
}

void function AddPowerupSpawn( entity spawnpoint )
{
	file.powerupSpawns.append( spawnpoint )
}

void function RespawnPowerups()
{
	foreach ( entity spawnpoint in file.powerupSpawns )
	{
		PowerUp powerupDef = GetPowerUpFromItemRef( expect string( spawnpoint.kv.powerUpType ) )
		thread PowerupSpawnerThink( spawnpoint, powerupDef )
	}
}

void function PowerupSpawnerThink( entity spawnpoint, PowerUp powerupDef )
{
	svGlobal.levelEnt.EndSignal( "CleanUpEntitiesForRoundEnd" )

	entity base = CreatePropDynamic( powerupDef.baseModel, spawnpoint.GetOrigin(), spawnpoint.GetAngles(), 2 )
	OnThreadEnd( function() : ( base ) 
	{
		base.Destroy()
	})
	
	while ( true )
	{
		if ( !powerupDef.spawnFunc() )
			return
						
		entity powerup = CreateEntity( "item_powerup" )
		
		powerup.SetOrigin( base.GetOrigin() + powerupDef.modelOffset )
		powerup.SetAngles( base.GetAngles() + powerupDef.modelAngles )
		powerup.SetValueForModelKey( powerupDef.model )
		powerup.s.powerupRef <- powerupDef.itemRef // this needs to be done before dispatchspawn since OnPowerupCollected will run as soon as we call dispatchspawn if there's a player on battery as it spawns
	
		DispatchSpawn( powerup )
		
		// unless i'm doing something really dumb, this all has to be done after dispatchspawn to get the powerup to not have gravity
		powerup.StopPhysics()
		powerup.SetOrigin( base.GetOrigin() + powerupDef.modelOffset )
		powerup.SetAngles( base.GetAngles() + powerupDef.modelAngles )
		
		powerup.SetModel( powerupDef.model )
		
		PickupGlow glow = CreatePickupGlow( powerup, powerupDef.glowColor.x.tointeger(), powerupDef.glowColor.y.tointeger(), powerupDef.glowColor.z.tointeger() )
		glow.glowFX.SetOrigin( spawnpoint.GetOrigin() ) // want the glow to be parented to the powerup, but have the position of the spawnpoint
		
		OnThreadEnd( function() : ( powerup )
		{
			if ( IsValid( powerup ) )
				powerup.Destroy()
		})
		
		powerup.WaitSignal( "OnDestroy" )
		wait powerupDef.respawnDelay
	}
}

bool function OnPowerupCollected( entity player, entity healthpack )
{
	PowerUp powerup = GetPowerUpFromItemRef( expect string( healthpack.s.powerupRef ) )
	
	if ( powerup.titanPickup == player.IsTitan() )
	{
		// hack because i couldn't figure out any other way to do this without modifying sh_powerup
		// ensure we don't kill the powerup if it's a battery the player can't pickup
		if ( powerup.index == ePowerUps.titanTimeReduction || powerup.index == ePowerUps.LTS_TitanTimeReduction )
		{
			if ( player.IsTitan() )
				return false
			
			if ( PlayerHasMaxBatteryCount( player ) )
				return false
			
			// this is seemingly innacurate to what fra actually did, but for whatever reason embarking with >1 bat crashes in vanilla code
			// so idk this is easier 
			if ( GAMETYPE == FREE_AGENCY && ( IsValid( player.GetPetTitan() ) || IsTitanAvailable( player ) ) && GetPlayerBatteryCount( player ) > 0 )
				return false
		}
	
		// idk why the powerup.destroyFunc doesn't just return a bool? would mean they could just handle stuff like this in powerup code
		powerup.destroyFunc( player )
		return true // destroys the powerup
	}
	
	return false // keeps powerup alive
}