#include "common.qh"

#include "misc.qh"
#include "damage.qh"

#include "server.qh"

.entity dmg_attacker;

.void () deathmsg;
.string deathmsg1, deathmsg2, deathmsg3, deathmsg4, deathmsg5, deathmsg6;

// You _MUST_ have th_takedamage if you .takedamage!
// Return TRUE if you were damaged, FALSE otherwise.
.float (float dmg) th_takedamage;

/* Allows you to globally override damage you deal */
/* self = you, other = target */
/* returns new amount of damage, or < 0 for don't */
.float (float dmg) th_dealdamage;

.void (entity missile) th_attack;

/* Update frags in these. */
.void () th_die;
.void () th_kill;

.float (entity missile) th_projectile;

entity ghost_inflictor;

void ()
damage_init =
{
	ghost_inflictor = spawn ("GHOST_INFLICTOR");
};

void (entity from, entity to)
deathmsg_copy =
{
	to.deathmsg1 = from.deathmsg1;
	to.deathmsg2 = from.deathmsg2;
	to.deathmsg3 = from.deathmsg3;
	to.deathmsg4 = from.deathmsg4;
	to.deathmsg5 = from.deathmsg5;
	to.deathmsg6 = from.deathmsg6;
};

string (entity e, string s) _deathmsg_custom_str = {
	if (s == "name(self)")
		return name(self);
	if (s == "name(attacker)")
		return name (self.dmg_attacker);
	if (s == "name(inflictor)")
		return name (self.dmg_inflictor);
	return s;
};

void ()
_deathmsg_custom =
{
	local string s1, s2, s3, s4, s5, s6;

	s1 = s2 = s3 = s4 = s5 = s6 = "";

	s1 = _deathmsg_custom_str (self, self.deathmsg1);
	if (s1)
		s2 = _deathmsg_custom_str (self, self.deathmsg2);
	if (s2)
		s3 = _deathmsg_custom_str (self, self.deathmsg3);
	if (s3)
		s4 = _deathmsg_custom_str (self, self.deathmsg4);
	if (s4)
		s5 = _deathmsg_custom_str (self, self.deathmsg5);
	if (s5)
		s6 = _deathmsg_custom_str (self, self.deathmsg6);

	// I don't use separate bprints, because iD in all its wisdom
	// made them unreliable -- I'd rather lose the whole thing than parts.
	bprint (PRINT_DEATH, s1, s2, s3, s4, s5, s6);
};

void ()
deathmsg_nodisplay =
{
//	refcount_dec (self.dmg_attacker);
//	refcount_dec (self.dmg_inflictor);
	self.dmg_attacker = world;
	self.dmg_inflictor = world;

	self.deathmsg = NOTHING_function;
};

void ()
deathmsg_display =
{
	if (self.th_die)
		self.th_die ();

	if (other.th_kill) {
		local entity oldself, oldother;

		oldself = self;
		oldother = other;
	
		self = oldother;
		other = oldself;

		self.th_kill ();

		self = oldself;
		other = oldother;
	}

	self.deathmsg ();
	deathmsg_nodisplay ();
};

float (float d)
damage_armor = {
	local float adamg, rest;

	adamg = d * self.armortype;
	if (self.armorvalue < adamg)
		adamg = self.armorvalue;

	rest = d - adamg;

	self.armorvalue = self.armorvalue - adamg;
	if (self.armorvalue <= 0) {
		self.armorvalue = 0;
		self.armortype = 0.0;
	}

	return rest;
};

void (float d)
damage_push =
{
	local vector vel;

	if (self.mass == 0) {
		d = 0;
	} else {
		if (self.dmg_attacker == self)
			d = (self.dmg_inflictor.mass + d * rocket_jump) / self.mass;
		else
			d = self.dmg_inflictor.mass / self.mass;
	}

	if (self.dmg_inflictor.velocity == '0 0 0') {
		vel = normalize (self.origin - self.dmg_inflictor.origin);
		vel *= self.dmg_inflictor.speed;
	} else {
		vel = self.dmg_inflictor.velocity;
	}

	self.velocity += d * vel;
};

// deathmessage _IS_ overridden if inflictor.deathmsg1
float (entity ent, entity attacker, entity inflictor, float d,
	   void() deathmessage)
damage =
{
	local entity oldself, oldother;
	local float wasdamaged;

	if (!ent.takedamage)
		return FALSE;

	oldself = self;
	oldother = other;
		
	ent.dmg_attacker = attacker;
	ent.dmg_inflictor = inflictor;

//	refcount_inc (ent.dmg_attacker);
//	refcount_inc (ent.dmg_inflictor);

	if (inflictor.deathmsg1)
		ent.deathmsg = _deathmsg_custom;
	else
		ent.deathmsg = deathmessage;

	if (attacker.th_dealdamage) {
		self = attacker;
		other = ent;
		
		d = self.th_dealdamage(d);
		
		self = oldself;
		other = oldother;

		if (d < 0)
			return FALSE;
	}

	self = ent;
	other = attacker;

	wasdamaged = self.th_takedamage(d);

	self = oldself;
	other = oldother;

	return wasdamaged;
};

float (vector org, entity e)
_damage_radius_can_hit =
{
	if (e.movetype == MOVETYPE_PUSH) {
		traceline (org, center (e), TRUE, world);

		if (trace_fraction == 1 || trace_ent == e)
			return TRUE;
		return FALSE;
	}

	traceline (org, e.origin, TRUE, self);
	if (trace_fraction == 1)
		return TRUE;
	traceline (org, e.origin + '15 15 0', TRUE, self);
	if (trace_fraction == 1)
		return TRUE;
	traceline (org, e.origin + '15 -15 0', TRUE, self);
	if (trace_fraction == 1)
		return TRUE;
	traceline (org, e.origin + '-15 15 0', TRUE, self);
	if (trace_fraction == 1)
		return TRUE;
	traceline (org, e.origin + '-15 -15 0', TRUE, self);
	if (trace_fraction == 1)
		return TRUE;

	return FALSE;
};

void (entity ignore, entity attacker, entity inflictor, void () deathmessage)
damage_radius =
{
	local entity head;
	local vector iorg, org;
	local float points, d, m, r;

	d = inflictor.dmg;
	m = inflictor.mass;
	r = inflictor.lip;

	iorg = center (inflictor);

	for (head = findradius (iorg, r + 40); head; head = head.chain) {
		if (head == ignore)
			continue;

		if (!_damage_radius_can_hit (iorg, head))
			continue;

		org = center(head);

		points = r - (0.5 * vlen (iorg - org));
		if (points <= 0)
			continue;

		if (head == attacker)
			points *= 0.5;

		inflictor.dmg = (points * d) / r;
		inflictor.mass = (points * m) / r;

		damage (head, attacker, inflictor, inflictor.dmg, deathmessage);
	}
};
