/*
 * Copyright 2011-2012 Arx Libertatis Team (see the AUTHORS file)
 *
 * This file is part of Arx Libertatis.
 *
 * Arx Libertatis is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Arx Libertatis is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Arx Libertatis.  If not, see <http://www.gnu.org/licenses/>.
 */
/* Based on:
===========================================================================
ARX FATALIS GPL Source Code
Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.

This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code'). 

Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public 
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code.  If not, see 
<http://www.gnu.org/licenses/>.

In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these 
additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx 
Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o 
ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
// Code: Cyril Meynier
//
// Copyright (c) 1999-2001 ARKANE Studios SA. All rights reserved

#include <cctype>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>

#include "game/Equipment.h"
#include "game/NPC.h"
#include "game/Damage.h"
#include "game/Player.h"
#include "game/Inventory.h"
#include "game/Spells.h"

#include "gui/Interface.h"

#include "graphics/BaseGraphicsTypes.h"
#include "graphics/Color.h"
#include "graphics/GraphicsTypes.h"
#include "graphics/Math.h"
#include "graphics/Vertex.h"
#include "graphics/data/Mesh.h"
#include "graphics/data/MeshManipulation.h"
#include "graphics/data/TextureContainer.h"
#include "graphics/particle/ParticleEffects.h"

#include "io/resource/ResourcePath.h"

#include "math/Random.h"
#include "math/Vector2.h"
#include "math/Vector3.h"

#include "physics/Collisions.h"

#include "platform/String.h"
#include "platform/Platform.h"

#include "scene/Object.h"
#include "scene/LinkedObject.h"
#include "scene/GameSound.h"
#include "scene/Interactive.h"

#include "script/Script.h"

using std::min;
using std::max;
using std::string;

struct EQUIP_INFO
{
	char name[64];
};

#define SP_SPARKING 1
#define SP_BLOODY 2

extern Vec3f PUSH_PLAYER_FORCE;
extern long HERO_SHOW_1ST;
extern long EXTERNALVIEW;

extern EERIE_3DOBJ * arrowobj;

EQUIP_INFO equipinfo[IO_EQUIPITEM_ELEMENT_Number];

//***********************************************************************************************
// Returns the object type flag corresponding to a string
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/29)
//***********************************************************************************************
ItemType ARX_EQUIPMENT_GetObjectTypeFlag(const string & temp) {
	
	if(temp.empty()) {
		return 0;
	}
	
	char c = temp[0];
	
	arx_assert(std::tolower(c) == c);
	
	switch(c) {
		case 'w':
			return OBJECT_TYPE_WEAPON;
		case 'd':
			return OBJECT_TYPE_DAGGER;
		case '1':
			return OBJECT_TYPE_1H;
		case '2':
			return OBJECT_TYPE_2H;
		case 'b':
			return OBJECT_TYPE_BOW;
		case 's':
			return OBJECT_TYPE_SHIELD;
		case 'f':
			return OBJECT_TYPE_FOOD;
		case 'g':
			return OBJECT_TYPE_GOLD;
		case 'r':
			return OBJECT_TYPE_RING;
		case 'a':
			return OBJECT_TYPE_ARMOR;
		case 'h':
			return OBJECT_TYPE_HELMET;
		case 'l':
			return OBJECT_TYPE_LEGGINGS;
	}
	
	return 0;
}
//***********************************************************************************************
// Releases Equiped Id from player
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/29)
//***********************************************************************************************
void ARX_EQUIPMENT_Release(long id)
{
	if (id)
	{
		for (long i = 0; i < MAX_EQUIPED; i++)
		{
			if (player.equiped[i] == id)
			{
				player.equiped[i] = 0;
			}
		}
	}
}

//***********************************************************************************************
// Releases Equipment Structure
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/29)
//***********************************************************************************************
static void ARX_EQUIPMENT_ReleaseEquipItem(INTERACTIVE_OBJ * io)
{
	if (!io) return;

	if (!(io->ioflags & IO_ITEM)) return;

	if (io->_itemdata->equipitem)
	{
		free(io->_itemdata->equipitem);
		io->_itemdata->equipitem = NULL;
	}
}

//***********************************************************************************************
// Releases all id equipments in IO io
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/29)
//***********************************************************************************************
void ARX_EQUIPMENT_ReleaseAll(INTERACTIVE_OBJ * io)
{
	if (io)
		ARX_EQUIPMENT_ReleaseEquipItem(io);
}
extern long EXITING;
//***********************************************************************************************
// Recreates player mesh from scratch
//***********************************************************************************************


static void applyTweak(EquipmentSlot equip, TweakType tw, const string & selection) {
	
	if(!player.equiped[equip] || !ValidIONum(player.equiped[equip])) {
		return;
	}
	
	INTERACTIVE_OBJ * io = inter.iobj[0];
	
	arx_assert(inter.iobj[player.equiped[equip]]->tweakerinfo != NULL);
	
	const IO_TWEAKER_INFO & tweak = *inter.iobj[player.equiped[equip]]->tweakerinfo;
	
	if(!tweak.filename.empty()) {
		res::path mesh = "graph/obj3d/interactive/npc/human_base/tweaks" / tweak.filename;
		EERIE_MESH_TWEAK_Do(io, tw, mesh);
	}
	
	if(tweak.skintochange.empty() || tweak.skinchangeto.empty()) {
		return;
	}
	
	res::path file = "graph/obj3d/textures" / tweak.skinchangeto;
	TextureContainer * temp = TextureContainer::Load(file, TextureContainer::Level);
	
	long mapidx = ObjectAddMap(io->obj, temp);
	
	long sel = -1;
	for(size_t i = 0; i < io->obj->selections.size(); i++) {
		if(io->obj->selections[i].name == selection) {
			sel = i;
			break;
		}
	}
	if(sel == -1) {
		return;
	}
	
	long textochange = -1;
	for(size_t i = 0; i < io->obj->texturecontainer.size(); i++) {
		if(tweak.skintochange == io->obj->texturecontainer[i]->m_texName.filename()) {
			textochange = i;
		}
	}
	if(textochange == -1) {
		return;
	}
	
	for(size_t i = 0; i < io->obj->facelist.size(); i++) {
		if(IsInSelection(io->obj, io->obj->facelist[i].vid[0], sel) != -1
		   && IsInSelection(io->obj, io->obj->facelist[i].vid[1], sel) != -1
		   && IsInSelection(io->obj, io->obj->facelist[i].vid[2], sel) != -1) {
			if(io->obj->facelist[i].texid == textochange) {
				io->obj->facelist[i].texid = (short)mapidx;
			}
		}
	}
	
}

void ARX_EQUIPMENT_RecreatePlayerMesh() {
	
	if(EXITING) {
		return;
	}
	
	INTERACTIVE_OBJ * io = inter.iobj[0];
	if(!io) {
		return;
	}
	
	if(io->obj != hero) {
		delete io->obj;
	}
	io->obj = loadObject("graph/obj3d/interactive/npc/human_base/human_base.teo", false);
	
	applyTweak(EQUIP_SLOT_HELMET, TWEAK_HEAD, "head");
	applyTweak(EQUIP_SLOT_ARMOR, TWEAK_TORSO, "chest");
	applyTweak(EQUIP_SLOT_LEGGINGS, TWEAK_LEGS, "leggings");
	
	INTERACTIVE_OBJ * target = inter.iobj[0];
	INTERACTIVE_OBJ * toequip = NULL;

	if (!target) return;

	for (long i = 0; i < MAX_EQUIPED; i++)
	{
		if ((player.equiped[i] != 0)
		        &&	ValidIONum(player.equiped[i]))
		{
			toequip = inter.iobj[player.equiped[i]];

			if (toequip)
			{
				if (toequip->type_flags & (OBJECT_TYPE_DAGGER
				                           |	OBJECT_TYPE_1H
				                           |	OBJECT_TYPE_2H
				                           |	OBJECT_TYPE_BOW))
				{
					if (player.Interface & INTER_COMBATMODE)	
					{
						ARX_EQUIPMENT_AttachPlayerWeaponToHand();
					}
					else
					{
						EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "weapon_attach", "primary_attach", toequip); //
					}
				}
				else if (toequip->type_flags & OBJECT_TYPE_SHIELD)
				{
					if (player.equiped[EQUIP_SLOT_SHIELD] != 0)
					{
						EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "shield_attach", "shield_attach", toequip);
					}

				}
			}
		}
	}

	ARX_PLAYER_Restore_Skin();
	HERO_SHOW_1ST = -1;

	if (EXTERNALVIEW)
	{
		ARX_INTERACTIVE_Show_Hide_1st(inter.iobj[0], 0);
	}
	else 
	{
		ARX_INTERACTIVE_Show_Hide_1st(inter.iobj[0], 1);
	}

	ARX_INTERACTIVE_HideGore(inter.iobj[0], 1);
	EERIE_Object_Precompute_Fast_Access(hero);
	EERIE_Object_Precompute_Fast_Access(inter.iobj[0]->obj);

	ARX_INTERACTIVE_RemoveGoreOnIO(inter.iobj[0]); 
}

void ARX_EQUIPMENT_UnEquipAllPlayer()
{
	for (long i = 0; i < MAX_EQUIPED; i++)
	{
		if ((player.equiped[i]) && (ValidIONum(player.equiped[i])))
		{
			ARX_EQUIPMENT_UnEquip(inter.iobj[0], inter.iobj[player.equiped[i]]);
		}
	}

	ARX_PLAYER_ComputePlayerFullStats();
}


bool ARX_EQUIPMENT_IsPlayerEquip(INTERACTIVE_OBJ * _pIO)
{
	INTERACTIVE_OBJ * io = inter.iobj[0];

	if (io == NULL) return false;

	if (io != inter.iobj[0]) return false;

	for (long i = 0; i < MAX_EQUIPED; i++)
	{
		if ((player.equiped[i] != 0) &&
		        ValidIONum(player.equiped[i]))
		{
			INTERACTIVE_OBJ * toequip = inter.iobj[player.equiped[i]];

			if (toequip == _pIO)
			{
				return true;
			}
		}
	}

	return false;
}


//***********************************************************************************************
// flags & 1 == destroyed !
//***********************************************************************************************
void ARX_EQUIPMENT_UnEquip(INTERACTIVE_OBJ * target, INTERACTIVE_OBJ * tounequip, long flags)
{
	if (target == NULL) return;

	if (tounequip == NULL) return;

	if (target != inter.iobj[0]) return;

	for (long i = 0; i < MAX_EQUIPED; i++)
	{
		if ((player.equiped[i] != 0)
		        &&	ValidIONum(player.equiped[i])
		        &&	(inter.iobj[player.equiped[i]] == tounequip))
		{
			EERIE_LINKEDOBJ_UnLinkObjectFromObject(target->obj, tounequip->obj);
			ARX_EQUIPMENT_Release(player.equiped[i]);
			target->bbox1.x = 9999;
			target->bbox2.x = -9999;
			
			if(!flags & 1) {
				if(DRAGINTER == NULL) {
					ARX_SOUND_PlayInterface(SND_INVSTD);
					Set_DragInter(tounequip);
				} else {
					giveToPlayer(tounequip);
				}
			}
			
			EVENT_SENDER = tounequip;
			SendIOScriptEvent(inter.iobj[0], SM_EQUIPOUT);
			EVENT_SENDER = inter.iobj[0];
			SendIOScriptEvent(tounequip, SM_EQUIPOUT);
		}
	}

	if ((tounequip->type_flags & OBJECT_TYPE_HELMET)
	        ||	(tounequip->type_flags & OBJECT_TYPE_ARMOR)
	        ||	(tounequip->type_flags & OBJECT_TYPE_LEGGINGS))
		ARX_EQUIPMENT_RecreatePlayerMesh();
}
//***********************************************************************************************
//***********************************************************************************************
void ARX_EQUIPMENT_AttachPlayerWeaponToHand()
{
	INTERACTIVE_OBJ * target = inter.iobj[0];
	INTERACTIVE_OBJ * toequip = NULL;

	if (!target) return;

	for (long i = 0; i < MAX_EQUIPED; i++)
	{
		if ((player.equiped[i] != 0)
		        &&	ValidIONum(player.equiped[i]))
		{
			toequip = inter.iobj[player.equiped[i]];

			if (toequip)
			{
				if ((toequip->type_flags & OBJECT_TYPE_DAGGER)
				        ||	(toequip->type_flags & OBJECT_TYPE_1H)
				        ||	(toequip->type_flags & OBJECT_TYPE_2H)
				        ||	(toequip->type_flags & OBJECT_TYPE_BOW)
				   )
				{
					EERIE_LINKEDOBJ_UnLinkObjectFromObject(target->obj, toequip->obj);
					EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "primary_attach", "primary_attach", toequip); //
					return;
				}
			}
		}
	}
}
//***********************************************************************************************
//***********************************************************************************************
void ARX_EQUIPMENT_AttachPlayerWeaponToBack()
{
	INTERACTIVE_OBJ * target = inter.iobj[0];
	INTERACTIVE_OBJ * toequip = NULL;

	if (!target) return;

	for (long i = 0; i < MAX_EQUIPED; i++)
	{
		if ((player.equiped[i] != 0)
		        &&	ValidIONum(player.equiped[i]))
		{
			toequip = inter.iobj[player.equiped[i]];

			if (toequip)
			{
				if ((toequip->type_flags & OBJECT_TYPE_DAGGER)
				        ||	(toequip->type_flags & OBJECT_TYPE_1H)
				        ||	(toequip->type_flags & OBJECT_TYPE_2H)
				        ||	(toequip->type_flags & OBJECT_TYPE_BOW)
				   )
				{
					if (toequip->type_flags & OBJECT_TYPE_BOW)
					{
						EERIE_LINKEDOBJ_UnLinkObjectFromObject(target->obj, toequip->obj);
						EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "weapon_attach", "test", toequip); //
						return;
					}

					EERIE_LINKEDOBJ_UnLinkObjectFromObject(target->obj, toequip->obj);
					EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "weapon_attach", "primary_attach", toequip); //
					return;
				}
			}
		}
	}
}
//***********************************************************************************************
//***********************************************************************************************
long ARX_EQUIPMENT_GetPlayerWeaponType()
{
	INTERACTIVE_OBJ * io = inter.iobj[0];

	if (!io) return WEAPON_BARE;

	if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
	        &&	ValidIONum(player.equiped[EQUIP_SLOT_WEAPON]))
	{
		INTERACTIVE_OBJ * toequip = inter.iobj[player.equiped[EQUIP_SLOT_WEAPON]];

		if (toequip)
		{
			if (toequip->type_flags & OBJECT_TYPE_DAGGER)	return WEAPON_DAGGER;

			if (toequip->type_flags & OBJECT_TYPE_1H)		return WEAPON_1H;

			if (toequip->type_flags & OBJECT_TYPE_2H)		return WEAPON_2H;

			if (toequip->type_flags & OBJECT_TYPE_BOW)		return WEAPON_BOW;
		}
	}

	return WEAPON_BARE;
}
//***********************************************************************************************
//***********************************************************************************************
void ARX_EQUIPMENT_LaunchPlayerUnReadyWeapon()
{
	INTERACTIVE_OBJ * io = inter.iobj[0];

	if (!io) return;

	ANIM_HANDLE * anim;
	long type = ARX_EQUIPMENT_GetPlayerWeaponType();

	switch (type)
	{
		case WEAPON_DAGGER:
			anim = io->anims[ANIM_DAGGER_UNREADY_PART_1];
			break;
		case WEAPON_1H:
			anim = io->anims[ANIM_1H_UNREADY_PART_1];
			break;
		case WEAPON_2H:
			anim = io->anims[ANIM_2H_UNREADY_PART_1];
			break;
		case WEAPON_BOW:
		{
			anim = io->anims[ANIM_MISSILE_UNREADY_PART_1];

			if (arrowobj) 
			{
				EERIE_LINKEDOBJ_UnLinkObjectFromObject(io->obj, arrowobj);
			}
		}
		break;
		default:
			anim = io->anims[ANIM_BARE_UNREADY];
			break;
	}

	AcquireLastAnim(io);
	ANIM_Set(&io->animlayer[1], anim);
}
//***********************************************************************************************
//***********************************************************************************************
float ARX_EQUIPMENT_ComputeDamages(INTERACTIVE_OBJ * io_source, INTERACTIVE_OBJ * io_target, float ratioaim, Vec3f * position)
{
	EVENT_SENDER = io_source;
	SendIOScriptEvent(io_target, SM_AGGRESSION);

	if ((!io_source)
	        ||	(!io_target))
		return 0.f;

	if (!(io_target->ioflags & IO_NPC))
	{
		if (io_target->ioflags & IO_FIX)
		{
			if (io_source == inter.iobj[0])
				ARX_DAMAGES_DamageFIX(io_target, player.Full_damages, 0, 0);
			else if (io_source->ioflags & IO_NPC)
				ARX_DAMAGES_DamageFIX(io_target, io_source->_npcdata->damages, GetInterNum(io_source), 0);
			else
				ARX_DAMAGES_DamageFIX(io_target, 1, GetInterNum(io_source), 0);
		}

		return 0.f;
	}

	float attack, ac, damages;
	float backstab = 1.f;

	string _wmat = "bare";
	const string * wmat = &_wmat;
	
	string _amat = "flesh";
	const string * amat = &_amat;

	bool critical = false;

	if(io_source == inter.iobj[0]) {
		
		if(player.equiped[EQUIP_SLOT_WEAPON] != 0 && ValidIONum(player.equiped[EQUIP_SLOT_WEAPON])) {
			INTERACTIVE_OBJ * io = inter.iobj[player.equiped[EQUIP_SLOT_WEAPON]];
			if(io && !io->weaponmaterial.empty()) {
				wmat = &io->weaponmaterial;
			}
		}

		attack = player.Full_damages;

		if (rnd() * 100 <= (float)(player.Full_Attribute_Dexterity - 9) * 2.f + (float)(player.Full_Skill_Close_Combat * ( 1.0f / 5 )))
		{
			if (SendIOScriptEvent(io_source, SM_CRITICAL) != REFUSE)
				critical = true;
		}
		else critical = false;

		damages = attack * ratioaim; 

		if (io_target->_npcdata->npcflags & NPCFLAG_BACKSTAB)
		{
			if (rnd() * 100.f <= player.Full_Skill_Stealth * ( 1.0f / 2 ))
			{
				if (SendIOScriptEvent(io_source, SM_BACKSTAB) != REFUSE)
					backstab = 1.5f; 
			}
		}
	}
	else
	{
		if (!(io_source->ioflags & IO_NPC)) return 0.f; // no NPC source...

		if(!io_source->weaponmaterial.empty()){
			wmat = &io_source->weaponmaterial;
		}
		
		if(io_source->_npcdata->weapon != NULL) {
			INTERACTIVE_OBJ * iow = io_source->_npcdata->weapon;
			if(!iow->weaponmaterial.empty()) {
				wmat = &iow->weaponmaterial;
			}
		}
		
		attack = io_source->_npcdata->tohit;
		
		damages = io_source->_npcdata->damages * ratioaim * (rnd() * ( 1.0f / 2 ) + 0.5f);
#ifdef BUILD_EDITOR
		if(GAME_EDITOR) {
			damages = io_source->_npcdata->damages * ratioaim;
		}
#endif

		long value = ARX_SPELLS_GetSpellOn(io_source, SPELL_CURSE);

		if (value >= 0)
		{
			damages *= (spells[value].caster_level * 0.05f);
		}

		if (rnd() * 100 <= io_source->_npcdata->critical) 
		{
			if (SendIOScriptEvent(io_source, SM_CRITICAL) != REFUSE)
				critical = true;
		}
		else critical = false;

		if (rnd() * 100.f <= (float)io_source->_npcdata->backstab_skill)
		{
			if (SendIOScriptEvent(io_source, SM_BACKSTAB) != REFUSE)
				backstab = 1.5f; 
		}
	}

	float absorb;

	if (io_target == inter.iobj[0])
	{
		ac = player.Full_armor_class;
		absorb = player.Full_Skill_Defense * ( 1.0f / 2 );
	}
	else
	{
		ac = ARX_INTERACTIVE_GetArmorClass(io_target);
		absorb = io_target->_npcdata->absorb;
		long value = ARX_SPELLS_GetSpellOn(io_target, SPELL_CURSE);

		if (value >= 0)
		{
			float modif = (spells[value].caster_level * 0.05f);
			ac *= modif;
			absorb *= modif;
		}
	}


	if(!io_target->armormaterial.empty()) {
		amat = &io_target->armormaterial;
	}

	if(io_target == inter.iobj[0]) {
		if(player.equiped[EQUIP_SLOT_ARMOR] > 0 && ValidIONum(player.equiped[EQUIP_SLOT_ARMOR])) {
			INTERACTIVE_OBJ * io = inter.iobj[player.equiped[EQUIP_SLOT_ARMOR]];
			if(io && !io->armormaterial.empty()) {
				amat = &io->armormaterial;
			}
		}
	}

	float dmgs = damages * backstab;
	dmgs -= dmgs * (absorb * ( 1.0f / 100 ));

	Vec3f pos;
	pos.x = io_target->pos.x;
	pos.y = io_target->pos.y;
	pos.z = io_target->pos.z;
	float power;
	power = dmgs * ( 1.0f / 20 );

	if (power > 1.f) power = 1.f;
	
	power = power * 0.1f + 0.9f;
	
	ARX_SOUND_PlayCollision(*amat, *wmat, power, 1.f, &pos, io_source);


	float chance = 100.f - (ac - attack); 
	float dice = rnd() * 100.f;

	if (dice <= chance) 
	{
		ARX_SOUND_PlayCollision("flesh", *wmat, power, 1.f, &pos, io_source);

		Vec3f pos;
		pos.x = io_target->pos.x;
		pos.y = io_target->pos.y + io_target->physics.cyl.height * ( 1.0f / 2 );
		pos.z = io_target->pos.z;


		if (dmgs > 0.f)
		{
			if (critical)
			{
				dmgs *= 1.5f; 
			}

			if (io_target == inter.iobj[0])
			{
				Vec3f ppos;
				ppos.x = io_source->pos.x - player.pos.x;
				ppos.y = io_source->pos.y - player.pos.y - PLAYER_BASE_HEIGHT;
				ppos.z = io_source->pos.z - player.pos.z;
				fnormalize(ppos);

				//------- player push START
				Vec3f push = ppos;
				push.x *= -dmgs * ( 1.0f / 11 );
				push.y *= -dmgs * ( 1.0f / 30 );
				push.z *= -dmgs * ( 1.0f / 11 );
				PUSH_PLAYER_FORCE += push;
				//------- player push END

				ppos *= 60.f;
				ppos += ACTIVECAM->pos;
				ARX_DAMAGES_DamagePlayer(dmgs, 0, GetInterNum(io_source));
				ARX_DAMAGES_DamagePlayerEquipment(dmgs);
			}
			else
			{

				Vec3f ppos;
				ppos.x = io_source->pos.x - io_target->pos.x;
				ppos.y = io_source->pos.y - io_target->pos.y;
				ppos.z = io_source->pos.z - io_target->pos.z;

				if (io_target == inter.iobj[0]) ppos.y -= PLAYER_BASE_HEIGHT;

				fnormalize(ppos);

				//------- player NPC START
				Vec3f push = ppos;
				push *= -dmgs;
				io_target->forcedmove += push;

				//------- player NPC END
				if (position)
					ARX_DAMAGES_DamageNPC(io_target, dmgs, GetInterNum(io_source), 0, position);
				else
					ARX_DAMAGES_DamageNPC(io_target, dmgs, GetInterNum(io_source), 0, &io_target->pos);
			}
		}

		return dmgs;
	}


	return 0.f;
}

static float ARX_EQUIPMENT_GetSpecialValue(INTERACTIVE_OBJ * io, long val) {
	
	if ((!io) || !(io->ioflags & IO_ITEM) || !io->_itemdata->equipitem) return -1;

	for (long i = IO_EQUIPITEM_ELEMENT_SPECIAL_1; i <= IO_EQUIPITEM_ELEMENT_SPECIAL_4; i++)
	{
		if (io->_itemdata->equipitem->elements[i].special == val)
		{
			return (io->_itemdata->equipitem->elements[i].value);
		}
	}

	return -1;
}

//***********************************************************************************************
// flags & 1 = blood spawn only
//-----------------------------------------------------------------------------------------------
//***********************************************************************************************
bool ARX_EQUIPMENT_Strike_Check(INTERACTIVE_OBJ * io_source, INTERACTIVE_OBJ * io_weapon, float ratioaim, long flags, long targ)
{
	
#ifdef BUILD_EDITOR
	if (TRUEFIGHT) ratioaim = 1.f;
#endif
	
	bool ret = false;
	long source = GetInterNum(io_source);
	long weapon = GetInterNum(io_weapon);
	EERIE_SPHERE sphere;

	Vec3f * v0;
	EXCEPTIONS_LIST_Pos = 0;
	float rad;

	long nbact = io_weapon->obj->actionlist.size();
	float drain_life = ARX_EQUIPMENT_GetSpecialValue(io_weapon, IO_SPECIAL_ELEM_DRAIN_LIFE);
	float paralyse = ARX_EQUIPMENT_GetSpecialValue(io_weapon, IO_SPECIAL_ELEM_PARALYZE);

	for (long j = 0; j < nbact; j++) // TODO iterator
	{
		if (!ValidIONum(weapon)) return false;

		rad = GetHitValue(io_weapon->obj->actionlist[j].name);

		if (rad == -1) continue;

		v0 = &io_weapon->obj->vertexlist3[io_weapon->obj->actionlist[j].idx].v;
		sphere.origin.x = v0->x;
		sphere.origin.y = v0->y;
		sphere.origin.z = v0->z;
	
		sphere.radius = rad; 

		if (source != 0) sphere.radius += 15.f;

		if (CheckEverythingInSphere(&sphere, source, targ))
		{
			for (size_t jj = 0; jj < MAX_IN_SPHERE_Pos; jj++)
			{
				if (ValidIONum(EVERYTHING_IN_SPHERE[jj])
				        && (!(inter.iobj[EVERYTHING_IN_SPHERE[jj]]->ioflags & IO_BODY_CHUNK)))
				{
					long HIT_SPARK = 0;
					EXCEPTIONS_LIST[EXCEPTIONS_LIST_Pos] = EVERYTHING_IN_SPHERE[jj];
					EXCEPTIONS_LIST_Pos++;

					if (EXCEPTIONS_LIST_Pos >= MAX_IN_SPHERE) EXCEPTIONS_LIST_Pos--;

					INTERACTIVE_OBJ * target = inter.iobj[EVERYTHING_IN_SPHERE[jj]];
			
					Vec3f	pos;
					Color color = Color::white;
					long		hitpoint	=	-1;
					float		curdist		=	999999.f;
					
					Vec3f vector;
					vector.x = sphere.origin.x - target->pos.x;
					vector.y = (sphere.origin.y - target->pos.y) * ( 1.0f / 2 );
					vector.z = sphere.origin.z - target->pos.z;
					vector.normalize();

					for (size_t ii = 0; ii < target->obj->facelist.size(); ii++)
					{
						if (target->obj->facelist[ii].facetype & POLY_HIDE) continue;

						float d = dist(sphere.origin, target->obj->vertexlist3[target->obj->facelist[ii].vid[0]].v);

						if (d < curdist)
						{
							hitpoint = target->obj->facelist[ii].vid[0];
							curdist = d;
						}
					}

					if(hitpoint >= 0) {
						color = (target->ioflags & IO_NPC) ? target->_npcdata->blood_color : Color::white;
						pos = target->obj->vertexlist3[hitpoint].v;
					}
					else ARX_DEAD_CODE(); 
					
					float dmgs = 0.f;
					if (!(flags & 1))
					{
						Vec3f posi;

						if (hitpoint >= 0)
						{
							posi = target->obj->vertexlist3[hitpoint].v;
							dmgs = ARX_EQUIPMENT_ComputeDamages(io_source, target, ratioaim, &posi);

						}
						else
						{
							dmgs = ARX_EQUIPMENT_ComputeDamages(io_source, target, ratioaim);

						}

						if (target->ioflags & IO_NPC)
						{
							ret = true;
							target->spark_n_blood = 0;
							target->_npcdata->SPLAT_TOT_NB = 0;

							if (drain_life > 0.f)
							{
								float life_gain = min(dmgs, drain_life);
								life_gain = min(life_gain, target->_npcdata->life);
								life_gain = max(life_gain, 0.f);
								ARX_DAMAGES_HealInter(io_source, life_gain);
							}

							if (paralyse > 0.f)
							{
								float ptime = min(dmgs * 1000.f, paralyse);
								ARX_SPELLS_Launch(SPELL_PARALYSE, weapon, SPELLCAST_FLAG_NOMANA | SPELLCAST_FLAG_NOCHECKCANCAST
								                  , 5, EVERYTHING_IN_SPHERE[jj], (long)(ptime));
							}
						}

						if (io_source == inter.iobj[0])
						{
							ARX_DAMAGES_DurabilityCheck(io_weapon, 0.2f);
						}
					}

					if ((dmgs > 0.f) || ((target->ioflags & IO_NPC) && (target->spark_n_blood == SP_BLOODY)))
					{
						if (target->ioflags & IO_NPC)
						{
							target->spark_n_blood = SP_BLOODY;

							if (!(flags & 1))
							{
								ARX_PARTICLES_Spawn_Splat(pos, dmgs, color);

								EERIE_SPHERE sp;
								float power;
								power = (dmgs * ( 1.0f / 40 )) + 0.7f;
								Vec3f vect;
								vect.x = target->obj->vertexlist3[hitpoint].v.x - io_source->pos.x;
								vect.y = 0;
								vect.z = target->obj->vertexlist3[hitpoint].v.z - io_source->pos.z;
								fnormalize(vect);
								sp.origin.x = target->obj->vertexlist3[hitpoint].v.x + vect.x * 30.f;
								sp.origin.y = target->obj->vertexlist3[hitpoint].v.y;
								sp.origin.z = target->obj->vertexlist3[hitpoint].v.z + vect.z * 30.f;
								sp.radius = 3.5f * power * 20;

								if(CheckAnythingInSphere(&sp, 0, CAS_NO_NPC_COL)) {
									Color3f rgb = color.to<float>();
									SpawnGroundSplat(&sp, &rgb, 30, 1);
								}
							}

							ARX_PARTICLES_Spawn_Blood2(pos, dmgs, color, target);

							if (!ValidIONum(weapon)) io_weapon = NULL;
						}
						else
						{
							if (target->ioflags & IO_ITEM)
								ARX_PARTICLES_Spawn_Spark(&pos, rnd() * 3.f, 0);
							else
								ARX_PARTICLES_Spawn_Spark(&pos, rnd() * 30.f, 0);

							ARX_NPC_SpawnAudibleSound(&pos, io_source);

							if (io_source == inter.iobj[0])
								HIT_SPARK = 1;
						}
					}
					else if ((target->ioflags & IO_NPC)
					         &&	((dmgs <= 0.f) || (target->spark_n_blood == SP_SPARKING)))
					{
						long nb;

						if (target->spark_n_blood == SP_SPARKING) 
							nb = Random::get(0, 3);
						else
							nb = 30;

						if (target->ioflags & IO_ITEM)
							nb = 1;

						ARX_PARTICLES_Spawn_Spark(&pos, (float)nb, 0); 
						ARX_NPC_SpawnAudibleSound(&pos, io_source);
						target->spark_n_blood = SP_SPARKING;

						if (!(target->ioflags & IO_NPC))
							HIT_SPARK = 1;
					}
					else if ((dmgs <= 0.f)
					         &&	((target->ioflags & IO_FIX) || (target->ioflags & IO_ITEM)))
					{
						long  nb;

						if (target->spark_n_blood == SP_SPARKING)
							nb = Random::get(0, 3);
						else
							nb = 30;

						if (target->ioflags & IO_ITEM)
							nb = 1;

						ARX_PARTICLES_Spawn_Spark(&pos, (float)nb, 0);
						ARX_NPC_SpawnAudibleSound(&pos, io_source);
						target->spark_n_blood = SP_SPARKING;

						if (!(target->ioflags & IO_NPC))
							HIT_SPARK = 1;
					}

					if (HIT_SPARK)
					{
						if (!(io_source->aflags & IO_NPC_AFLAG_HIT_BACKGROUND))
						{
							ARX_DAMAGES_DurabilityCheck(io_weapon, 1.f);
							io_source->aflags |= IO_NPC_AFLAG_HIT_BACKGROUND;

							if (!ValidIONum(weapon))
							{
								io_weapon = NULL;
							}
							else
							{
								string _weapon_material = "metal";
								const string * weapon_material = &_weapon_material;

								if(io_weapon && !io_weapon->weaponmaterial.empty()) {
									weapon_material = &io_weapon->weaponmaterial;
								}

								char bkg_material[128];

								if (ARX_MATERIAL_GetNameById(target->material, bkg_material))
									ARX_SOUND_PlayCollision(*weapon_material, bkg_material, 1.f, 1.f, &sphere.origin, NULL);
							}
						}
					}

				}
			}
		}


		EERIEPOLY * ep = CheckBackgroundInSphere(&sphere);
		if (ep)
		{
			if (io_source == inter.iobj[0])
			{
				if (!(io_source->aflags & IO_NPC_AFLAG_HIT_BACKGROUND))
				{
					ARX_DAMAGES_DurabilityCheck(io_weapon, 1.f);
					io_source->aflags |= IO_NPC_AFLAG_HIT_BACKGROUND;

					if (!ValidIONum(weapon))
					{
						io_weapon = NULL;
					}
					else
					{
						string _weapon_material = "metal";
						const string * weapon_material = &_weapon_material;
						if(io_weapon && !io_weapon->weaponmaterial.empty()) {
							weapon_material = &io_weapon->weaponmaterial;
						}

						std::string bkg_material = "earth";

						if (ep &&  ep->tex && !ep->tex->m_texName.empty())
							bkg_material = GetMaterialString( ep->tex->m_texName );

						ARX_SOUND_PlayCollision(*weapon_material, bkg_material, 1.f, 1.f, &sphere.origin, io_source);
					}
				}
			}

			ARX_PARTICLES_Spawn_Spark(&sphere.origin, rnd() * 10.f, 0);
			ARX_NPC_SpawnAudibleSound(&sphere.origin, io_source);
		}
	}

	return ret;
}

//***********************************************************************************************
//-----------------------------------------------------------------------------------------------
//***********************************************************************************************
void ARX_EQUIPMENT_LaunchPlayerReadyWeapon()
{
	INTERACTIVE_OBJ * io = inter.iobj[0];

	if (!io) return;

	long type = ARX_EQUIPMENT_GetPlayerWeaponType();
	ANIM_HANDLE * anim = NULL;

	switch (type)
	{
		case WEAPON_DAGGER:
			anim = io->anims[ANIM_DAGGER_READY_PART_1];
			break;
		case WEAPON_1H:
			anim = io->anims[ANIM_1H_READY_PART_1];
			break;
		case WEAPON_2H:

			if (player.equiped[EQUIP_SLOT_SHIELD] == 0)
				anim = io->anims[ANIM_2H_READY_PART_1];

			break;
		case WEAPON_BOW:

			if (player.equiped[EQUIP_SLOT_SHIELD] == 0)
				anim = io->anims[ANIM_MISSILE_READY_PART_1];

			break;
		default:
			anim = io->anims[ANIM_BARE_READY];
			break;
	}

	AcquireLastAnim(io);
	ANIM_Set(&io->animlayer[1], anim);
}

//***********************************************************************************************
//-----------------------------------------------------------------------------------------------
//***********************************************************************************************

void ARX_EQUIPMENT_UnEquipPlayerWeapon()
{
	if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
	        &&	ValidIONum(player.equiped[EQUIP_SLOT_WEAPON]))
	{
		INTERACTIVE_OBJ * pioOldDragInter;
		pioOldDragInter = DRAGINTER;
		DRAGINTER = inter.iobj[player.equiped[EQUIP_SLOT_WEAPON]];

		if (DRAGINTER)
			ARX_SOUND_PlayInterface(SND_INVSTD);

		ARX_EQUIPMENT_UnEquip(inter.iobj[0], inter.iobj[player.equiped[EQUIP_SLOT_WEAPON]]);
		DRAGINTER = pioOldDragInter;
	}

	player.equiped[EQUIP_SLOT_WEAPON] = 0;
}

bool bRing = false;

//***********************************************************************************************
//-----------------------------------------------------------------------------------------------
//***********************************************************************************************
void ARX_EQUIPMENT_Equip(INTERACTIVE_OBJ * target, INTERACTIVE_OBJ * toequip)
{
	if (!target) return;

	if (!toequip) return;

	if (target != inter.iobj[0]) return;

	long validid = -1;

	for (long i = 0; i < inter.nbmax; i++)
	{
		if (inter.iobj[i] == toequip)
		{
			validid = i;
			break;
		}
	}

	if (validid == -1) return;

	RemoveFromAllInventories(toequip);
	toequip->show = SHOW_FLAG_ON_PLAYER; // on player

	if (toequip == DRAGINTER)
		Set_DragInter(NULL);

	if ((toequip->type_flags & OBJECT_TYPE_DAGGER)
	        ||	(toequip->type_flags & OBJECT_TYPE_1H)
	        ||	(toequip->type_flags & OBJECT_TYPE_2H)
	        ||	(toequip->type_flags & OBJECT_TYPE_BOW)
	   )
	{
		if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
		        &&	ValidIONum(player.equiped[EQUIP_SLOT_WEAPON]))
		{
			ARX_EQUIPMENT_UnEquip(target, inter.iobj[player.equiped[EQUIP_SLOT_WEAPON]]);
		}

		player.equiped[EQUIP_SLOT_WEAPON] = (short)validid;

		if (toequip->type_flags & OBJECT_TYPE_BOW)
		{
			EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "weapon_attach", "test", toequip); //
		}
		else
		{
			EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "weapon_attach", "primary_attach", toequip); //
		}

		if ((toequip->type_flags & OBJECT_TYPE_2H) || (toequip->type_flags & OBJECT_TYPE_BOW))
		{
			if (player.equiped[EQUIP_SLOT_SHIELD] != 0)
			{
				ARX_EQUIPMENT_UnEquip(target, inter.iobj[player.equiped[EQUIP_SLOT_SHIELD]]);
			}
		}
	}
	else if (toequip->type_flags & OBJECT_TYPE_SHIELD)
	{
		if ((player.equiped[EQUIP_SLOT_SHIELD] != 0)
		        &&	ValidIONum(player.equiped[EQUIP_SLOT_SHIELD]))
		{
			ARX_EQUIPMENT_UnEquip(target, inter.iobj[player.equiped[EQUIP_SLOT_SHIELD]]);
		}

		player.equiped[EQUIP_SLOT_SHIELD] = (short)validid;
		EERIE_LINKEDOBJ_LinkObjectToObject(target->obj, toequip->obj, "shield_attach", "shield_attach", toequip);

		if ((player.equiped[EQUIP_SLOT_WEAPON] != 0)
		        &&	ValidIONum(player.equiped[EQUIP_SLOT_WEAPON]))
		{
			if ((inter.iobj[player.equiped[EQUIP_SLOT_WEAPON]]->type_flags & OBJECT_TYPE_2H) ||
			        (inter.iobj[player.equiped[EQUIP_SLOT_WEAPON]]->type_flags & OBJECT_TYPE_BOW))
			{
				ARX_EQUIPMENT_UnEquip(target, inter.iobj[player.equiped[EQUIP_SLOT_WEAPON]]);
			}
		}
	}
	else if (toequip->type_flags & OBJECT_TYPE_RING)
	{
		// check first, if not already equiped
		if (!((ValidIONum(player.equiped[EQUIP_SLOT_RING_LEFT]) && (toequip == inter.iobj[player.equiped[EQUIP_SLOT_RING_LEFT]]))
		        ||	(ValidIONum(player.equiped[EQUIP_SLOT_RING_RIGHT]) && (toequip == inter.iobj[player.equiped[EQUIP_SLOT_RING_RIGHT]]))))
		{
			long willequip = -1;

			if (player.equiped[EQUIP_SLOT_RING_LEFT] == 0) willequip = EQUIP_SLOT_RING_LEFT;

			if (player.equiped[EQUIP_SLOT_RING_RIGHT] == 0) willequip = EQUIP_SLOT_RING_RIGHT;

			if (willequip == -1)
			{
				if (bRing)
				{
					if (ValidIONum(player.equiped[EQUIP_SLOT_RING_RIGHT]))
						ARX_EQUIPMENT_UnEquip(target, inter.iobj[player.equiped[EQUIP_SLOT_RING_RIGHT]]);

					willequip = EQUIP_SLOT_RING_RIGHT;
				}
				else
				{
					if (ValidIONum(player.equiped[EQUIP_SLOT_RING_LEFT]))
						ARX_EQUIPMENT_UnEquip(target, inter.iobj[player.equiped[EQUIP_SLOT_RING_LEFT]]);

					willequip = EQUIP_SLOT_RING_LEFT;
				}

				bRing = !bRing;
			}

			player.equiped[willequip] = (short)validid;
		}
	}
	else if (toequip->type_flags & OBJECT_TYPE_ARMOR)
	{
		if ((player.equiped[EQUIP_SLOT_ARMOR] != 0)
		        &&	ValidIONum(player.equiped[EQUIP_SLOT_ARMOR]))
		{
			ARX_EQUIPMENT_UnEquip(target, inter.iobj[player.equiped[EQUIP_SLOT_ARMOR]]);
		}

		player.equiped[EQUIP_SLOT_ARMOR] = (short)validid;
	}
	else if (toequip->type_flags & OBJECT_TYPE_LEGGINGS)
	{
		if ((player.equiped[EQUIP_SLOT_LEGGINGS] != 0)
		        &&	ValidIONum(player.equiped[EQUIP_SLOT_LEGGINGS]))
		{
			ARX_EQUIPMENT_UnEquip(target, inter.iobj[player.equiped[EQUIP_SLOT_LEGGINGS]]);
		}

		player.equiped[EQUIP_SLOT_LEGGINGS] = (short)validid;
	}
	else if (toequip->type_flags & OBJECT_TYPE_HELMET)
	{
		if ((player.equiped[EQUIP_SLOT_HELMET] != 0)
		        &&	(ValidIONum(player.equiped[EQUIP_SLOT_HELMET])))
		{
			ARX_EQUIPMENT_UnEquip(target, inter.iobj[player.equiped[EQUIP_SLOT_HELMET]]);
		}

		player.equiped[EQUIP_SLOT_HELMET] = (short)validid;
	}

	if ((toequip->type_flags & OBJECT_TYPE_HELMET)
	        ||	(toequip->type_flags & OBJECT_TYPE_ARMOR)
	        ||	(toequip->type_flags & OBJECT_TYPE_LEGGINGS))
		ARX_EQUIPMENT_RecreatePlayerMesh();

	ARX_PLAYER_ComputePlayerFullStats();
}

bool ARX_EQUIPMENT_SetObjectType(INTERACTIVE_OBJ & io, const string & temp, bool set) {
	
	ItemType flag = ARX_EQUIPMENT_GetObjectTypeFlag(temp);
	
	if(set) {
		io.type_flags |= flag;
	} else {
		io.type_flags &= ~flag;
	}
	
	return (flag != 0);
}

//***********************************************************************************************
// Initializes Equipment infos
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/29)
//***********************************************************************************************
void ARX_EQUIPMENT_Init()
{
	// IO_EQUIPITEM_ELEMENT_... are Defined in EERIEPOLY.h
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_STRENGTH].name, "strength");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_DEXTERITY].name, "dexterity");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_CONSTITUTION].name, "constitution");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_MIND].name, "intelligence");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Stealth].name, "stealth");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Mecanism].name, "mecanism");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Intuition].name, "intuition");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Etheral_Link].name, "etheral_link");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Object_Knowledge].name, "object_knowledge");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Casting].name, "casting");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Projectile].name, "projectile");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Close_Combat].name, "close_combat");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Defense].name, "defense");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Armor_Class].name, "armor_class");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Resist_Magic].name, "resist_magic");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Resist_Poison].name, "resist_poison");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Critical_Hit].name, "critical_hit");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Damages].name, "damages");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Duration].name, "duration");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_AimTime].name, "aim_time");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Identify_Value].name, "identify_value");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Life].name, "life");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_Mana].name, "mana");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_MaxLife].name, "maxlife");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_MaxMana].name, "maxmana");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_SPECIAL_1].name, "special1");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_SPECIAL_2].name, "special2");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_SPECIAL_3].name, "special3");
	strcpy(equipinfo[IO_EQUIPITEM_ELEMENT_SPECIAL_4].name, "special4");
}

//***********************************************************************************************
// Removes All special equipement properties
//-----------------------------------------------------------------------------------------------
// VERIFIED (Cyril 2001/10/29)
//***********************************************************************************************
void ARX_EQUIPMENT_Remove_All_Special(INTERACTIVE_OBJ * io)
{
	if (!io) return;

	if (!(io->ioflags & IO_ITEM)) return;

	io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_SPECIAL_1].special = IO_SPECIAL_ELEM_NONE;
	io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_SPECIAL_2].special = IO_SPECIAL_ELEM_NONE;
	io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_SPECIAL_3].special = IO_SPECIAL_ELEM_NONE;
	io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_SPECIAL_4].special = IO_SPECIAL_ELEM_NONE;
}
//***********************************************************************************************
// Sets an equipment property
//-----------------------------------------------------------------------------------------------
//***********************************************************************************************
float ARX_EQUIPMENT_Apply(INTERACTIVE_OBJ * io, long ident, float trueval)
{
	if (io == NULL) return trueval;

	if (io != inter.iobj[0]) return trueval;

	float toadd = 0;

	for (long i = 0; i < MAX_EQUIPED; i++)
	{
		if ((player.equiped[i] != 0)
		        &&	ValidIONum(player.equiped[i]))
		{
			INTERACTIVE_OBJ * toequip = inter.iobj[player.equiped[i]];

			if ((toequip) && (toequip->ioflags & IO_ITEM) && (toequip->_itemdata->equipitem))
			{
				IO_EQUIPITEM_ELEMENT * elem = &toequip->_itemdata->equipitem->elements[ident];

				if (!(elem->flags & 1)) 
					toadd += elem->value;
			}
		}
	}

	return toadd;
}

float ARX_EQUIPMENT_ApplyPercent(INTERACTIVE_OBJ * io, long ident, float trueval)
{
	if (io == NULL) return trueval;

	if (io != inter.iobj[0]) return trueval;

	float toadd = 0;

	for (long i = 0; i < MAX_EQUIPED; i++)
	{
		if ((player.equiped[i] != 0)
		        &&	ValidIONum(player.equiped[i]))
		{
			INTERACTIVE_OBJ * toequip = inter.iobj[player.equiped[i]];

			if ((toequip) && (toequip->ioflags & IO_ITEM) && (toequip->_itemdata->equipitem))
			{
				IO_EQUIPITEM_ELEMENT * elem = &toequip->_itemdata->equipitem->elements[ident];

				if (elem->flags & 1) // percentile value...
				{
					toadd += elem->value;
				}
			}
		}
	}

	return (toadd * trueval * ( 1.0f / 100 ));
}

void ARX_EQUIPMENT_SetEquip(INTERACTIVE_OBJ * io, bool special, const std::string & param2, float val, short flags)
{
	if (io == NULL) return;

	if (!(io->ioflags & IO_ITEM)) return;

	if (!io->_itemdata->equipitem)
	{
		io->_itemdata->equipitem = (IO_EQUIPITEM *) malloc(sizeof(IO_EQUIPITEM));

		if (io->_itemdata->equipitem == NULL) return;

		memset(io->_itemdata->equipitem, 0, sizeof(IO_EQUIPITEM));
		io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Duration].value = 10;
		io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_AimTime].value = 10;
		io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value = 0;
		io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Identify_Value].value = 0;
	}

	if(special) {
		for (long i = IO_EQUIPITEM_ELEMENT_SPECIAL_1; i <= IO_EQUIPITEM_ELEMENT_SPECIAL_4; i++)
		{
			if (io->_itemdata->equipitem->elements[i].special == IO_SPECIAL_ELEM_NONE)
			{
				if(param2 == "paralyse") {
					io->_itemdata->equipitem->elements[i].special = IO_SPECIAL_ELEM_PARALYZE;
				} else if(param2 == "drainlife") {
					io->_itemdata->equipitem->elements[i].special = IO_SPECIAL_ELEM_DRAIN_LIFE;
				}

				io->_itemdata->equipitem->elements[i].value = val;
				io->_itemdata->equipitem->elements[i].flags = flags;
				return;
			}
		}

		return;
		
	} else {
		for(long i = 0; i < IO_EQUIPITEM_ELEMENT_Number; i++) {
			if(param2 == equipinfo[i].name) {
				io->_itemdata->equipitem->elements[i].value = val;
				io->_itemdata->equipitem->elements[i].special = 0;
				io->_itemdata->equipitem->elements[i].flags = flags;
				return;
			}
		}
	}
}

//-----------------------------------------------------------------------------
void ARX_EQUIPMENT_IdentifyAll()
{
	INTERACTIVE_OBJ * io = inter.iobj[0];

	if (io == NULL) return;

	if (io != inter.iobj[0]) return;

	for (long i = 0; i < MAX_EQUIPED; i++)
	{
		if ((player.equiped[i] != 0)
		        &&	ValidIONum(player.equiped[i]))
		{
			INTERACTIVE_OBJ * toequip = inter.iobj[player.equiped[i]];

			if ((toequip) && (toequip->ioflags & IO_ITEM) && (toequip->_itemdata->equipitem))
			{
				if (player.Full_Skill_Object_Knowledge + player.Full_Attribute_Mind
				        >= toequip->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Identify_Value].value)
				{
					SendIOScriptEvent(toequip, SM_IDENTIFY);
				}
			}
		}
	}
}
float GetHitValue( const std::string& name)
{
	long len = name.length();

	if (len < 5) return -1;

	if ((name[0] == 'h')
	        && (name[1] == 'i')
	        && (name[2] == 't')
	        && (name[3] == '_'))
	{
		long val = atoi( name.substr(4) ); // Get the number after the first 4 characters in the string
		return (float)val;
	}

	return -1;
}
