-- LegacyWorlds Beta 6
-- PostgreSQL database scripts
--
-- Game updates - battles (start, main computation, end)
--
-- Copyright(C) 2004-2010, DeepClone Development
-- --------------------------------------------------------


CREATE OR REPLACE FUNCTION sys.process_planet_battle_start_updates( c_tick BIGINT )
		RETURNS VOID
		STRICT VOLATILE
		SECURITY INVOKER
	AS $$
DECLARE
	p_id	INT;
BEGIN
	FOR p_id IN SELECT p.name_id
				FROM sys.updates su
					INNER JOIN verse.updates vu ON vu.update_id = su.id
					INNER JOIN verse.planets p ON vu.planet_id = p.name_id
					LEFT OUTER JOIN battles.battles b
						ON b.location_id = p.name_id AND b.last_tick IS NULL
				WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
					AND su.gu_type = 'PLANET_BATTLE_START' AND b.location_id IS NULL
				FOR UPDATE OF p
	LOOP
		IF battles.check_start( p_id ) THEN
			PERFORM events.battle_start_event( battles.initialise( p_id , c_tick ) );
		END IF;
	END LOOP;
END;
$$ LANGUAGE plpgsql;




CREATE OR REPLACE FUNCTION sys.process_planet_battle_main_updates( c_tick BIGINT )
		RETURNS VOID
		STRICT VOLATILE
		SECURITY INVOKER
	AS $$
DECLARE
	ttfi		INT;
	initi		DOUBLE PRECISION;
	dbonus		DOUBLE PRECISION;
	dmg			DOUBLE PRECISION;
	rdmg		DOUBLE PRECISION;
	rec			RECORD;
	a_power		BIGINT;
	d_power		BIGINT;
	p_power		BIGINT;
	bmod		DOUBLE PRECISION;
	a_dmg		DOUBLE PRECISION;
	d_dmg		DOUBLE PRECISION;
BEGIN
	ttfi := floor( sys.get_constant( 'game.battle.timeToFullIntensity' ) )::INT;
	initi := sys.get_constant( 'game.battle.initialIntensity' );
	dbonus := sys.get_constant( 'game.battle.defenceBonus');
	dmg := sys.get_constant( 'game.battle.damage' );
	rdmg := sys.get_constant( 'game.battle.randomDamage' );

	FOR rec IN SELECT b.id AS battle , b.first_tick AS first_tick ,
					b.location_id AS location , ph.current / p.population AS happiness
				FROM sys.updates su
					INNER JOIN verse.updates vu ON vu.update_id = su.id
					INNER JOIN verse.planets p ON vu.planet_id = p.name_id
					INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
					INNER JOIN battles.battles b
						ON b.location_id = p.name_id AND b.last_tick IS NULL
				WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
					AND su.gu_type = 'PLANET_BATTLE_MAIN'
				FOR UPDATE OF p , b
	LOOP
		PERFORM sys.write_log( 'BattleUpdate' , 'DEBUG'::log_level , 'Handling battle #' || rec.battle
				|| ' at planet #' || rec.location );

		-- Get stationary defence power
		p_power := floor( verse.adjust_production( verse.get_raw_production( rec.location , 'DEF' ) , rec.happiness ) );

		-- Get fleets power
		a_power := battles.get_fleets_power( rec.battle , c_tick , TRUE );
		d_power := battles.get_fleets_power( rec.battle , c_tick , FALSE );
		IF a_power = 0 OR d_power + p_power = 0
		THEN
			PERFORM battles.set_defence_power( rec.battle , c_tick , p_power );
			CONTINUE;
		END IF;
		
		PERFORM sys.write_log( 'BattleUpdate' , 'TRACE'::log_level , 'Attack: ' || a_power
			|| '; planetary defences: ' || p_power || '; defence: ' || d_power );
		
		-- Compute battle intensity
		IF c_tick - rec.first_tick < ttfi THEN
			bmod := initi + ( 1 - initi ) * ( c_tick - rec.first_tick ) / ttfi;
		ELSE
			bmod := 1.0;
		END IF;
		PERFORM sys.write_log( 'BattleUpdate' , 'TRACE'::log_level , 'Intensity modifier: ' || bmod );

		-- Compute damage
		d_dmg := bmod * ( d_power * ( 1 + dbonus ) + p_power ) * dmg * ( 1 - rdmg + 2.0 * rdmg * random() );
		a_dmg := bmod * a_power * dmg * ( 1 - rdmg + 2.0 * rdmg * random() );
		PERFORM sys.write_log( 'BattleUpdate' , 'TRACE'::log_level , 'Damage - to defence: ' || a_dmg
			|| '; to attack: ' || d_dmg );

		-- Inflict damage
		PERFORM battles.inflict_damage( rec.battle , a_dmg , FALSE , c_tick );
		PERFORM battles.inflict_damage( rec.battle , d_dmg , TRUE , c_tick );
		
		-- Update defence power
		p_power := floor( verse.adjust_production( verse.get_raw_production( rec.location , 'DEF' ) , rec.happiness ) );
		PERFORM battles.set_defence_power( rec.battle , c_tick , p_power );
	END LOOP;
END;
$$ LANGUAGE plpgsql;




CREATE OR REPLACE FUNCTION sys.process_planet_battle_end_updates( c_tick BIGINT )
		RETURNS VOID
		STRICT VOLATILE
		SECURITY INVOKER
	AS $$
DECLARE
	rec		RECORD;
	n_owner	INT;
BEGIN
	FOR rec IN SELECT b.id AS battle , b.location_id AS location
				FROM sys.updates su
					INNER JOIN verse.updates vu ON vu.update_id = su.id
					INNER JOIN verse.planets p ON vu.planet_id = p.name_id
					INNER JOIN battles.battles b
						ON b.location_id = p.name_id AND b.last_tick IS NULL
				WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
					AND su.gu_type = 'PLANET_BATTLE_END'
				FOR UPDATE OF p , b
	LOOP
		IF battles.get_fleets_power( rec.battle , c_tick , TRUE ) = 0 THEN
			-- Attack is dead/gone, end the battle
			UPDATE battles.battles SET last_tick = c_tick
				WHERE id = rec.battle;
			PERFORM events.battle_end_event( rec.battle );
		ELSEIF battles.get_fleets_power( rec.battle , c_tick , FALSE ) + battles.get_defence_power( rec.battle , c_tick ) = 0 THEN
			-- Defence is dead/gone, transfer planet ownership to biggest fleet owner
			n_owner := battles.get_biggest_fleet_owner( rec.battle , c_tick );
			PERFORM events.planet_ochange_events( rec.location , n_owner );
			PERFORM emp.leave_planet( rec.location );
			INSERT INTO emp.planets( planet_id , empire_id )
				VALUES( rec.location , n_owner );

			-- End the battle
			UPDATE battles.battles SET last_tick = c_tick
				WHERE id = rec.battle;
			PERFORM events.battle_end_event( rec.battle );

			-- Set fleets in orbit to defence if they're not on the new owner's enemy list
			UPDATE fleets.fleets f SET attacking = ( ele.empire IS NOT NULL )
				FROM fleets.fleets f2
					LEFT OUTER JOIN fleets.movements m ON m.fleet_id = f2.id
					LEFT OUTER JOIN emp.enemies ele ON ele.enemy = f2.owner_id AND ele.empire = n_owner
				WHERE f.id = f2.id AND f.location_id = rec.location AND m.fleet_id IS NULL;
				
			-- Check if the battle needs to be restarted
			IF battles.check_start( rec.location ) THEN
				PERFORM events.battle_start_event( battles.initialise( rec.location , c_tick ) );
			END IF;
		ELSE
			CONTINUE;
		END IF;
		
		-- Mark the end of the battle
		INSERT INTO battles.finished_battles_list
			SELECT empire, battle, planet, x, y, orbit, name, first_tick, last_tick, last_update
				FROM battles.full_battles_list
				WHERE battle = rec.battle;
	END LOOP;
END;
$$ LANGUAGE plpgsql;

