

//==========================================================================================
//Update events
//==========================================================================================

void LAi_Character_FrameUpdate()
{
	float dltTime = GetEventData();
	//Обновляем состояние персонажей
	LAi_AllCharactersUpdate(dltTime);
	//Задержка исполнения квестов
	LAi_QuestDelayProcess(dltTime);
}

//MAXIMUS -->
void LAi_CharacterTypeUpdate()
{
	//Параметры
	string chID = GetEventData();
	aref chr = CharacterFromID(chID);
	if(!CheckAttribute(chr,"chr_ai.type")) return;
	float dltTime = 0.0;
	//Процессируем соответствующий тип
	string func = chr.chr_ai.type;
	if(func != "")
	{
		func = "LAi_type_" + func + "_CharacterUpdate";
		call func(chr, dltTime);
	}
	//Процессируем соответствующий шаблон
	func = chr.chr_ai.tmpl;
	if(func == "") return;
	func = "LAi_tmpl_" + func + "_CharacterUpdate";
	call func(chr, dltTime);
	DelEventHandler("CharacterTypeUpdate", "LAi_CharacterTypeUpdate");
}
//MAXIMUS <--

void LAi_CharacterUpdate()
{
	//Параметры
	aref chr = GetEventData();
	float dltTime = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_CharacterUpdate") == false) return;
	//Процессируем соответствующий тип
	string func = chr.chr_ai.type;
	if(func != "")
	{
		func = "LAi_type_" + func + "_CharacterUpdate";
		call func(chr, dltTime);
	}
	//Процессируем соответствующий шаблон
	func = chr.chr_ai.tmpl;
	if(func == "") return;
	func = "LAi_tmpl_" + func + "_CharacterUpdate";
	call func(chr, dltTime);
}

//==========================================================================================
//EndTask events
//==========================================================================================

void LAi_CharacterEndTask()
{
	//Параметры
	string endTask = GetEventData();
	aref chr = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_CharacterEndTask") == false) return;
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	//Определяем отработавшую задачу
	bool isProcessed = false;
	switch(endTask)
	{
	case "Goto point":
		func = "LAi_tmpl_" + func + "_EndGoToPoint";
		isProcessed = true;
		break;
	case "Runto point":
		func = "LAi_tmpl_" + func + "_EndRunToPoint";
		isProcessed = true;
		break;
	case "Escape":
		func = "LAi_tmpl_" + func + "_EndEscape";
		isProcessed = true;
		break;
	}
	if(isProcessed != false)
	{
		call func(chr);
	}else{
		Trace("LAi_CharacterEndTask -> unknow end task <" + endTask + ">");
	}
}

//==========================================================================================
//TaskFailure events
//==========================================================================================

void LAi_CharacterTaskFailure()
{
	//Параметры
	string endTask = GetEventData();
	aref chr = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_CharacterTaskFailure") == false) return;
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	//Определяем невыполнившиюся задачу
	bool isProcessed = false;
	switch(endTask)
	{
	case "Goto point":
		func = "LAi_tmpl_" + func + "_FailureGoToPoint";
		isProcessed = true;
		break;
	case "Runto point":
		func = "LAi_tmpl_" + func + "_FailureRunToPoint";
		isProcessed = true;
		break;
	case "Follow character":
		func = "LAi_tmpl_" + func + "_FailureFollow";
		isProcessed = true;
		break;
	case "Fight":
		func = "LAi_tmpl_" + func + "_FailureFight";
		isProcessed = true;
		break;
	case "Escape":
		func = "LAi_tmpl_" + func + "_FailureEscape";
		isProcessed = true;
		break;
	}
	if(isProcessed != false)
	{
		call func(chr);
	}else{
		Trace("LAi_CharacterTaskFailure -> unknow failure task <" + endTask + ">");
	}
}

void LAi_CharacterBusyPos()
{
	//Параметры
	aref chr = GetEventData();
	float x = GetEventData();
	float y = GetEventData();
	float z = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_CharacterTaskFailure") == false) return;
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	func = "LAi_tmpl_" + func + "_BusyPos";
	call func(chr, x, y, z);
}


//==========================================================================================
//Follow character's events
//==========================================================================================

void LAi_CharacterFollowGo()
{
	//Параметры
	string endTask = GetEventData();
	aref chr = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_CharacterFollowGo") == false) return;
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	//Исполнение
	func = "LAi_tmpl_" + func + "_FollowGo";
	call func(chr);
}

void LAi_CharacterFollowStay()
{
	//Параметры
	string endTask = GetEventData();
	aref chr = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_CharacterFollowStay") == false) return;
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	//Исполнение
	func = "LAi_tmpl_" + func + "_FollowStay";
	call func(chr);
}

//==========================================================================================
//Fight character's events
//==========================================================================================

void LAi_CharacterFightGo()
{
	//Параметры
	string endTask = GetEventData();
	aref chr = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_CharacterFightGo") == false) return;
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	//Исполнение
	func = "LAi_tmpl_" + func + "_FightGo";
	call func(chr);
}

void LAi_CharacterFightStay()
{
	//Параметры
	string endTask = GetEventData();
	aref chr = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_CharacterFightStay") == false) return;
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	//Исполнение
	func = "LAi_tmpl_" + func + "_FightStay";
	call func(chr);
}

void LAi_CharacterAttack()
{
	//int tmpLangFileID = LanguageOpenFile("interface_strings.txt");
	aref attack = GetEventData();
	aref enemy = GetEventData();
	float attackDmg = GetEventData();
	float hitDmg = GetEventData();

	// end of unchanged original code

	bool CheckHostile = true; // run LAi_Attacked etc. set-hostile code. NK 05-07-10
	bool SetHostile = true;
	string effects_to_play = "always"; // for selective playing of FX
	int i;
	ref mainCh = GetMainCharacter();//MAXIMUS
	bool bPlayer = false;//MAXIMUS

	//skillearning(attack, "Fencing", 1);	// ccc skillearning

	// ccc special weapon assembly kit start
	// note: the function we are in here runs if a character (referred to as "attack") uses a blade on a targetcharacter ("enemy")
	if(CheckAttribute(attack, "equip.blade")) // NK just in case.
	{
		string weaponID = GetCharacterEquipByGroup(attack,BLADE_ITEM_TYPE); // defines weaponname
		aref weapon;
		Items_FindItem(weaponID, &weapon);	// defines object for weaponattributes

		if( CheckAttribute(weapon, "sound" ) ) { LAi_CharacterPlaySound(attack, weapon.sound ); }
			// plays soundfile if defined in items\itemsinit.c


	// move FX to bottom. NK 05-07-10

// NK move hardcoded chances to the weapon attribute. 05-07-10

		// NK add check so only works for pchar and only works on person who is not pchar's group. 05-07-06
		if( CheckAttribute(weapon, "steal" ) && sti(attack.index) == GetMainCharacterIndex() && !LAi_IsFightMode(enemy) && attack.chr_ai.group != enemy.chr_ai.group)
		 	// if weapon is a thief's tool; works only if victim is not prepared for fight
		{
			CheckHostile = false; // NK for stealth check below
			//skillearning(attack, "sneak", 1);	// ccc skillearning

			LAi_AttemptSteal(attack, enemy, weapon); // NK do this in one function. 05-07-10
		}
		if( CheckAttribute(weapon, "stun" ) && !LAi_IsFightMode(enemy) && enemy.chr_ai.group != LAI_GROUP_PLAYER )
		// if weapon stuns victim; works only if victim is not prepared for fight and no playerassociate
		{
			//skillearning(attack, "sneak", 1);	// ccc skillearning
			if(frnd() < stf(weapon.stun)*GetAttackLuckMod(&attack))	// jun 05 stun doesnt work always anymore
			{
				int duration = 100;
				if(CheckAttribute(weapon, "stun.duration")) duration = sti(weapon.stun.duration);
				//MAXIMUS: during fight, character can join to his crew. Sometimes it may be helpful -->
				bPlayer = false;
				if(CheckAttribute(enemy, "chr_ai.group") && enemy.chr_ai.group==LAI_GROUP_PLAYER)
				{
					if(sti(mainCh.skill.Leadership)>sti(enemy.skill.Leadership)) bPlayer = true;
				}
				else
				{
					if(sti(mainCh.skill.Leadership)<sti(enemy.skill.Leadership)) bPlayer = true;
				}
				LAi_Stunned_StunCharacter(enemy, duration, true, STUN_STARS, bPlayer);
				//MAXIMUS: during fight, character can join to his crew. Sometimes it may be helpful <--
				//effects_to_play += " stun";
			}
		}

		if( CheckAttribute(weapon, "poison" ) ) 	// if weapon poisonous
		{
			Log_SetStringToLog(TranslateString("","Victim poisoned!"));
			enemy.chr_ai.poison =  300 ;		// initial targetchr poisoned
			LAi_CharacterPlaySound(enemy, "OBJECTS\Voices\dead\male\dead4.wav");
			effects_to_play += " poison";
		}
		// NK fix for stealing weapons 05-07-06
		// 05-07-10 reorder this, do actual hostile-setting at bottom.
		if(CheckHostile)
		{
			if(CheckAttribute(weapon, "stealth"))	// silent weapons don't alarm the victim, for others next line runs
			{
		          	if(frnd() < stf(weapon.stealth)*GetAttackLuckMod(&attack)) { SetHostile = false; }
				else { LAi_CharacterPlaySound(enemy,"VOICE\" + LanguageGetLanguage() + "\Eng_m_a_067.wav"); } // sound alarm
			}
		}
		else SetHostile = false;
		
		if( CheckAttribute(weapon, "throw" ) )	// for onetime use
		{
			if(GetCharacterItem(attack, weaponID) <= 1) RemoveCharacterEquip(attack, weapon.groupID);	// unequips thrown weapon
	              TakeItemFromCharacter(attack, weaponID );		// deletes thrown weapon from inventory
		}


		if( CheckAttribute(weapon, "fist" ) && !LAi_IsImmortal(enemy))
		// PB: New weapons attribute, exclusively for your fists
		//     Can't knock out immortal characters, nor can you do damage to them
		{
			int rank = makeint(attack.rank);
			int punch = rand(rank) + rank;
			// Calculates the strength of your punch; character's rank increases damage

			LAi_ApplyCharacterDamage(enemy, punch);
			// Applies damage to enemy
			LAi_CharacterPlaySound(enemy, "OBJECTS\duel\punch"+sti(Rand(5)+1)+".wav");//MAXIMUS: added 3 new sounds from Pirates
			// Play a random punch impact sound

			LAi_CheckKillCharacter(enemy); // Checks if this has killed the character

			if(!LAi_IsDead(enemy)) //checks if char now dead or not
			{
				if(rand(100) < punch + sti(attack.skill.Sneak) && sti(enemy.index) != GetMainCharacterIndex())
				// Checks if punch knocks enemy out; character's rank increases chance
				{	//MAXIMUS: during fight, character can join to his crew. Sometimes it may be helpful -->
					bPlayer = false;
					if(CheckAttribute(enemy, "chr_ai.group") && enemy.chr_ai.group==LAI_GROUP_PLAYER)
					{
						if(sti(mainCh.skill.Leadership)>sti(enemy.skill.Leadership)) bPlayer = true;
					}
					else
					{
						if(sti(mainCh.skill.Leadership)<sti(enemy.skill.Leadership)) bPlayer = true;
					}
					LAi_Stunned_StunCharacter(enemy, 100, true, STUN_STARS, bPlayer);
					//MAXIMUS: during fight, character can join to his crew. Sometimes it may be helpful <--
					SetHostile = false;
				}
			}
		}

		// CCC and PB: New attribute to weapons to allow for disarming opponents
		bool tbool = CheckAttribute(enemy, "equip.blade");
		if(tbool) tbool = GetCharacterEquipByGroup(enemy,BLADE_ITEM_TYPE) != "bladeX4";
		if( DISARM_MODE && CheckAttribute(weapon, "disarm" ) &&  LAi_IsFightMode(enemy) && !CheckAttribute(enemy, "nodisarm" ) && tbool)
		{
			int disarmbonus = 0;
			if(CheckAttribute(attack,"perks.list.SwordplayProfessional")) {disarmbonus = 20;}
			// if attacker has professional fencing skill, increase disarm chance with 20%

			int disarmpenalty = 0;
			if(CheckAttribute(enemy,"perks.list.SwordplayProfessional")) {disarmpenalty = 10;}
			// if attacked victim has professional fencing skill, decrease disarm chance with 10%

			if(sti(rand(100)+enemy.skill.Fencing) < sti(weapon.disarm)+sti(attack.skill.Fencing) + disarmbonus - disarmpenalty)
			{
				LAi_CharacterPlaySound(enemy,"OBJECTS\duel\sword_fallen.wav");
				string enemy_blade = GetCharacterEquipByGroup(enemy,BLADE_ITEM_TYPE);
				RemoveCharacterEquip(enemy,  BLADE_ITEM_TYPE );
				GiveItem2Character(attack, enemy_blade);

				if ( enemy.chr_ai.group !=  LAI_GROUP_PLAYER && !bAbordageStarted )
				// no disarming opponents during boarding
				{
					LAi_SetCitizenTypeNoGroup(enemy);
					Log_SetStringToLog(TranslateString("","Opponent disarmed !"));
				}
				else
				{
					TakeItemFromCharacter(enemy, enemy_blade);
					if ( sti(enemy.index) != GetMainCharacterIndex() && !bAbordageStarted ) // if NOT player
					// officers safe from being disarmed boarding; player NOT safe from being disarmed during boarding
					{
						RemoveCharacterEquip(attack, BLADE_ITEM_TYPE );			// makes enemy use your blade against your officer
						EquipCharacterByItem(attack, enemy_blade);			// and enables you to get it back by killing him/her
						Log_SetStringToLog(TranslateString("","One of your officers was disarmed!"));
						LAi_tmpl_afraid_SetAfraidCharacter(enemy, attack, true);	// makes officer flee
						LAi_SetAfraidDead(enemy);
					}
					else
					{
						RemoveCharacterEquip(attack, BLADE_ITEM_TYPE );			// makes enemy use your blade against you
						EquipCharacterByItem(attack, enemy_blade);			// and enables you to get it back by killing him/her
						EquipCharacterByItem(enemy, "bladeX4");
						Log_SetStringToLog(TranslateString("","You were disarmed!"));
					}
				}
			}
		}

		//Реакция груп на атаку
		// ccc LAi_group_Attack(attack, enemy);	// line moved into "stealth" section


		//Начисление повреждений

		// ccc particle effects, runs only if effect defined  in inititems.c
		LAi_PlayHitGFX(attack, enemy, weapon, effects_to_play); // NK do in centralized function 05-07-10

		if(!CheckAttribute(weapon, "fist" ) )	// PB: Fists will only do damage once with one hit
		{LAi_ApplyCharacterAttackDamage(attack, enemy, attackDmg, hitDmg);}
	}

	// TIH --> prevent stunned characters getting hit, causing everyone to go hostile with them
	if(CheckAttribute(enemy, "stuntime"))
	{
		SetHostile = false;
	}
	// TIH <--

	if(SetHostile) // if we are setting enemy hostile to us (and haven't yet)
	{
		if(!LAi_IsDead(enemy)) { LAi_group_Attack(attack, enemy); } // if not dead, alarm!
		else // otherwise...
		{
			// for now, just behave like normal. When I have a chance, I'll check near characters vs. see/hear radius.
			//int num = FindNearCharacters(enemy, float rad, float ax, float testAng, float nearDist, bool isVisibleTest, bool isSort)
			LAi_group_Attack(attack, enemy);
		}
	}
	// ccc special weapon assembly kit end

	if(LAi_IsDead(enemy) && CheckAttribute(enemy,"itemtrade"))
	//if you killed a merchant
	{
		logit(TranslateString("","The Merchantguild's watchmen are very vigilant..."));
		// TIH --> bug fix... this now works right Jul23'06
		int numGuards = 30 - LAi_numloginedcharacters;		// LDH - changed from 32 to TIH's original 30
		if ( numGuards > 0 ) Ambush("guards", numGuards, "enemy", "friend","");
		if ( sti(attack.index) == GetMainCharacterIndex() ) {
			ref PlayerChar = GetMainCharacter();
			ChangeCharacterReputation(PlayerChar, -100);// TIH MUST BE INT Nov17'06
			if ( CheckAttribute(enemy,"nation") ) {
				SetNationRelation2MainCharacter(sti(enemy.nation), RELATION_ENEMY);
				LooseLetterOfMarque(sti(enemy.nation));
				LeaveService(PlayerChar, sti(enemy.nation), false);
				SetRMRelation(PlayerChar, sti(enemy.nation), REL_WAR);
			}
			PlayerChar.nation = PIRATE;
			if(GetRMRelation(PlayerChar, PIRATE) < REL_AMNESTY) SetRMRelation(PlayerChar, PIRATE, REL_AMNESTY);
			UpdateRelations();
			RefreshBattleInterface();
		}
		// TIH <--
	}

	// unchanged code begins:

	//Исполнение типа
	string func = attack.chr_ai.type;
	if(func == "") return;
	func = "LAi_type_" + func + "_Attack";
	call func(attack, enemy, attackDmg, hitDmg);
	//Обновим цель сразу
	LAi_group_UpdateTargets(enemy);
	func = enemy.chr_ai.type;
	if(func == "") return;
	func = "LAi_type_" + func + "_Attacked";
	call func(enemy, attack);
	func = "LAi_type_" + enemy.chr_ai.type + "_CharacterUpdate";
	call func(enemy, 0.0001);	
	//LanguageCloseFile(tmpLangFileID);
}

void LAi_CharacterBlock()
{
	aref attack = GetEventData();
	aref enemy = GetEventData();
	float attackDmg = GetEventData();
	float hitDmg = GetEventData();

	// PB: Bladedamage mod -->
	int bladedamage_usage;
	int bladedamage_level = 0; // changed by MAXIMUS
	int total_chance;
	string BladeID;
	string BladeBaseID;
	string weaponID;
	aref weapon;
	bool DecreaseQuality;
	bool BladeBreak;
	bool Continue_Bladedamage;

	ref PChar = GetMainCharacter();

// Counters to gather information for balancing this mod -->
	if(sti(enemy.index) == GetMainCharacterIndex())
	// This counts how many times an enemy hit the PChar while the PChar was blocking
	{
		if(!CheckAttribute(PChar,"hitduringblock")) PChar.hitduringblock = 0;
		PChar.hitduringblock = sti(PChar.hitduringblock) + 1;
	}

	if(sti(attack.index) == GetMainCharacterIndex())
	// This counts how many times the PChar hit an enemy while the enemy was blocking
	{
		if(!CheckAttribute(PChar,"hitwhileblocking")) PChar.hitwhileblocking = 0;
		PChar.hitwhileblocking = sti(PChar.hitwhileblocking) + 1;
	}
// Counters to gather information for balancing this mod <--

	// Code for the bladedamage for the enemy:
	if(BLADEDAMAGE_ENABLED && ENABLE_WEAPONSMOD && GetCharacterEquipByGroup(enemy,BLADE_ITEM_TYPE) != "bladeX4" && !bDeckEnter)
	{
		if(!CheckAttribute(enemy,"bladedamagecounter"))
		{
			if(sti(enemy.index) == GetMainCharacterIndex())
		 	{enemy.bladedamagecounter = 0;}
			else
			{enemy.bladedamagecounter = rand(BLADEDAMAGE_RAND + BLADEDAMAGE_BASE) - 0.5 * BLADEDAMAGE_RAND;} // to decrease the chance of an enemy's blade breaking on first hit
		}
		enemy.bladedamagecounter = sti(enemy.bladedamagecounter) + 1;
		// Counts the amount of strokes you performed with your blade

			BladeID = GetCharacterEquipByGroup(enemy,BLADE_ITEM_TYPE);
		if (findSubStr(&BladeID, "+", 0) > -1 || findSubStr(&BladeID, "-", 0) > -1)
			BladeBaseID = strcut(&BladeID, 0, strlen(BladeID) - 3);
		else
			BladeBaseID = BladeID;

		weaponID = GetCharacterEquipByGroup(enemy,BLADE_ITEM_TYPE); // defines weaponname
		Items_FindItem(weaponID, &weapon);	// defines object for weaponattributes

		DecreaseQuality = false;
		BladeBreak = false;
		Continue_Bladedamage = true;

		// Bladedamage based on the usage of your blade
		// -------------------------------------------------------
		bladedamage_usage = enemy.bladedamagecounter;

		//LogIt("Bladedamage Usage = " + bladedamage_usage);

		// Bladedamage based on your level vs the blade's minlevel
		// -------------------------------------------------------
		if(CheckAttribute(weapon,"minlevel")) bladedamage_level = sti(weapon.minlevel) - sti(enemy.rank); // changed by MAXIMUS
		if(bladedamage_level < 0) bladedamage_level = 0;
		bladedamage_level = 0.01 * (BLADEDAMAGE_BASE + BLADEDAMAGE_RAND) * sti(bladedamage_level); // to keep this effect having impact, even if BASE or RAND are increased

		//LogIt("Bladedamage Level = " + bladedamage_level);

		total_chance = sti(bladedamage_usage) + sti(bladedamage_level);

		if(total_chance > rand(BLADEDAMAGE_RAND) + BLADEDAMAGE_BASE)
		{DecreaseQuality = true;}

		//LogIt("Total chance points = " + total_chance + " for character " + enemy.name + " " + enemy.lastname);

		// Broken blades break VERY quickly!!!
		if(GetItemQualityByID(BladeID) == 0 && rand(sti(bladedamage_usage)) > 10) // Add some randomness
		{DecreaseQuality = true;}

		if(DecreaseQuality)
		{
			RemoveCharacterEquip(enemy, BLADE_ITEM_TYPE);
			TakeItemFromCharacter(enemy, BladeID);

			enemy.bladedamagecounter = 0;

			switch(GetItemQualityByID(BladeID))
			{
				case 0:
					LAi_CharacterPlaySound(enemy, "OBJECTS\duel\sword_broken.wav");//MAXIMUS
					BladeBreak = true;
				break;

				case 1:
					if(enemy.chr_ai.group ==  LAI_GROUP_PLAYER)
					{LAi_CharacterPlaySound(enemy, "OBJECTS\duel\sword_broken.wav");}//MAXIMUS
					// To only play the sound for player-related characters

					// To give broken blades
					if(enemy.chr_ai.group ==  LAI_GROUP_PLAYER || BLADEDAMAGE_KEEPBROKENBLADE)
					{
						GiveItem2Character(enemy, BladeBaseID + "-2");
						EquipCharacterByItem(enemy, BladeBaseID + "-2");
					}
					BladeBreak = true;
				break;

				case 2:
					GiveItem2Character(enemy, BladeBaseID + "-1");
					EquipCharacterByItem(enemy, BladeBaseID + "-1");
				break;

				case 3:
					GiveItem2Character(enemy, BladeBaseID);
					EquipCharacterByItem(enemy, BladeBaseID);
				break;

				case 4:
					GiveItem2Character(enemy, BladeBaseID + "+1");
					EquipCharacterByItem(enemy, BladeBaseID + "+1");
				break;

				case 5:
					GiveItem2Character(enemy, BladeBaseID + "+2");
					EquipCharacterByItem(enemy, BladeBaseID + "+2");
				break;
			}

			if(BladeBreak)
			{
				if(sti(enemy.index) == GetMainCharacterIndex()) Continue_Bladedamage = false;
				if(CheckAttribute(enemy, "nodisarm")) Continue_Bladedamage = false;
				if(enemy.chr_ai.group == LAI_GROUP_PLAYER) Continue_Bladedamage = false;
				if(bAbordageStarted) Continue_Bladedamage = false;

				if(Continue_Bladedamage && rand(1) > 0)
				{
					LAi_SetCitizenTypeNoGroup(enemy);
				}
				else
				{
					if(CheckCharacterItem(enemy, FindCharacterItemByGroup(enemy, BLADE_ITEM_TYPE)) && GetItemQualityByID(FindCharacterItemByGroup(enemy, BLADE_ITEM_TYPE)) != 0 && BLADEDAMAGE_USEOTHERBLADE)
					// If character has another blade and the other blade is not broken and USEOTHERBLADE is on
					{
						// Then use the other blade
						EquipCharacterByItem(enemy, FindCharacterItemByGroup(enemy, BLADE_ITEM_TYPE));
					}
					else
					{
						// Else use the default blade
						if(!CheckCharacterItem(enemy, BLADEDAMAGE_DEFAULTBLADE))
						{GiveItem2Character(enemy, BLADEDAMAGE_DEFAULTBLADE);}
						// To prevent characters being given another defaultblade if they already have one
						EquipCharacterByItem(enemy, BLADEDAMAGE_DEFAULTBLADE);
					}
				}
			}
		}
	}

	// Code for the bladedamage for the attack:
	if(BLADEDAMAGE_ENABLED && ENABLE_WEAPONSMOD && GetCharacterEquipByGroup(attack,BLADE_ITEM_TYPE) != "bladeX4" && !bDeckEnter)
	{
		if(!CheckAttribute(attack,"bladedamagecounter"))
		{
			if(sti(attack.index) == GetMainCharacterIndex())
		 	{attack.bladedamagecounter = 0;}
			else
			{attack.bladedamagecounter = rand(BLADEDAMAGE_RAND + BLADEDAMAGE_BASE) - 0.5 * BLADEDAMAGE_RAND;} // to decrease the chance of an enemy's blade breaking on first hit
		}
		attack.bladedamagecounter = sti(attack.bladedamagecounter) + 1;
		// Counts the amount of strokes you performed with your blade

			BladeID = GetCharacterEquipByGroup(attack,BLADE_ITEM_TYPE);
		if (findSubStr(&BladeID, "+", 0) > -1 || findSubStr(&BladeID, "-", 0) > -1)
			BladeBaseID = strcut(&BladeID, 0, strlen(BladeID) - 3);
		else
			BladeBaseID = BladeID;

		weaponID = GetCharacterEquipByGroup(attack,BLADE_ITEM_TYPE); // defines weaponname
		Items_FindItem(weaponID, &weapon);	// defines object for weaponattributes

		DecreaseQuality = false;
		BladeBreak = false;
		Continue_Bladedamage = true;

		// Bladedamage based on the usage of your blade
		// -------------------------------------------------------
		bladedamage_usage = attack.bladedamagecounter;

		//LogIt("Bladedamage Usage = " + bladedamage_usage);

		// Bladedamage based on your level vs the blade's minlevel
		// -------------------------------------------------------
		if(CheckAttribute(weapon,"minlevel")) bladedamage_level = sti(weapon.minlevel) - sti(attack.rank); // changed by MAXIMUS
		if(bladedamage_level < 0) bladedamage_level = 0;
		bladedamage_level = 0.01 * (BLADEDAMAGE_BASE + BLADEDAMAGE_RAND) * sti(bladedamage_level); // to keep this effect having impact, even if BASE or RAND are increased

		//LogIt("Bladedamage Level = " + bladedamage_level);

		total_chance = sti(bladedamage_usage) + sti(bladedamage_level);

		if(total_chance > rand(BLADEDAMAGE_RAND) + BLADEDAMAGE_BASE)
		{DecreaseQuality = true;}

		//LogIt("Total chance points = " + total_chance + " for character " + attack.name + " " + attack.lastname);

		// Broken blades break VERY quickly!!!
		if(GetItemQualityByID(BladeID) == 0 && rand(sti(bladedamage_usage)) > 10) // Add some randomness
		{DecreaseQuality = true;}

		if(DecreaseQuality)
		{
			RemoveCharacterEquip(attack, BLADE_ITEM_TYPE);
			TakeItemFromCharacter(attack, BladeID);

			attack.bladedamagecounter = 0;

			switch(GetItemQualityByID(BladeID))
			{
				case 0:
					LAi_CharacterPlaySound(attack, "OBJECTS\duel\sword_broken.wav");//MAXIMUS
					BladeBreak = true;
				break;

				case 1:
					if(attack.chr_ai.group ==  LAI_GROUP_PLAYER)
					{LAi_CharacterPlaySound(attack, "OBJECTS\duel\sword_broken.wav");}//MAXIMUS
					// To only play the sound for player-related characters

					// To give broken blades
					if(attack.chr_ai.group ==  LAI_GROUP_PLAYER || BLADEDAMAGE_KEEPBROKENBLADE)
					{
						GiveItem2Character(attack, BladeBaseID + "-2");
						EquipCharacterByItem(attack, BladeBaseID + "-2");
					}
					BladeBreak = true;
				break;

				case 2:
					GiveItem2Character(attack, BladeBaseID + "-1");
					EquipCharacterByItem(attack, BladeBaseID + "-1");
				break;

				case 3:
					GiveItem2Character(attack, BladeBaseID);
					EquipCharacterByItem(attack, BladeBaseID);
				break;

				case 4:
					GiveItem2Character(attack, BladeBaseID + "+1");
					EquipCharacterByItem(attack, BladeBaseID + "+1");
				break;

				case 5:
					GiveItem2Character(attack, BladeBaseID + "+2");
					EquipCharacterByItem(attack, BladeBaseID + "+2");
				break;
			}

			if(BladeBreak)
			{
				if(sti(attack.index) == GetMainCharacterIndex()) Continue_Bladedamage = false;
				if(CheckAttribute(attack, "nodisarm")) Continue_Bladedamage = false;
				if(attack.chr_ai.group == LAI_GROUP_PLAYER) Continue_Bladedamage = false;
				if(bAbordageStarted) Continue_Bladedamage = false;

				if(Continue_Bladedamage && rand(1) > 0)
				{
					LAi_SetCitizenTypeNoGroup(attack);
				}
				else
				{
					if(CheckCharacterItem(attack, FindCharacterItemByGroup(attack, BLADE_ITEM_TYPE)) && GetItemQualityByID(FindCharacterItemByGroup(attack, BLADE_ITEM_TYPE)) != 0 && BLADEDAMAGE_USEOTHERBLADE)
					// If character has another blade and the other blade is not broken and USEOTHERBLADE is on
					{
						// Then use the other blade
						EquipCharacterByItem(attack, FindCharacterItemByGroup(attack, BLADE_ITEM_TYPE));
					}
					else
					{
						// Else use the default blade
						if(!CheckCharacterItem(attack, BLADEDAMAGE_DEFAULTBLADE))
						{GiveItem2Character(attack, BLADEDAMAGE_DEFAULTBLADE);}
						// To prevent characters being given another defaultblade if they already have one
						EquipCharacterByItem(attack, BLADEDAMAGE_DEFAULTBLADE);
					}
				}
			}
		}
	}
	// PB: Bladedamage mod <--

	//skillearning(attack, "Fencing", 1);	// ccc skillearning

	//Реакция груп на атаку
	LAi_group_Attack(attack, enemy);
	//Начисление повреждений
	LAi_ApplyCharacterBlockDamage(attack, enemy, attackDmg, hitDmg);
	//Исполнение типа
	string func = attack.chr_ai.type;
	if(func == "") return;
	func = "LAi_type_" + func + "_Block";
	call func(attack, enemy, attackDmg, hitDmg);
	//Обновим цель сразу
	LAi_group_UpdateTargets(enemy);
	func = enemy.chr_ai.type;
	if(func == "") return;
	func = "LAi_type_" + func + "_Attacked";
	call func(enemy, attack);
	func = "LAi_type_" + enemy.chr_ai.type + "_CharacterUpdate";
	call func(enemy, 0.0001);
}

void LAi_CharacterFire()
	// ccc jul 05 Most code moved into the new LAi_CharacterFireExecute()
	// This now only gets the EventData from the original targeting process and passes them on if we don't correct them
{
	aref attack = GetEventData();
	aref enemy = GetEventData();
	float kDist = GetEventData();	//0..1
	int isFindedEnemy = GetEventData();

	// ccc jul05 shooting past corpses and barricades
	if(isFindedEnemy)
	{
		bool bPass = CheckAttribute(enemy,"corpse")==true;
		(!bPass) bPass = enemy.name=="A ";
		if(bPass)// if original target is corpse or barricade
		{
			int nm = FindNearCharacters(enemy, 2.5, -1.0, -1.0, 0.001, false, true);
			for(int k = 0; k <= nm; k++)
				// checks for other chrs around original target and loops through them
			{
				//changed by MAXIMUS - [fix - CheckAttribute] -->
				int ix = -1;
				if(CheckAttribute(chrFindNearCharacters[k],"index")) ix = sti(chrFindNearCharacters[k].index);
				if(ix == -1) return;
				//changed by MAXIMUS - [fix - CheckAttribute] <--
				ref findC = GetCharacter(ix);
				if(!CheckAttribute(findC,"corpse") && findC.name != "A " && findC.name != attack.name)// if bystander is neither corpse, barricade or shooter himself...
				{
					LAi_CharacterFireExecute(attack, findC, kDist, isFindedEnemy);//... shooting is executed with bystander as new target
					break;
				}
			}
		}
		else
		{
			LAi_CharacterFireExecute(attack, enemy, kDist, isFindedEnemy);	// unchanged targetdata passed on
		}
	}
	else LAi_CharacterFireExecute(attack, enemy, kDist, isFindedEnemy);	// unchanged targetdata passed on
}


void LAi_CharacterFireExecute(aref attack, aref enemy, float kDist, int isFindedEnemy)
	// ccc jul 05 Former LAi_CharacterFire() function minus the EventData yielded by the original targeting process
	// Shooting can now also be executed from other functions with other data
{
	//int tmpLangFileID = LanguageOpenFile("interface_strings.txt");
	//ccc aref attack = GetEventData();
	//ccc aref enemy = GetEventData();
	//ccc float kDist = GetEventData();
	//ccc int isFindedEnemy = GetEventData();
	//Заряд персонажа
	if(!CheckAttribute(attack, "chr_ai.charge")) attack.chr_ai.charge = "0";
	float charge = stf(attack.chr_ai.charge) - 1.0;
	if(charge <= 0.0)
	{
		charge = 0.0;
		attack.chr_ai.chargeprc = "1";
	}
	attack.chr_ai.charge = charge;
	//Если промахнулись, то ничего не делаем
	if(isFindedEnemy == 0)
	{
		//здесь можно поднимать тревогу в случае близкого выстрела
		//LanguageCloseFile(tmpLangFileID);
		return;
	}

	// end of unchanged original code

	//skillearning(attack, "Fencing", 1);	// ccc skillearning

	// ccc special weapon assembly kit start
	// note: the function we are in here runs if a character (refered to as "attack") uses a gun AND has a targetcharacter ("enemy") in range

	string weaponID = GetCharacterEquipByGroup(attack,GUN_ITEM_TYPE);	// defines weaponname
	aref weapon;
	Items_FindItem(weaponID, &weapon);	// defines object for weaponattributes

	bool CheckHostile = true; // run LAi_Attacked etc. set-hostile code. NK 05-07-10
	bool SetHostile = true;
	string effects_to_play = "always"; // for selective playing of FX
	int i;

//================================================================================================ 
// JRH ammo mod --->

float u, v, w;          // coordinates for effects, JRH misf mod
GetCharacterPos(attack, &u, &v, &w);    // determines position of initial target 

int gp;
int pb;
int pg;
int mb;

if (ENABLE_AMMOMOD == 1 )
{
    if(IsMainCharacter(attack) || IsOfficer(attack) ) // JRH fix oct 06: officer should consume ammo
    {
 	if(CheckAttribute(weapon, "shottype") && weapon.shottype == "pg2")
	{	
		gp = GetCharacterItem(attack, "gunpowder");
 		pg = GetCharacterItem(attack, "pistolgrapes");
		if(gp >=2 && pg >= 2)
		{
			TakeNItems(attack,"gunpowder", -2);
		        TakeNItems(attack,"pistolgrapes", -2);	//shot! 

			gp = GetCharacterItem(attack, "gunpowder");
			pg = GetCharacterItem(attack, "pistolgrapes");
			if(gp >=2 && pg >= 2)
			{
				//ok
			}
			else
			{
				if(IsMainCharacter(attack)) // JRH fix oct 06: moved back from LAi_fightparams
				{
					if(gp == 1) Log_SetStringToLog(TranslateString("","There is not enough Gunpowder"));
					if(pg == 1) Log_SetStringToLog(TranslateString("","There are not enough Grapeshots"));
					if(gp == 0) Log_SetStringToLog(TranslateString("","There is no Gunpowder left"));
					if(pg == 0) Log_SetStringToLog(TranslateString("","There are no Grapeshots left"));
				}
		        }
		}
		else
		{		
			weapon.misfunction21 = 100;	//misf needed to avoid normal pistol sound
			if(IsMainCharacter(attack)) 
			{
				if(gp == 1) Log_SetStringToLog(TranslateString("","There is not enough Gunpowder"));
				if(pg == 1) Log_SetStringToLog(TranslateString("","There are not enough Grapeshots"));
				if(gp == 0) Log_SetStringToLog(TranslateString("","There is no Gunpowder left"));
				if(pg == 0) Log_SetStringToLog(TranslateString("","There are no Grapeshots left"));
			}
		}
	}
	else
	{
	   if(CheckAttribute(weapon, "shottype") && weapon.shottype == "mb")
	   {
		gp = GetCharacterItem(attack, "gunpowder");
		mb = GetCharacterItem(attack, "musketbullets");
		if(gp >=2 && mb >= 1)
		{
			TakeNItems(attack,"gunpowder", -2);
			TakeNItems(attack,"musketbullets", -1);
			
			gp = GetCharacterItem(attack, "gunpowder");
			mb = GetCharacterItem(attack, "musketbullets");
			if(gp >=2 && mb >= 1)
			{
				//ok
			}
			else
			{
				if(IsMainCharacter(attack)) 
				{
					if(gp == 1) Log_SetStringToLog(TranslateString("","There is not enough Gunpowder"));
					if(gp == 0) Log_SetStringToLog(TranslateString("","There is no Gunpowder left"));
					if(mb == 0) Log_SetStringToLog(TranslateString("","There are no Musketbullets left"));
				}
		        }
		}
		else
		{		
			weapon.misfunction21 = 100;	//misf needed to avoid normal pistol sound
			if(IsMainCharacter(attack)) 
			{
				if(gp == 1) Log_SetStringToLog(TranslateString("","There is not enough Gunpowder"));
				if(gp == 0) Log_SetStringToLog(TranslateString("","There is no Gunpowder left"));
				if(mb == 0) Log_SetStringToLog(TranslateString("","There are no Musketbullets left"));
			}
		}
	   }
	}
	else
	{
	   if(CheckAttribute(weapon, "shottype") && weapon.shottype == "pg")
	   {
		gp = GetCharacterItem(attack, "gunpowder");
		pg = GetCharacterItem(attack, "pistolgrapes");
		if(gp >= 1 && pg >= 1)
		{
			TakeNItems(attack,"gunpowder", -1);
		        TakeNItems(attack,"pistolgrapes", -1);	//shot! 

			gp = GetCharacterItem(attack, "gunpowder");
			pg = GetCharacterItem(attack, "pistolgrapes");
			if(gp >= 1 && pg >= 1)
			{
				//ok
			}
			else
			{
				if(IsMainCharacter(attack)) 
				{
					if(gp == 0) Log_SetStringToLog(TranslateString("","There is no Gunpowder left"));
					if(pg == 0) Log_SetStringToLog(TranslateString("","There are no Grapeshots left"));
				}
		        }
		}
		else
		{		
			weapon.misfunction21 = 100;	//misf needed to avoid normal pistol sound
			if(IsMainCharacter(attack)) 
			{
				if(gp == 0) Log_SetStringToLog(TranslateString("","There is no Gunpowder left"));
				if(pg == 0) Log_SetStringToLog(TranslateString("","There are no Grapeshots left"));
			}
		}
	   }
	}
	else
	{
	   if(CheckAttribute(weapon, "shottype") && weapon.shottype == "pb")						
	   {	
		gp = GetCharacterItem(attack, "gunpowder");
		pb = GetCharacterItem(attack, "pistolbullets");
		if(gp >= 1 && pb >= 1)
		{
			TakeNItems(attack,"gunpowder", -1);
		        TakeNItems(attack,"pistolbullets", -1);	//shot! 

			gp = GetCharacterItem(attack, "gunpowder");
			pb = GetCharacterItem(attack, "pistolbullets");
			if(gp >= 1 && pb >= 1)
			{
				//ok
			}
			else
			{
				if(IsMainCharacter(attack)) 
				{
					if(gp == 0) Log_SetStringToLog(TranslateString("","There is no Gunpowder left")); 
					if(pb == 0) Log_SetStringToLog(TranslateString("","There are no Pistolbullets left"));
				}
		        }
		}
		else
		{		
			weapon.misfunction21 = 100;	//misf needed to avoid normal pistol sound
			if(IsMainCharacter(attack)) 
			{
				if(gp == 0) Log_SetStringToLog(TranslateString("","There is no Gunpowder left")); 
				if(pb == 0) Log_SetStringToLog(TranslateString("","There are no Pistolbullets left"));
			}
		}
	   }
	}
   }
}

	//click-shot, normally this will NOT show up, just in case
	//gun will not reload if gp or shot = 0 see LAi_fightparams & Character_utilite
	//mtoon & musket with not ENOUGH ammo will give a click-shot, and that is only if inventory is opened
	if(CheckAttribute(weapon, "misfunction21") && rand(100)<sti(weapon.misfunction21) )	
	{		
		CreateParticleSystem("gunfire_red" , u, v+1.5, w, 0.0, 0.0, 0.0, sti(20) ); 
		CreateParticleSystem("stars_fire" , u, v+1.5, w, 0.0, 0.0, 0.0, sti(20) );
		LAi_CharacterPlaySound(attack, "OBJECTS\duel\GI_ValidClick.wav");//MAXIMUS

		weapon.misfunction21 = 0;

		//LanguageCloseFile(tmpLangFileID);
		return;
	}

// JRH ammo mod <---
//================================================================================================

	if(CheckAttribute(weapon, "misfire") && rand(100)<sti(weapon.misfire) )
		// if a weapon can misfire & chance for that, as defined in items\itemsinit.c
	{
		Log_SetStringToLog(TranslateString("","Weapon misfired & blew up !"));
		Explosion (attack, rand(20));			// New "explosion" function hurts weaponuser
  		if(GetCharacterItem(attack, weaponID) <= 1) RemoveCharacterEquip(attack, weapon.groupID);	// unequips thrown weapon
               	TakeItemFromCharacter(attack, weaponID );	// deletes exploded weapon from inventory
		LAi_CharacterPlaySound(attack, "OBJECTS\duel\sword_fallen.wav");//MAXIMUS
		//LanguageCloseFile(tmpLangFileID);
		return;
	}

	if( CheckAttribute(weapon, "sound" ) ) {
		LAi_CharacterPlaySound(attack, weapon.sound); //MAXIMUS: plays soundfile if defined in items\itemsinit.c
	} else {
		LAi_CharacterPlaySound(attack, "OBJECTS\duel\pistol_shot2.wav"); //MAXIMUS: otherwise normal pistolsound
	}

	int damagetoapply = 0;
	if( CheckAttribute(weapon, "multidmg" ) )	// next section runs only if chr near target shall be hurt
	{
		int num = FindNearCharacters(enemy, 2.5, -1.0, -1.0, 0.001, false, true);
			// checks if there are other people around the initial target. 2.5 is the checkradius

		for(int j = 0; j <= num; j++)	// the following loop runs for each near character
		{
			int idx = -1;// MAXIMUS [fix for CheckAttribute]
			if(CheckAttribute(chrFindNearCharacters[j],"index")) idx = sti(chrFindNearCharacters[j].index);	// defines near character as idx
			if(idx == -1) continue;
			ref findCh;
			findCh = GetCharacter(idx);	// necessary? to get object for next line

			LAi_ApplyCharacterFireDamage(attack, &Characters[idx], kDist );
				// applies normal gunfire damage to near characters EXCEPT user & officers!

			if( CheckAttribute(weapon, "selfdmg" ) && findCh.id==attack.id)	// if user shall be hurt if too close
			{
				LAi_ApplyCharacterDamage( &Characters[idx], sti(weapon.dmg_min) );
					// applies minimumdamage of weapon to every near character, i.e. user also

				if(stf(attack.chr_ai.hp) < 1.0) attack.chr_ai.hp = 1 + makeint(rand(10));
					// jun05 so you dont kill yourself

				// LAi_CheckKillCharacter( &Characters[idx] );
					// checks if this has killed the character
			}

			if(CheckAttribute(weapon, "stun")&&!LAi_IsFightMode(&Characters[idx])&&findCh.chr_ai.group!=LAI_GROUP_PLAYER)
			 	// if weapon stuns victim; works only if victim is not prepared for fight and no playerassociate
			{
				int tduration = 100;
				if(CheckAttribute(weapon, "stun.duration")) tduration = sti(weapon.stun.duration);
				//MAXIMUS: during fight, character can join to his crew. Sometimes it may be helpful -->
				bool bPlayer = false;
				ref mainCh = GetMainCharacter();
				if(CheckAttribute(&findCh, "chr_ai.group") && findCh.chr_ai.group==LAI_GROUP_PLAYER)
				{
					if(sti(mainCh.skill.Leadership)>sti(findCh.skill.Leadership)) bPlayer = true;
				}
				else
				{
					if(sti(mainCh.skill.Leadership)<sti(findCh.skill.Leadership)) bPlayer = true;
				}
				LAi_Stunned_StunCharacter(&findCh, tduration, true, STUN_STARS, bPlayer);
				//MAXIMUS: during fight, character can join to his crew. Sometimes it may be helpful <--
			}

			if( CheckAttribute(weapon, "poison" ) ) 	// if weapon poisonous
			{
				findCh.chr_ai.poison =  300 ;	// poisons EVERY near character
				// NK play sound too 05-07-10
				if(findCh.sex == "woman") {LAi_CharacterPlaySound(findCh, "OBJECTS\Voices\dead\female\dead_wom2.wav");}	// groan
				else{LAi_CharacterPlaySound(findCh, "OBJECTS\Voices\dead\male\dead4.wav");}
			}

		}	// end of loop
		// NK do this below for GFX support
		damagetoapply = sti(weapon.dmg_min);
		/*LAi_ApplyCharacterDamage( enemy, sti(weapon.dmg_min) );
			// to ensure that initial target suffers minimumdamage, wasn't always the case
		LAi_CheckKillCharacter( enemy );
			// checks if this has killed the character*/

	}	// end of multidamage section

	if( CheckAttribute(weapon, "steal" ) && !LAi_IsFightMode(enemy) )
	 	// if weapon is a thief's tool; works only if victim is not prepared for fight
	{

		//skillearning(attack, "sneak", 1);	// ccc skillearning
		CheckHostile = false;
		LAi_AttemptSteal(attack, enemy, weapon); // NK do this in one function. 05-07-10
	}

	if( CheckAttribute(weapon, "stun" ) && !LAi_IsFightMode(enemy) && enemy.chr_ai.group != LAI_GROUP_PLAYER )
	 	// if weapon stuns victim; works only if victim is not prepared for fight and no playerassociate
	{

		//skillearning(attack, "sneak", 1);	// ccc skillearning
		if(frnd() < stf(weapon.stun)*GetAttackLuckMod(&attack))	// jun 05 stun doesnt work always anymore
		{
			int duration = 100;
			if(CheckAttribute(weapon, "stun.duration")) duration = sti(weapon.stun.duration);
				//MAXIMUS: during fight, character can join to his crew. Sometimes it may be helpful -->
				bPlayer = false;
				if(CheckAttribute(enemy, "chr_ai.group") && enemy.chr_ai.group==LAI_GROUP_PLAYER)
				{
					if(sti(mainCh.skill.Leadership)>sti(enemy.skill.Leadership)) bPlayer = true;
				}
				else
				{
					if(sti(mainCh.skill.Leadership)<sti(enemy.skill.Leadership)) bPlayer = true;
				}
				LAi_Stunned_StunCharacter(enemy, duration, true, STUN_STARS, bPlayer);
				//MAXIMUS: during fight, character can join to his crew. Sometimes it may be helpful <--
			//effects_to_play += " stun";
		}
	}

	if( CheckAttribute(weapon, "poison" ) ) 	// if weapon poisonous
	{
		enemy.chr_ai.poison =  300 ;		// initial targetchr poisoned
		if(enemy.sex == "man") {LAi_CharacterPlaySound(enemy, "OBJECTS\Voices\dead\male\dead4.wav");}	// groan
		else{LAi_CharacterPlaySound(enemy, "OBJECTS\Voices\dead\female\dead_wom2.wav");}
		if(!CheckAttribute(weapon, "selfdmg" ) )	// if user shall NOT ! be hurt if too close
		{ attack.chr_ai.poison = 0 ;}			// "UNpoisons" the user in this case
			// this may in rare cases heal the user from another poisoning, but I doubt that's a problem
			effects_to_play += " poison";
	}

	if(CheckHostile)
	{
		if(CheckAttribute(weapon, "stealth"))	// silent weapons don't alarm the victim, for others next line runs
		{
	          	if(frnd() < stf(weapon.stealth)*GetAttackLuckMod(&attack)) { SetHostile = false; }
			//MAXIMUS -->
			else
			{
				if(enemy.sex=="woman") LAi_CharacterPlaySound(enemy, "VOICE\" + LanguageGetLanguage() + "\Eng_f_c_001.wav");//woman voice - "Help, help me, please. I adjure..."
				else LAi_CharacterPlaySound(enemy, "VOICE\" + LanguageGetLanguage() + "\Fre_m_c_038.wav");//man voice - "Stay here! Who are you?"
			}
			PlaySound("AMBIENT\TOWN\Alarm.wav");
			//MAXIMUS <--
//			else { PlaySound("VOICE\" + LanguageGetLanguage() + "\Eng_m_a_067.wav"); } // sound alarm
		}
	}
	else SetHostile = false;

	if( CheckAttribute(weapon, "throw" ) && charge <= 1.0 )	// for weapons that you throw, so only onetime use
	{
			if(GetCharacterItem(attack, weaponID) <= 1) RemoveCharacterEquip(attack, weapon.groupID);	// unequips thrown weapon
			TakeItemFromCharacter(attack, weaponID );		// deletes thrown weapon from inventory
	}

	//Реакция груп на атаку
	// ccc LAi_group_Attack(attack, enemy);	// line moved into "stealth" section

	// ccc particle effects, runs only if effect defined  in inititems.c
	LAi_PlayHitGFX(attack, enemy, weapon, effects_to_play); // NK do in centralized function 05-07-10
	

	//Начисление повреждений
	//jul 05 tools don't do damage. NK multidmg to target chr still is done, but is done direct, not with firedamage
	if( !CheckAttribute(weapon, "multidmg" )&&!CheckAttribute(weapon, "tool") ) { LAi_ApplyCharacterFireDamage(attack, enemy, kDist); }
	else // NK except we need to apply multidmg to the target chr _after_ the GFX or the GFX won't have a character to get position from.
	{
		if(damagetoapply > 0) // i.e. is multidmg after all
		{
			LAi_ApplyCharacterDamage( enemy, damagetoapply );
			// to ensure that initial target suffers minimumdamage, wasn't always the case

			// ccc sep05, keeps player & officers barely alive
			if(stf(enemy.chr_ai.hp)<1.0 && enemy.chr_ai.group==LAI_GROUP_PLAYER) enemy.chr_ai.hp = MIN_GUNATTACK_HP;

			LAi_CheckKillCharacter( enemy );
			// checks if this has killed the character
		}
	}

	if(SetHostile) // if we are setting enemy hostile to us (and haven't yet)
	{
		if(!LAi_IsDead(enemy)) { LAi_group_Attack(attack, enemy); } // if not dead, alarm!
		else // otherwise...
		{
			// for now, just behave like normal. When I have a chance, I'll check near characters vs. see/hear radius.
			//int num = FindNearCharacters(enemy, float rad, float ax, float testAng, float nearDist, bool isVisibleTest, bool isSort)
			LAi_group_Attack(attack, enemy);
		}
	}
	// ccc special weapon assembly kit end

	if(LAi_IsDead(enemy) && CheckAttribute(enemy,"itemtrade"))
	//if you killed a merchant
	{
		Logit(TranslateString("","The Merchantguild's watchmen are very vigilant..."));
		Ambush("guards", 4, "enemy", "friend",""); // 4 cops attack you
	}
	//LanguageCloseFile(tmpLangFileID);

	// unchanged code begins:

	//Исполнение типа
	string func = attack.chr_ai.type;
	if(func == "") return;
	func = "LAi_type_" + func + "_Fire";
	call func(attack, enemy, kDist, isFindedEnemy != 0);
	//Обновим цель сразу
	LAi_group_UpdateTargets(enemy);
	func = enemy.chr_ai.type;
	if(func == "") return;
	func = "LAi_type_" + func + "_Attacked";
	call func(enemy, attack);
	func = "LAi_type_" + enemy.chr_ai.type + "_CharacterUpdate";
	call func(enemy, 0.0001);
}


bool LAi_tmp_return_bool;
bool LAi_CharacterIsFire()
{
	aref chr = GetEventData();
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	//Исполнение
	func = "LAi_tmpl_" + func + "_IsFire";
	LAi_tmp_return_bool = call func(chr);
	return LAi_tmp_return_bool;
}

bool LAi_CharacterIsFight()
{
	aref chr = GetEventData();
	string func = chr.chr_ai.tmpl;
	if(func == "") return true; // KK
	//Исполнение
	func = "LAi_tmpl_" + func + "_IsFight";
	LAi_tmp_return_bool = call func(chr);
	return LAi_tmp_return_bool;
}

//==========================================================================================
//Escape events
//==========================================================================================

void LAi_CharacterEscapeSlide()
{
	//Параметры
	string endTask = GetEventData();
	aref chr = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_CharacterEscapeSlide") == false) return;
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	//Исполнение
	func = "LAi_tmpl_" + func + "_EscapeSlide";
	call func(chr);
}

//==========================================================================================
//Collision events
//==========================================================================================

void LAi_CharacterColThreshold()
{
	//Параметры
	string endTask = GetEventData();
	aref chr = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_CharacterColThreshold") == false) return;
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	//Исполнение
	func = "LAi_tmpl_" + func + "_ColThreshold";
	call func(chr);
}

//==========================================================================================
//Animation events
//==========================================================================================

void LAi_Character_EndAction()
{
	//Параметры
	aref chr = GetEventData();
	if(LAi_CheckCharacter(chr, "LAi_Character_EndAction") == false) return;
	string func = chr.chr_ai.tmpl;
	if(func == "") return;
	//Исполнение
	func = "LAi_tmpl_" + func + "_EndAction";
	call func(chr);
}


//==========================================================================================
//Dead event
//==========================================================================================

void LAi_Character_Dead_Process(aref chr)
{
	// PB: Blade Damage mod
	if(!IsMainCharacter(chr) && FindCharacterItemByGroup(chr, "bladeX4")) TakeItemFromCharacter(chr, "bladeX4");

// ccc building kit aug05 ; deletes "killed" buildings from location
	if(CheckAttribute(chr,"chr_ai.type") && chr.chr_ai.type == "Building") // TIH
	{
		ref lcn = &Locations[FindLocation(chr.location)];	// determines where you are
		Building_delete(lcn, chr.lastname);

		// demolition effects
		float x, y, z;			// coordinates for effects...
		//get deathsite coords(the current coords of logoffed chr are different!)
		x=stf(chr.deathx);
		y=stf(chr.deathy);
		z=stf(chr.deathz);

		CreateParticleSystemX("blast_inv", x, y, z, x, y, z,0);	// effects at that pos
		CreateParticleSystemX("cancloud", x, y, z, x, y, z,20);
		CreateParticleSystemX("cancloud", x+5, y, z, x, y, z,20);
		CreateParticleSystemX("cancloud", x-5, y, z, x, y, z,20);
		CreateParticleSystemX("cancloud", x, y, z+5, x, y, z,20);
		CreateParticleSystemX("cancloud", x, y, z-5, x, y, z,20);

		PlaySound("OBJECTS\shipcharge\mast_fall3.wav");
		PlaySound("OBJECTS\shipcharge\hit_torock.wav");
	}
// ccc building kit end

// added by MAXIMUS [fix for Player dialog closed after anyone is dead] -->
	int tmpi = SendMessage(GetMainCharacter(),"ls",MSG_CHARACTER_EX_MSG,"FindDialogCharacter");
	if(CheckAttribute(chr,"index") && tmpi==sti(chr.index)) DialogExit();
// added by MAXIMUS [fix for Player dialog closed after anyone is dead] <--

	DeleteAttribute(chr, "donotshowXP"); // NK so XP is shown next time.
	// move logoff into if(), CORPSEMODE dependent.
	if(!IsMainCharacter(chr)) // MAXIMUS
	{
		//SCREWFACE : if a permanent crewmember is killed, his corpse disapear immediatly (don't remove that unless that will cause a bug) 
		if(CORPSEMODE == 3 && !LAi_DoCORPEMODE(chr)) // NK persist original corpse 05-07-19 //GO Add Test for Quest that doesn't work with CORPSEMODE=3
		{
			LAi_GenerateFantomFromMe(chr);
			SetCharacterTask_Stay(chr);
			//PopCharacterTask(GetCharacter(sti(chr.index)));
			LAi_Stunned_MakeCorpse(chr);
			LAi_SetImmortal(chr, true);
		}
		else
		{
			if(CORPSEMODE!=2 && CORPSEMODE!=4 && !CheckAttribute(chr,"CORPSEMODE"))
			{//MAXIMUS: so, Maltese-corpse will always be lootable
				if(CheckAttribute(chr,"dialog.filename"))
				{
					if(chr.dialog.filename=="monk_dialog.c" || chr.dialog.filename=="Maltese_corpse_dialog.c") chr.CORPSEMODE = 4;
				}
			}
			int bCorpseMode = CORPSEMODE;
			if(CheckAttribute(chr,"CORPSEMODE") && sti(chr.CORPSEMODE)==4) bCorpseMode = 4;
			if(bCorpseMode<4 || CheckAttribute(chr,"makelocator")) { LAi_CharacterLogoff(chr); } //MAXIMUS: makes box from corpse with hand-icon in the top-left corner
			if(bCorpseMode==0) PostEvent("LAi_event_Character_Dead", 100, "l", sti(chr.index)); // MAXIMUS: corpse disappear
			else
			{
				PostEvent("LAi_event_Character_Dead", 500, "l", sti(chr.index)); // ccc jul05 corpse, time chg from 10000. NK change to 500. Make part of corpsemode != 3. // KK
			}
		}
		// NK do this in DeadEvent below: 05-07-13 - LAi_GenerateFantomFromMe(chr);
		LAi_group_CheckGroupQuest(chr);
	}
	else
	{
		LAi_CharacterLogoff(chr);
		if(!LAi_IsBoardingProcess()) PostEvent("LAi_event_GameOver", 5000, "s", "land");
		else PostEvent("LAi_event_GameOver", 5000, "s", "boarding");
	}
}

#event_handler("LAi_event_Character_Dead", "LAi_Character_Dead_Event");
void LAi_Character_Dead_Event() // should never be run when CORPSEMODE == 3. NK 05-07-19
{
	int chridx = GetEventData(); // KK
	if (chridx < 0) return; // KK

	int tmpNameFileID = LanguageOpenFile("characters_names.txt");

	ref chr = GetCharacter(chridx); // KK

// changed by MAXIMUS 12.10.2006 -->
	bool bLoc = CheckAttribute(chr,"location")==true;
	ref lcn;
	if(bLoc) lcn = &Locations[FindLocation(chr.location)];	// determines where you are
// changed by MAXIMUS 12.10.2006 <--

	// TIH --> changed behavior Sep2'06
	bool DissolveBody = true;
	int bCorpseMode = CORPSEMODE;
	if(CheckAttribute(chr,"CORPSEMODE") && sti(chr.CORPSEMODE)==4) bCorpseMode = 4;

	if(bCorpseMode==1 || bCorpseMode==4) DissolveBody = false;//MAXIMUS
	if(LAi_IsBoardingProcess()==true && AUTOLOOT_BOARDINGS) DissolveBody = false;//MAXIMUS: excludes player's decks
// changed by MAXIMUS 12.10.2006 -->
	if(bLoc)
	{
		if(CheckAttribute(lcn, "abbey") == true && sti(lcn.abbey) == true) DissolveBody = true; // TIH for maltese knight abbey // KK
	}
// changed by MAXIMUS 12.10.2006 <--
	
	if(DissolveBody)
	{
		// this removes the current body
		// we don't do this when we are leaving dead bodies around, like for AUTOLOOT_BOARDINGS, or CORPSEMODE 1
		// if CORPSEMODE is 2, a new body is created below as a lootable corpse, so it appears to 'stay'
		if(bCorpseMode<4 || CheckAttribute(chr,"makelocator")) CharacterExitFromLocation(chr);//MAXIMUS
	}
	else
	{
		// we wont be clearing the old body nor making a new body, so we need to keep this one around
		// however we set it as a neutral entity so that the warning indicator will go out when all enemies are dead
		if(bCorpseMode<4)
		{
			LAi_group_MoveCharacter(chr, LAI_GROUP_CORPSES);
		}
		if(bCorpseMode<4 || CheckAttribute(chr,"makelocator")) { LAi_CharacterLogoff(chr); }//MAXIMUS
	}
	// TIH <--

	// original chr body disappears, put 'if(chr.name != "The late")' in front to keep corpses lying around after looting
	// ccc jul05 corpse for looting

// KK -->
	/*if (!CheckAttribute(chr, "name"))		// LDH added to prevent errors on dead captains
	{
		Trace("Warning: Invalid character in LAi_CharacterDeadEvent(), ignored");
		LanguageCloseFile(tmpLangFileID);
		LanguageCloseFile(tmpNameFileID);
		return;
	}*/
// <-- KK
	bool makeCorpse = bCorpseMode==4 && CheckAttribute(chr,"corpse")==true;
	if(!makeCorpse) makeCorpse = CheckAttribute(chr,"corpse")==false;
	if(makeCorpse && bCorpseMode!=3) // do not generate a corpse from a corpse
	{
		bool GenerateCorpse = true;
		if(CheckAttribute(chr,"sex") && chr.sex == "monkey") GenerateCorpse = false; // No corpses for monkeys
		if(chr.name==LanguageConvertString(tmpNameFileID,"Crewmember") && bCorpseMode<4) GenerateCorpse = false; // No corpses for permanent crewmembers (Screwface) //MAXIMUS
		if(CheckAttribute(chr,"chr_ai.type") && chr.chr_ai.type == "Building") GenerateCorpse = false; // No corpses for buildings (ccc building kit Jan06)
		if(LAi_IsBoardingProcess()==true && AUTOLOOT_BOARDINGS && bCorpseMode<4) GenerateCorpse = false; // PB: No corpses during boardings - Instead automatic looting (at LDH's request) //MAXIMUS: excludes player's decks
		if(bCorpseMode!=2 && bCorpseMode<4) GenerateCorpse = false; // TIH only corpsemode 2 makes a lootable corpse Sep2'06
		// LDH 27Sep06 added CheckAttribute
// changed by MAXIMUS 12.10.2006 -->
		if(bLoc)
		{
			if(CheckAttribute(lcn, "abbey") == true && sti(lcn.abbey) == true) GenerateCorpse = true; // TIH for maltese knight abbey // KK
		}
// changed by MAXIMUS 12.10.2006 <--
		if(bCorpseMode==4 && CheckAttribute(chr,"makelocator")) { DeleteAttribute(chr,"makelocator"); CharacterExitFromLocation(chr); GenerateCorpse = false; }//MAXIMUS: for captains cabin

		if(GenerateCorpse)
		{
			if(bCorpseMode!=4) { LAi_GenerateFantomFromMe(chr); }// NK do this here 05-07-13 //MAXIMUS
				// ccc Nov 05 Greater Oxbay create ghosts in Maltese Abbey
				ref corpse;
				int x, y, z;
				aref ito, ifrom;
				// TIH --> we make sure that no already looted items are transfered Sep2'06
				bool bPurgeLoot = false;
				string tempitm;

				if(CheckAttribute(chr,"model") && chr.model == "Animists3")
				{
					if(bCorpseMode==4) { LAi_GenerateFantomFromMe(chr); } //MAXIMUS
					x = stf(chr.deathx);
					y = stf(chr.deathy);
					z = stf(chr.deathz);

					CreateParticleSystemX("fire_medium", x, y+2, z, x, y+2, z,2);	// effects at that pos
					If(USE_PARTICLES>0) CreateParticleSystemX("seafoam", x, y+1.5, z, x, y+2, z,2);	// ccc Dec05 tweak added
					If(USE_PARTICLES>1) CreateParticleSystemX("artifact", x, y+1.5, z, x, y+2, z,2);	// ccc Dec05 tweak added
	
					LAi_CharacterPlaySound(chr, "OBJECTS\VOICES\DEAD\skeleton\skeleton_dead01.wav");
					PlayStereoSound("AMBIENT\INCA TEMPLE\teleporter.wav");
					If(USE_PARTICLES>0) PlayStereoSound("NATURE\wind_tornado.wav");	// ccc Dec05 tweak added
					logit(TranslateString("","The weird monk reveals his true nature.."));
	
					corpse = LAi_CreateFantomCharacterEx(false, 3, true, false, 0.0, "skel2", "man", "", "");
					GiveItem2Character(corpse, "blade8");
					EquipCharacterByItem(corpse, "blade8");
					LAi_SetWarriorType(corpse);
					LAi_group_MoveCharacter(corpse, LAI_GROUP_MONSTERS);
					LAi_group_SetRelation(LAI_GROUP_PLAYER, LAI_GROUP_MONSTERS, LAI_GROUP_ENEMY);
					corpse.name = LanguageConvertString(tmpNameFileID,"Maltese");
					corpse.lastname = LanguageConvertString(tmpNameFileID,"Knight");
					corpse.dialog.filename = "Maltese_corpse_dialog.c";
					corpse.humantype = "MalteseKnight";
					LAi_SetImmortal(corpse, false);
					if(bCorpseMode==4)
					{
						if(CheckAttribute(chr, "items"))
						{
							makearef(ifrom, chr.items);
							corpse.items = "";
							makearef(ito, corpse.items);
							CopyAttributes(&ito, &ifrom);
						}
						LAi_CharacterLogoff(chr);
						CharacterExitFromLocation(chr);

						TeleportCharacterToPosAY(corpse, stf(chr.deathx), stf(chr.deathy), stf(chr.deathz), stf(chr.deathay));
					}
				} 	
				else
				{
					// original corpsecreation
					if(bCorpseMode!=4)
					{
						corpse = LAi_CreateFantomCharacterEx(false, 0, false, false, 0.0, chr.model, chr.sex, "", "");
						corpse.corpse = true;
						LAi_SetImmortal(corpse, true);
						//corpse.lastname = GetMyFullName(chr);
						//corpse.name = XI_ConvertString("The late");
						LAi_SetStunnedTypeNoGroup(corpse);
						LAi_group_MoveCharacter(corpse, LAI_GROUP_CORPSES);
						corpse.dialog.filename = "Corpse_dialog.c";
						if(CheckAttribute(corpse,"humantype") && corpse.humantype=="MalteseKnight") corpse.dialog.filename = "Maltese_corpse_dialog.c";
						DeleteAttribute(corpse, "items");
						if(AUTOLOOT_ON) bPurgeLoot = true;
						if(bAbordageStarted && AUTOLOOT_BOARDINGS) bPurgeLoot = true;
						if(CheckAttribute(chr, "items"))
						{
							if(bPurgeLoot)
							{
								tempitm = "";
								tempitm = GetCharacterEquipByGroup(chr,BLADE_ITEM_TYPE);
								if (tempitm != "") TakeItemFromCharacter(chr, tempitm);
								tempitm = GetCharacterEquipByGroup(chr,GUN_ITEM_TYPE);
								if (tempitm != "") TakeItemFromCharacter(chr, tempitm);
							}
							makearef(ifrom, chr.items);
							corpse.items = "";
							makearef(ito, corpse.items);
							CopyAttributes(&ito, &ifrom);
						}
						TeleportCharacterToPosAY(corpse, stf(chr.deathx), stf(chr.deathy), stf(chr.deathz), stf(chr.deathay));
						if(CORPSE_AUTODISAPPEAR>0 && bCorpseMode!=1)//MAXIMUS: if(CORPSEMODE==1) - corpse must stay in location till player will leave this place
						{
							if(bCaptureShip==true) PostEvent("LAi_RemoveCorpse", 100, "i", corpse);
							else PostEvent("LAi_RemoveCorpse", CORPSE_AUTODISAPPEAR, "i", corpse);
						}
					}// NK added Friend, turned off genrank. 05-07-10//MAXIMUS
					else
					{
						corpse = CharacterFromId(chr.id);
						LAi_group_MoveCharacter(corpse, LAI_GROUP_CORPSES);
						//MAXIMUS: makes box from corpse with hand-icon in the top-left corner -->
						int locNum = 1;
						ref corpseLoc = &Locations[FindLocation(corpse.location)];
						if(CheckAttribute(corpseLoc,"locators.box"))
						{
							aref rootLocs; makearef(rootLocs, corpseLoc.locators.box);
							locNum = GetAttributesNum(rootLocs)+1;
						}
						string newLocName = "corpse_box"+locNum;
						WriteLocatorGlobal(chr.location, "box", newLocName, sti(chr.index), stf(chr.deathx), stf(chr.deathy), stf(chr.deathz), true);
						//MAXIMUS: makes box from corpse with hand-icon in the top-left corner <--
					}// KK -->
					tempitm = GetCharacterEquipByGroup(corpse, BLADE_ITEM_TYPE);
					if (tempitm != "") corpse.old.blade = tempitm;
					tempitm = GetCharacterEquipByGroup(corpse, GUN_ITEM_TYPE);
					if (tempitm != "") corpse.old.gun = tempitm;// <-- KK
				}
				if(CheckAttribute(chr,"CORPSEMODE")) corpse.CORPSEMODE = chr.CORPSEMODE;
				if(bPurgeLoot) chr.money = 0;// TIH if already looted, make sure there is no money Sep2'06
				if(!CheckAttribute(chr,"money")) { chr.money = 0; }//MAXIMUS: for missed attribute
				corpse.money = chr.money;
				LAi_group_CheckGroupQuest(chr);
		}
	}
	LanguageCloseFile(tmpNameFileID);
// ccc jul05 corpse end
}

// added by MAXIMUS [corpses are automatic disappear from location] -->
#event_handler("LAi_RemoveCorpse", "LAi_CleanCorpses");
void LAi_CleanCorpses()
{
	aref corpse = GetEventData();
// LDH -->  Keeps corpses from disappearing while we're in a dialog which breaks the dialog and can kill a quest.
	if(CheckAttribute(GetMainCharacter(), "IsInDialog"))
	{
		if (CORPSE_AUTODISAPPEAR > 0)	// Defensive programming, in case logic is changed elsewhere
			PostEvent("LAi_RemoveCorpse", CORPSE_AUTODISAPPEAR, "i", corpse);
		else
			PostEvent("LAi_RemoveCorpse", 500, "i", corpse);
		return;
	}
// LDH <--
	if(CheckAttribute(corpse,"chr_ai.immortal"))) Lai_KillCharacter(corpse);
}
// added by MAXIMUS [corpses are automatic disappear from location] <--

#event_handler("LAi_event_GameOver", "LAi_GameOver");
void LAi_GameOver()
{
	string str = GetEventData();
	GameOver(str);
}



//==========================================================================================
//Internal events
//==========================================================================================

//------------------------------------------------------------------------------------------
//Сообщение об окончании работы темплейта
//------------------------------------------------------------------------------------------

void LAi_Character_TemplateComplite(aref chr, string tmplName)
{

	int index = sti(chr.index);
	PostEvent("LAi_event_Character_TemplateComplite", 1, "ls", index, tmplName);

}

#event_handler("LAi_event_Character_TemplateComplite", "LAi_Character_TemplateComplite_Event");
void LAi_Character_TemplateComplite_Event()
{
	int index = GetEventData();
	string tmpl = GetEventData();
	ref chr = &Characters[index];
	if (!CheckAttribute(chr, "chr_ai.type")) return;		// LDH - fix for missing chr 20AUG06 [MAXIMUS fix - was (chr, "chr_ai")]
	string func = chr.chr_ai.type;
	if(func != "")
	{
		func = "LAi_type_" + func + "_TemplateComplite";
		call func(chr, tmpl);
	}
}

//------------------------------------------------------------------------------------------
//Запрос локатора
//------------------------------------------------------------------------------------------

void LAi_Character_FreeLocator(aref chr, string group, string locator)
{
	int index = sti(chr.index);
	PostEvent("LAi_event_Character_FreePos", 1, "lss", index, group, locator);
}

#event_handler("LAi_event_Character_FreePos", "LAi_Character_FreePos_Event");
void LAi_Character_FreePos_Event()
{
	int index = GetEventData();
	string group = GetEventData();
	string locator = GetEventData();
	for(int i = 0; i < LAi_numloginedcharacters; i++)
	{
		int idx = LAi_loginedcharacters[i];
		if(idx >= 0)
		{
			if(idx != index)
			{
				float dist;
				if(GetCharacterDistByLoc(&Characters[idx], group, locator, dist))
				{
					if(dist < 0.8)
					{
						string func = Characters[idx].chr_ai.tmpl;
						if(func == "") return;
						//Исполнение
						func = "LAi_tmpl_" + func + "_FreePos";
						call func(&Characters[idx], &Characters[index]);
					}
				}
			}
		}
	}
}


//------------------------------------------------------------------------------------------
//Запрос на диалог
//------------------------------------------------------------------------------------------

void LAi_Character_NeedDialog(aref chr, aref by)
{
	if(IsEntity(by))
	{
		string func = chr.chr_ai.type;
		if(func != "")
		{
			func = "LAi_type_" + func + "_NeedDialog";
			call func(by, chr);
		}
	}
}

//Запрос на диалог, если возвратить true то в этот момент можно начать диалог
bool LAi_Character_CanDialog(aref chr, aref by)
{
	if(IsEntity(by) && !CheckAttribute(by,"condition") && !CheckAttribute(chr,"condition"))//MAXIMUS: for skipping dialog with character during ChangeCharacterAddress
	{
		bool isDisable = false;
		if(CheckAttribute(chr, "chr_ai.disableDlg"))
		{
			if(sti(chr.chr_ai.disableDlg) != 0) isDisable = true;
		}
		if(isDisable == false)
		{
			string func = by.chr_ai.type;
			if(func != "")
			{
				func = "LAi_type_" + func + "_CanDialog";
				return call func(by, chr);
			}
		}
	}
	return false;
}

//Начать диалог
void LAi_Character_StartDialog(aref chr, aref by)
{
	if(IsEntity(by))
	{
		string func = by.chr_ai.type;
		if(func != "")
		{
			func = "LAi_type_" + func + "_StartDialog";
			call func(by, chr);
		}
	}
}

//Закончить диалог
void LAi_Character_EndDialog(aref chr, aref by)
{
	if(IsEntity(by))
	{
		string func = by.chr_ai.type;
		if(func != "")
		{
			func = "LAi_type_" + func + "_EndDialog";
			call func(by, chr);
		}
	}
}
bool LAi_DoCORPEMODE(aref chr)
{
		if (CheckAttribute(chr, "corpse")) return true;
		if (bAbordageStarted) return true;
		if (chr.name==XI_ConvertString("Crewmember")) return true;
		if (chr.name=="Pirate") return true;
		if (chr.name+chr.lastname=="Bernardo"+"Gamboa") return true;
		if (chr.name+chr.lastname=="Nigel"+"Blythe") return true;
		if (chr.name+chr.lastname=="Artois"+"Voysey") return true;
		if (chr.chr_ai.group=="FBQ_SPAIN_SOLDIERS") return true;
		if (chr.name=="French Soldier") return true;
		if (chr.chr_ai.group=="ENGLAND_TAVERN_SOLDIERS") return true;
		if (chr.chr_ai.group=="SMUGGLERS_SOLDIERS") return true;
		if (chr.chr_ai.group=="ACTOR") return true;
		if (chr.chr_ai.group=="GREENFORD_MURDERERS") return true;
		return false;		
}


