active_zones = { }
model_data = { }
base_object =
{	init = function(self, name, frame, threshold, fix_timeout)
			self.locked = false;
			self.name = name;
			self.frame = frame;
			if threshold then self.threshold = threshold * 0.01 * object.desc.Health end
			self.damage = 0;
			self.fix_timeout = fix_timeout;
-- log("init model part ["..name.."]");
		end,
	lock = function(self) self.locked = true end,
	unlock = function(self) self.locked = false end,
	check = function(self, damage)
		self.damage = self.damage + damage;
-- ScreenWarning(self.name..":check(" .. damage ..":  dmg = " .. self.damage .. ", thr = " .. self.threshold);
		if self.damage > self.threshold then
			self:lock();
			if self.frame.alt_destroyed and object.RandomInt(2) > 1 then
				self.frame.active = self.frame.alt_destroyed;
			else
				self.frame.active = self.frame.destroyed
			end
			object.SetGroupVisible(false, self.frame.normal);
			object.SetGroupVisible(true, self.frame.active);
			AIparts:AddOneTime(self.name .. ":fix", self.fix_timeout, function() self:fix() end);
		end
	end,
	fix = function(self)
		self.damage = 0;
		object.SetGroupVisible(false, self.frame.active);
		object.SetGroupVisible(true, self.frame.normal);
		self.frame.active = self.frame.normal;
		self:unlock();
	end,
}
crew =
{	init = function(self)
		self.crew = object.desc.Crew;
		self.name = "crew";
	end,
	check = function(self, damage)
		local coef = 166 / (health / damage);
		for i = 0, self.crew - 1 do
			unit.DoCrewDamage(i, object.RandomHalf(coef));
		end
	end,
}
gun_squad =
{	init = function(self)
		self.crew = object.desc.Crew;
		self.name = "crew";
	end,
	check = function(self, damage)
		local coef = 100 * damage;
-- ScreenWarning("GUN SQUAD GOT COEF = " .. coef);
		for i = 0, self.crew - 1 do
			unit.DoCrewDamage(i, 30+object.RandomHalf(coef));
		end
	end,
}
cargo =
{	init = function(self)
		self.cargo = object.desc.Passengers;
		self.name = "cargo";
	end,
	check = function(self, damage)
		local coef = 166 / (health / damage);
		for i = 0, self.cargo - 1 do
			unit.DoPassengerDamage(i, object.RandomHalf(coef));
		end
	end,
}
engine =
{	init = base_object.init,
	smoke_locator = object.GetLocatorId("locator_body"),
	smoke = function(self)
		CheckDamage(1);
		CreateObject("Effects/smoke/engine.dsc", object.GetLocatorPosition(self.smoke_locator));
		self.frame.black = self.frame.black + 0.004;
		object.SetGroupDamage(self.frame.black, self.frame.active);
	end,
	check = function(self, damage)
		self.damage = self.damage + damage;
-- ScreenWarning(self.name .. ":check(" .. damage ..":  dmg = " .. self.damage .. ", thr = " .. self.threshold);
		if self.damage > self.threshold then
			AIparts:AddPeriodic(self.name .. ":smoke", 10, function() self:smoke() end);
			AIparts:AddOneTime(self.name .. ":fix", self.fix_timeout, function() self:fix() end);
		end
	end,
	fix = function(self)
		self.damage = 0;
		AIparts:RemovePeriodic("smoke");
	end,
}
left =
{	init = base_object.init,
	lock = function(self)
		base_object.lock(self);
		unit.DisableMove();
	end,
	unlock = function(self)
		base_object.unlock(self);
		if not right.locked then unit.EnableMove() end
	end,
	check = base_object.check,
	fix = base_object.fix,
}
right =
{	init = base_object.init,
	lock = left.lock,
	unlock = function(self)
		base_object.unlock(self);
		if not left.locked then unit.EnableMove() end
	end,
	check = base_object.check,
	fix = base_object.fix,
}
body =
{	init = base_object.init,
	check = function(self, damage)
		if damage > object.GetHealth() then
			self.frame.active = self.frame.destroyed
			object.SetGroupVisible(false, self.frame.normal);
			object.SetGroupVisible(true, self.frame.active);
			object.SetGroupDamage(self.frame.black * 3, self.frame.active);
		end
	end,
}
turret =
{	init = base_object.init,
	check = function(self, damage)
		if damage > object.GetHealth() then
			self.frame.active=self.frame.destroyed;
			object.SetGroupVisible(false, self.frame.normal);
			object.SetGroupVisible(true, self.frame.active);
			object.SetGroupDamage(self.frame.black * 3, self.frame.active);
		end
	end,
}
gun =
{	init = base_object.init,
	check = function(self, damage)
		if damage > object.GetHealth() then
			self.frame.active=self.frame.destroyed;
			object.SetGroupVisible(false, self.frame.normal);
			object.SetGroupVisible(true, self.frame.active);
			object.SetGroupDamage(self.frame.black * 3, self.frame.active);
		end
	end,
}

function V2Init()
	health = object.GetHealth();
	crew:init();
	cargo:init();
	local unit_type = object.desc.UnitType;
	local armor = object.desc.Armor;
	if not unit_type then
log("vehicle2.lua: no unit_type found! Object " .. object.desc.DescFileName);
		return;
	else
		frames = { }
		blacken = 0.05;
-- log("- vehicle2.lua: initializing " .. object.desc.DescFileName .. ", type " .. unit_type);
		if unit_type == "boat" or unit_type == "ship" then
			frames =
			{	body = { normal = utils.AssignFrames("body"), destroyed = utils.AssignFrames("body_destroyed") },
			}
			model_data =
			{	{ name = "damagezone_body", armor = armor.Front, frames = { frames.body }, handler = crew, aux_handler = cargo },
			}
			body:init("body", frames.body);
			blacken = 0.2;
		elseif unit_type == "truck" or unit_type == "service" or unit_type == "tanker" then
			frames =
			{	body = { normal = utils.AssignFrames("body"), destroyed = utils.AssignFrames("body_destroyed") },
			}
			model_data =
			{	{ name = "damagezone_cargo", armor = armor.Back, frames = { frames.body }, handler = body, aux_handler = cargo },
				{ name = "damagezone_crew", armor = armor.Side, frames = { frames.body }, handler = body, aux_handler = crew },
				{ name = "damagezone_engine", armor = armor.Front, frames = { frames.body }, handler = body, aux_handler = engine },
				{ name = "damagezone_chassis", armor = armor.Side, frames = { frames.body }, handler = body, aux_handler = body },
			}
			engine:init("engine", frames.body, 3, 100);
			body:init("body", frames.body);
			blacken = 0.2;
		elseif unit_type == "gun" or unit_type == "field" or unit_type == "howitzer" or unit_type == "aa" or 
			unit_type == "atg" or unit_type == "coastal" or unit_type == "naval" or unit_type == "mg" or unit_type == "mortar" then
			frames =
			{	gun = { normal = utils.AssignFrames("gun"), destroyed = utils.AssignFrames("gun_destroyed") },
			}
			model_data =
			{	{ name = "damagezone_front", armor = armor.Front, frames = { frames.gun }, handler = gun, aux_handler = gun_squad },
				{ name = "damagezone_back", armor = armor.Front, frames = { frames.gun }, handler = gun_squad, aux_handler = gun },
			}
			gun:init("gun", frames.gun);
			gun_squad:init();
			blacken = 0.4;
		elseif unit_type == "fortification" then
			frames =
			{	gun = { normal = utils.AssignFrames("gun"), destroyed = utils.AssignFrames("gun_destroyed") },
			}
			model_data =
			{	{ name = "damagezone_front", armor = armor.Front, frames = { frames.gun }, handler = gun, aux_handler = crew },
			}
			gun:init("gun", frames.gun);
			blacken = 0.05;
		elseif unit_type == "rocketlauncher" then
			frames =
			{	body = { normal = utils.AssignFrames("body"), destroyed = utils.AssignFrames("body_destroyed") },
			}
			model_data =
			{	{ name = "damagezone_engine", armor = armor.Front, frames = { frames.body }, handler = engine },
				{ name = "damagezone_crew", armor = armor.Front, frames = { frames.body }, handler = crew },
				{ name = "damagezone_cargo", armor = armor.Front, frames = { frames.body }, handler = body },
				{ name = "damagezone_chassis", armor = armor.Front, frames = { frames.body }, handler = body },
			}
			engine:init("engine", frames.body, 3, 100);
			body:init("body", frames.body);
			blacken = 0.4;
		elseif unit_type == "apc" then
			frames =
			{	body = { normal = utils.AssignFrames("body"), destroyed = utils.AssignFrames("body_destroyed") },
				left_track = { normal = utils.AssignFrames("track_left"), destroyed = utils.AssignFrames("track_left_01_destroyed") or utils.AssignFrames("track_left_destroyed"), alt_destroyed = utils.AssignFrames("track_left_02_destroyed") },
				right_track = { normal = utils.AssignFrames("track_right"), destroyed = utils.AssignFrames("track_right_01_destroyed") or utils.AssignFrames("track_right_destroyed"), alt_destroyed = utils.AssignFrames("track_right_02_destroyed") },
			}
			model_data =
			{	{ name = "damagezone_front", armor = armor.Front, frames = { frames.body }, handler = engine },
				{ name = "damagezone_left", armor = armor.Side, frames = { frames.left_track }, handler = left },
				{ name = "damagezone_right", armor = armor.Side, frames = { frames.right_track }, handler = right },
				{ name = "damagezone_back", armor = armor.Rear, frames = { frames.body }, handler = body },
			}
			engine:init("engine", frames.body, 3, 100);
			body:init("body", frames.body);
			left:init("left", frames.left_track, 5, 300);
			right:init("right", frames.right_track, 5, 300);
		elseif unit_type == "tank" or unit_type == "mobile_aa" then
			frames =
			{	body = { normal = utils.AssignFrames("body"), destroyed = utils.AssignFrames("body_destroyed") },
				turret = { normal = utils.AssignFrames("turret"), destroyed = utils.AssignFrames("turret_destroyed") },
				left_track = { normal = utils.AssignFrames("track_left"), destroyed = utils.AssignFrames("track_left_01_destroyed") or utils.AssignFrames("track_left_destroyed"), alt_destroyed = utils.AssignFrames("track_left_02_destroyed") },
				right_track = { normal = utils.AssignFrames("track_right"), destroyed = utils.AssignFrames("track_right_01_destroyed") or utils.AssignFrames("track_right_destroyed"), alt_destroyed = utils.AssignFrames("track_right_02_destroyed") },
			}
			model_data =
			{	{ name = "damagezone_front", armor = armor.Front, frames = { frames.body, frames.turret }, handler = body, aux_handler = turret },
				{ name = "damagezone_turret", armor = armor.Turret, frames = { frames.turret }, handler = turret, aux_handler = crew },
				{ name = "damagezone_left", armor = armor.Side, frames = { frames.left_track }, handler = left, aux_handler = crew },
				{ name = "damagezone_right", armor = armor.Side, frames = { frames.right_track }, handler = right, aux_handler = crew },
				{ name = "damagezone_back", armor = armor.Back, frames = { frames.body }, handler = engine, aux_handler = crew },
			}
			engine:init("engine", frames.body, 3, 100);
			body:init("body", frames.body);
			turret:init("turret", frames.turret);
			left:init("left", frames.left_track, 5, 300);
			right:init("right", frames.right_track, 5, 300);
		elseif unit_type == "spg" then
			frames =
			{	body = { normal = utils.AssignFrames("body"), destroyed = utils.AssignFrames("body_destroyed") },
				left_track = { normal = utils.AssignFrames("track_left"), destroyed = utils.AssignFrames("track_left_01_destroyed") or utils.AssignFrames("track_left_destroyed"), alt_destroyed = utils.AssignFrames("track_left_02_destroyed") },
				right_track = { normal = utils.AssignFrames("track_right"), destroyed = utils.AssignFrames("track_right_01_destroyed") or utils.AssignFrames("track_right_destroyed"), alt_destroyed = utils.AssignFrames("track_right_02_destroyed") },
			}
			model_data =
			{	{ name = "damagezone_front", armor = armor.Front, frames = { frames.body }, handler = body },
				{ name = "damagezone_left", armor = armor.Side, frames = { frames.left_track }, handler = left, aux_handler = crew },
				{ name = "damagezone_right", armor = armor.Side, frames = { frames.right_track }, handler = right, aux_handler = crew },
				{ name = "damagezone_back", armor = armor.Back, frames = { frames.body }, handler = engine, aux_handler = crew },
			}
			engine:init("engine", frames.body, 2, 100);
			body:init("body", frames.body);
			left:init("left", frames.left_track, 30, 400);
			right:init("right", frames.right_track, 30, 400);
		else
log("ERROR: vehicle2.lua failed, unknown type ["..unit_type.."] in " .. object.desc.DescFileName);
			return;
		end

		if frames == nil or model_data == nil then
log("ERROR: invalid frames or model_data in " .. object.desc.DescFileName);
			return;
		end

		for _,v in pairs(frames) do
			if (not v.normal) or (not v.destroyed) then
log("ERROR: missing part frame or missing _destroyed frame in " .. object.desc.DescFileName);
				return;
			end

			v.black = object.GetGroupDamage(v.normal);
			if v.black == 0 or v.black == nil then v.black = 0.01 end;
-- log("ERROR: frame "..v.normal.." has damage of "..v.black);
			v.active = v.normal;
		end

		for _,v in ipairs(model_data) do
			local zone = utils.AssignDz(v.name);
			if zone then
				if not v.armor then
					v.armor = DEFAULT_ARMOR;
				end
				active_zones[zone] = v;
			end
		end
	end
	return true;
-- log("+ initalized ok");
end

function CheckSplashDamageZones(DetonationDamage, Caliber, Zones)
-- log("Checking splash zones for " .. object.desc.DescFileName);
	local damage = 0;
	local frames_concerned = { };
	for ZoneId,val in pairs(Zones) do
-- log("zone #"..ZoneId);
		local dz = active_zones[ZoneId];
		local v_sq = val.DistanceFactor * val.DistanceFactor * blacken;
--		damage = damage + DetonationDamage * val.DistanceFactor / dz.armor;
		damage = damage + Splash(DetonationDamage, dz.armor, val.DistanceFactor);
		for _,v in pairs(dz.frames) do
			v.black = v.black + v_sq;
-- log("frame " .. v.normal .. " black=" .. v.black);
			frames_concerned[v.active] = v.black;
		end
	end
	for k,v in pairs(frames_concerned) do
		object.SetGroupDamage(v, k);
	end
-- ScreenWarning("dmg sum = " .. damage);
	CheckDamage(damage);
end

function CheckDirectDamage(WeaponDamage, Caliber, Point, Distance, ZoneId)
	local zone = active_zones[ZoneId];
	local armor = zone.armor;
	local damage = Penetrate(WeaponDamage, armor, Distance);
	if damage > 0 then 
		local handler = zone.handler;
		if handler then
-- ScreenLog("direct hit to "..handler.name);
			handler:check(damage)
		end
		local aux_handler = zone.aux_handler;
		if aux_handler then
-- ScreenLog("aux hit to "..aux_handler.name);
			aux_handler:check(damage * 0.5)
		end
		CheckDamage(damage, Point)
	end
end

function CheckDamage(Damage, Point)
	local health = object.GetHealth() - Damage;
	if health < 0 then health = 0 end
	object.SetHealth(health);
-- ScreenError(object.desc.DescFileName.." got dmg = " .. Damage .. ", resulting health=" .. health);

	if health <= 0 then
		for _,v in pairs(frames) do
-- log(object.desc.DescFileName .. " is DYING");
--			object.SetGroupDamage(v.black * 3.5, v.active);
			object.SetGroupDamage(1, v.active);
		end
		object.Die();
	end
end

function Debug()
	ScreenLog("unit " .. object.desc.DescFileName);
	ScreenWarning("\th/p="..object.GetHealth());
	ScreenWarning("\tammo=" .. unit.GetAmmo1().." / "..unit.GetAmmo2());
	local normal, destroyed;
	ScreenLog("Guns");
	for k,v in pairs(object.desc.Armament.Guns) do
		ScreenWarning("\t" .. k .. "\t" .. v.Name .. ", dmg = " .. v.Damage);
	end
	ScreenLog("Frames");
	for k,v in pairs(frames) do
		if v.normal then normal = string.format("%02i", v.normal) else normal = "---" end
		if v.destroyed then destroyed = string.format("%02i", v.destroyed) else destroyed = "---" end
		ScreenWarning("\t" .. k ..": "..normal.." / "..destroyed)
	end
	ScreenLog("Zones");
	for k,v in pairs(active_zones) do
		local s = "\t["..k.."] "..v.name.." "..", armor="..v.armor .. ", associated frames:";
		for _,v1 in pairs(v.frames) do
			s = s .. v1.normal.." / "..v1.destroyed;
		end
		ScreenWarning(s);
	end
	ScreenLog("Armor:");
	for k,v in pairs(object.desc.Armor) do
		ScreenWarning("\t[" .. k .. "] = " .. v);
	end
	ScreenWarning("calling object.Debug():");
	object.Debug();
	ScreenError(" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
	ScreenWarning("left.locked:") 
	if left.locked then ScreenLog("TRUE") else ScreenLog("FALSE") end
	ScreenWarning("right.locked:") 
	if right.locked then ScreenLog("TRUE") else ScreenLog("FALSE") end
end

