/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
#include <engine/e_server_interface.h>
#include <engine/e_config.h>
#include <game/generated/g_protocol.hpp>
#include <game/server/gamecontext.hpp>
#include "laser.hpp"
#include <game/server/gamemodes/fb.hpp>
#include <engine/e_config.h>

//////////////////////////////////////////////////
// laser
//////////////////////////////////////////////////
LASER::LASER(vec2 pos, vec2 direction, float start_energy, int owner)
: ENTITY(NETOBJTYPE_LASER)
{
	this->pos = pos;
	this->owner = owner;
	energy = start_energy;
	dir = direction;
	bounces = 0;
	do_bounce();
	
	game.world.insert_entity(this);
}


bool LASER::hit_character(vec2 from, vec2 to)
{
	vec2 at;
	CHARACTER *owner_char = game.get_player_char(owner);

	CHARACTER *hit = game.world.intersect_character(pos + dir, to, 0.0f, at, owner_char);


	if(!hit || !owner_char)
	{
		return false;
	}
	
	if(hit && owner_char)
	{
		bool gothrough = (hit->player->frozen && config.sv_laser_gothroughfrozens || !hit->player->frozen && config.sv_laser_gothroughplayers) ? true : false;

		hit->take_damage(vec2(0,0), tuning.laser_damage, owner, WEAPON_RIFLE);

		if(gothrough)
			return false;
	}

	this->from = from;
	pos = at;

	energy = -1;		
	return true;
}

void LASER::hit_ball(vec2 from, vec2 to) {
	if (!game.controller->is_flagball() || config.sv_fb_laser_momentum == 0) return;
	for (int i = 0; i < FB_MAX_BALLS; ++i)
	{
		BALL* ball = static_cast<GAMECONTROLLER_FB *>(game.controller)->balls[i];
		if (!ball || ball->carrier) continue;
		vec2 ball_pos = ball->pos + vec2(config.sv_fb_ball_offset_x, -config.sv_fb_ball_offset_y);
		if (distance(ball_pos, closest_point_on_line(from, to, ball_pos)) < config.sv_fb_ball_radius)
		{
			ball->vel += normalize(to - from) * config.sv_fb_laser_momentum * 0.1f;
			ball->drop_tick = server_tick();
			ball->last_carrier = game.players[owner];
			ball->last_carrier_team = game.players[owner]->team;
		}
	};
};

void LASER::do_bounce()
{
	eval_tick = game_tick();
	
	if(energy < 0)
	{
		//dbg_msg("laser", "%d removed", server_tick());
		game.world.destroy_entity(this);
		return;
	}
	
	vec2 to = pos + dir*energy;
	vec2 org_to = to;
	
	if(col_intersect_line(pos, to, 0x0, &to))
	{
		if(!hit_character(pos, to))
		{
			hit_ball(pos, to);
			// intersected
			from = pos;
			pos = to;

			vec2 temp_pos = pos;
			vec2 temp_dir = dir*4.0f;
			
			move_point(&temp_pos, &temp_dir, 1.0f, 0);
			pos = temp_pos;
			dir = normalize(temp_dir);
			
			energy -= distance(from, pos) + tuning.laser_bounce_cost;
			bounces++;
			
			if(bounces > tuning.laser_bounce_num)
				energy = -1;
				
			game.create_sound(pos, SOUND_RIFLE_BOUNCE);

			if(bounces == 1 && config.sv_laserjumps)
				game.create_explosion(pos, owner, WEAPON_GAME, false);
		}
	}
	else
	{
		if(!hit_character(pos, to))
		{
			hit_ball(pos, to);
			from = pos;
			pos = to;
			energy = -1;
		}
	}
		
	//dbg_msg("laser", "%d done %f %f %f %f", server_tick(), from.x, from.y, pos.x, pos.y);
}
	
void LASER::reset()
{
	game.world.destroy_entity(this);
}

void LASER::tick()
{
	if(game_tick() > eval_tick+(server_tickspeed()*tuning.laser_bounce_delay)/1000.0f)
	{
		do_bounce();
	}
}

void LASER::snap(int snapping_client)
{
	if(networkclipped(snapping_client))
		return;

	NETOBJ_LASER *obj = (NETOBJ_LASER *)snap_new_item(NETOBJTYPE_LASER, id, sizeof(NETOBJ_LASER));
	obj->x = (int)pos.x;
	obj->y = (int)pos.y;
	obj->from_x = (int)from.x;
	obj->from_y = (int)from.y;
	obj->start_tick = eval_tick + (server_tick() - game_tick());
}
