object	AIBalls;
aref	Grapes, Knippels, Balls, Bombs;

// ------------------- Current Ball Info -------------------
// AIBalls.CurrentBallType = GOOD_BALLS, GOOD_KNIPPELS, ... etc
// AIBalls.CurrentBallCannonType = CANNON_TYPE_CULVERINE_LBS4, CANNON_TYPE_CULVERINE_LBS8, ... etc
// AIBalls.CurrentBallDistance = distance from start
// AIBalls.CurrentMaxBallDistance = max distance for balls

void DeleteBallsEnvironment()
{
	DeleteClass(&AIBalls);

	DelEventHandler(BALL_WATER_HIT, "Ball_WaterHitEvent");
	DelEventHandler(BALL_ISLAND_HIT, "Ball_IslandHit");
	DelEventHandler(BALL_FLY_UPDATE, "Ball_OnFlyUpdate");
	DelEventHandler(BALL_FORT_HIT, "Ball_FortHit");
	DelEventHandler(BALL_FLY_NEAR_CAMERA, "Ball_FlyNearCamera");
}

void CreateBallsEnvironment()
{
	CreateEntity(&AIBalls, "AIBalls");
	LayerAddObject(SEA_EXECUTE, &AIBalls, -1);
	LayerAddObject(SEA_REALIZE, &AIBalls, 65532);

	AIBalls.CurrentBallCannonType = -1;
	AIBalls.CurrentBallDistance = 0.0;
	AIBalls.CurrentMaxBallDistance = 0.0;
	AIBalls.BallFlySoundDistance = 15.0;
	AIBalls.BallFlySoundStereoMultiplyer = 2.0;

	AIBalls.SpeedMultiply = 3.0;
	AIBalls.Texture = "AllBalls.tga.tx";
	AIBalls.SubTexX = 2;
	AIBalls.SubTexY = 2;

	makearef(Grapes,AIBalls.Balls.Grapes);
	makearef(Knippels,AIBalls.Balls.Knippels);
	makearef(Balls,AIBalls.Balls.Balls);
	makearef(Bombs,AIBalls.Balls.Bombs);

	// Bombs
	Bombs.SubTexIndex = 0;		Bombs.Size = 0.3;		Bombs.GoodIndex = GOOD_BOMBS;
	Bombs.Particle = "bomb_smoke";

	// Grapes
	Grapes.SubTexIndex = 1;		Grapes.Size = 0.2;		Grapes.GoodIndex = GOOD_GRAPES;

	// Balls
	Balls.SubTexIndex = 2;		Balls.Size = 0.2;		Balls.GoodIndex = GOOD_BALLS;

	// Knippels
	Knippels.SubTexIndex = 3;	Knippels.Size = 0.2;	Knippels.GoodIndex = GOOD_KNIPPELS;

	AIBalls.isDone = 1;

	// cheat - fire from camera
	AIBalls.FireBallFromCamera = true;

	SetEventHandler(BALL_WATER_HIT, "Ball_WaterHitEvent", 0);
	SetEventHandler(BALL_ISLAND_HIT, "Ball_IslandHit", 0);
	SetEventHandler(BALL_FLY_UPDATE, "Ball_OnFlyUpdate", 0);
	SetEventHandler(BALL_FORT_HIT, "Ball_FortHit", 0);
	SetEventHandler(BALL_FLY_NEAR_CAMERA, "Ball_FlyNearCamera", 0);
}

void Ball_FlyNearCamera()
{
	float x = GetEventData();
	float y = GetEventData();
	float z = GetEventData();

	Play3DSound("fly_ball", x, y, z);
}

int ballNumber;

void Ball_AddBall(aref aCharacter, float fX, float fY, float fZ, float fSpeedV0, float fDirAng, float fHeightAng, float fCannonDirAng, float fMaxFireDistance)
{
	int iCannonType = sti(aCharacter.Ship.Cannons.Type);
	ref rCannon = GetCannonByType(iCannonType);
	float fCannonHeightMultiply = stf(rCannon.HeightMultiply);
	bool BowChaserGreekFire = false; // PB: Queen Anne's Revenge's Bow Chasers

	EntityUpdate(0);
	AIBalls.CannonType = iCannonType;
	AIBalls.x = fX;
	AIBalls.y = fY;
	AIBalls.z = fZ;
	AIBalls.CharacterIndex    = aCharacter.Index;
	AIBalls.Type = Goods[sti(aCharacter.Ship.Cannons.Charge.Type)].Name;
	AIBalls.HeightMultiply    = fCannonHeightMultiply;
	AIBalls.SizeMultiply      = rCannon.SizeMultiply;
	AIBalls.TimeSpeedMultiply = rCannon.TimeSpeedMultiply;
	AIBalls.MaxFireDistance   = fMaxFireDistance;

	float fTempDispersionY = Degree2Radian(15.0);
	float fTempDispersionX = Degree2Radian(5.0);

	//float fDamage2Cannons = 100.0;

    float fAccuracy = 1.2 - stf(aCharacter.TmpSkill.Accuracy);

	float fCannons = stf(aCharacter.TmpSkill.Cannons)*10;

	fCannons = 15.0 + MOD_SKILL_ENEMY_RATE - fCannons;

	if (fCannons > 0.0 && RealShips[sti(aCharacter.ship.type)].BaseName != "fort") // fix
	{
		if (fCannons > rand(100))
		{
            fCannons = (rand(4) + 2.0*(1.65 - stf(aCharacter.TmpSkill.Cannons))) * 10;
			SendMessage(&AISea, "laffff", AI_MESSAGE_CANNONS_BOOM_CHECK, aCharacter, fCannons, fx, fy, fz);  // fDamage2Cannons
		}
	}

	AIBalls.Dir = fDirAng + fK * fTempDispersionY * (frnd() - 0.5);
	AIBalls.SpdV0 = fSpeedV0 + fAccuracy * (10.0 * fTempDispersionY) * (frnd() - 0.5);
	AIBalls.Ang = fHeightAng + fAccuracy * (fTempDispersionX) * (frnd() - 0.5);

	AIBalls.Event = "";

	EntityUpdate(1);
	AIBalls.Add = "";
	
	// PB: Queen Anne's Revenge's Bow Chasers -->
		if(BowChaserGreekFire)
		{
			CreateParticleSystem("qar_fire", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 1);
			AIBalls.GreekFire = true;
			PlayStereoSound("objects\shipcharge\greek_fire.wav");
		}
		// PB: Queen Anne's Revenge's Bow Chasers <--
		else
		{
			//CreateParticleSystem("cancloud", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 12); // cannon smoke 100% redone by Merciless Mark
			// NK change the names to KNBcancloud*
			CreateParticleSystem("canfire2", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 1); // per Mehrunes's original idea, redone completely by Merciless Mark
			AIBalls.GreekFire = false;

			// Erwin Lindemann -->
			switch (USE_PARTICLES_CANNONS)	// Switch smoke quantity according to advanced options for particles
			{
				case 0: // None (Stock POTC, less smoke)
					PostEvent("CreateParticleSystemPost", 200, "sffffffl", "cancloud_stock", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 20);
				break;
				case 1: // Full
					PostEvent("CreateParticleSystemPost", 50, "sffffffl", "MMcancloud_Light", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 20);
					PostEvent("CreateParticleSystemPost", 60, "sffffffl", "MMcancloud2_Light", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 20);
				break;
				case 2: // Enhanced! High-End Machine Recommended!
					PostEvent("CreateParticleSystemPost", 50, "sffffffl", "KNBcancloud", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 20); // Post delay reduced to synchronize the fire and smoke better
					PostEvent("CreateParticleSystemPost", 60, "sffffffl", "KNBcancloud2", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 20); // Dirty smoke
					CreateParticleSystem("cannon_embers", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 20); // Gunpowder embers
				break;
			}
			// Erwin Lindemann <--

			if(JRH_GUNSOUNDS && CheckAttribute(rCannon,"sound")) // NK 05-05-03 add toggle and check.
			{
				Play3DSound(rCannon.sound, fX, fY, fZ);
			} else {
				Play3DSound("cannon_fire", fX, fY, fZ);
			}
		}
	} else {
		EntityUpdate(0);
		AIBalls.x = fX;
		AIBalls.y = fY;
		AIBalls.z = fZ;
		AIBalls.CharacterIndex = aCharacter.Index;
		fTempDispersionY = Degree2Radian(10.0);
		fTempDispersionX = Degree2Radian(5.0);

		fAccuracy = 1.2 - stf(aCharacter.TmpSkill.Accuracy);
		float fK = Bring2Range(0.5, 1.2, 0.2, 1.2, fAccuracy);
		AIBalls.Type = Goods[sti(aCharacter.Ship.Cannons.Charge.Type)].Name;
		AIBalls.Dir = fDirAng + fK * fTempDispersionY * (frnd() - 0.5);
		AIBalls.SpdV0 = fSpeedV0 + fAccuracy * (10.0 * fTempDispersionY) * (frnd() - 0.5);
		AIBalls.Ang = fHeightAng + fAccuracy * (fTempDispersionX) * (frnd() - 0.5);
		AIBalls.MaxFireDistance   = fMaxFireDistance;
		AIBalls.Event = "";
		/*if (sti(rCharacter.index) == GetMainCharacterIndex())
		{
			if (ballNumber == 1) { AIBalls.Event = BALL_FLY_UPDATE; }
			ballNumber++;
		}*/

		EntityUpdate(1);
		AIBalls.Add = "";

		CreateParticleSystem("canfire", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 1); // NK 04-09-21 per Mehrunes's idea
		//CreateParticleSystem("cancloud", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 12); // NK 04-09-21 make smoke last longer
		PostEvent("CreateParticleSystemPost", 300, "sffffffl", "cancloud_stock", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 20); // now done as post to not hide fire fx
		Play3DSound("cannon_fire", fX, fY, fZ);

	string sParticleName = "cancloud_fire";		// if (sti(aCharacter.ship.type) < SHIP_CORVETTE)

	int nBallWt = sti(rCannon.caliber);
	if (nBallWt >= CANNON_BALL_WT_48)
		{ sParticleName = "Bombard"; }
	else
	{
		if (nBallWt >= CANNON_BALL_WT_24)	{ sParticleName = "cancloud_fire_big"; }
	}
	//if (rand(1) == 0)
	CreateParticleSystem("canfire", fX, fY, fZ, -fHeightAng, fDirAng, 0.0, 1); // NK 04-09-21 per Mehrunes's idea
	Play3DSound(rCannon.Sound, fX, fY, fZ);
}

void Ball_WaterHitEvent()
{
	int		iCharacterIndex;
	float	x, y, z, vx, vy, vz;

	iCharacterIndex = GetEventData();
	x = GetEventData();
	y = GetEventData();
	z = GetEventData();

	int nBallType = sti(AIBalls.CurrentBallCannonType);
	if (nBallType >= 0)
	{
		ref rCannon = GetCannonByType(nBallType);
		if (CheckAttribute(rCannon, "BigBall") && sti(rCannon.BigBall))
            CreateParticleSystemXPS("splash_big", X, Y, Z, 0.0, 0.0, 0.0, 5);
			SendMessage(&BallSplash, "lffffff", MSG_BALLSPLASH_ADD, x, y, z, vx, vy, vz);
		else
            CreateParticleSystemXPS("splash", X, Y, Z, 0.0, 0.0, 0.0, 5);
			SendMessage(&BallSplash, "lffffff", MSG_BALLSPLASH_ADD, x, y, z, vx, vy, vz);
	}
	Play3DSound("ball_splash", x, y, z);
}

void Ball_FortHit()
{
	int		iCharacterIndex;
	float	x, y, z;

	iCharacterIndex = GetEventData();

	x = GetEventData();
	y = GetEventData();
	z = GetEventData();

	if (rand(4) == 1) CreateParticleSystem("blast", x, y, z, 0.0, 0.0, 0.0, 0); // fix
	SendMessage(&AIFort, "llfff", AI_MESSAGE_FORT_HIT, iCharacterIndex, x, y, z);
}

void Ball_IslandHit()
{
	int		iCharacterIndex;
	float	x, y, z;

	iCharacterIndex = GetEventData();

	x = GetEventData();
	y = GetEventData();
	z = GetEventData();

	if (rand(2) == 1) CreateParticleSystem("blast", x, y, z, 0.0, 0.0, 0.0, 0); // fix

	//Ship_SetLightsOff(&Characters[1], 15.0, true, true, false);
}

void Ball_OnFlyUpdate()
{
	int charIndex = GetEventData();
	int ballAlive = GetEventData();
	float x = GetEventData();
	float y = GetEventData();
	float z = GetEventData();
	float lx = GetEventData();
	float ly = GetEventData();
	float lz = GetEventData();
	SendMessage(&SeaOperator, "lalffffff", MSG_SEA_OPERATOR_BALL_UPDATE, &Characters[charIndex], ballAlive, x, y, z, lx, ly, lz);
}
