armrzone.cfg doubt

Get Help on scripting in POL with configurations, config setups, script trouble shooting, etc.
Post Reply
phao
Grandmaster Poster
Posts: 129
Joined: Fri Aug 31, 2007 2:25 pm

armrzone.cfg doubt

Post by phao »

Can I use info. on armrzone.cfg having my own combat script or is armrzone.cfg info. exclusively for CORE's combat program?

I've seen that sections in the armrzone.cfg file don't have keys, so I can't access them using cfgfile.em module functions. Any other way I can access them?
User avatar
Austin
Former Developer
Posts: 621
Joined: Wed Jan 25, 2006 2:30 am

Re: armrzone.cfg doubt

Post by Austin »

Look at the 098 or 099 distro SVN for how I made it use it.
I gave all the elems names in the config file (the core doesnt really care if they are there or not) and wrote code to use the chances, coverages, etc. in the combat hook, and an include file to handle the armorzone.cfg settings and a whole lotta other stuff.

Armrzone.cfg

Code: Select all

# $$
#
# Purpose:
# Defines armor zones for character layers, and chance to hit each zone.
#
# Structure:
# ArmorZone
# {
#   Name	(string zone name)
#   Chance	(int chance to hit)
#   [Layer  (int UO client layer)]
# }
#
# Explanation:
# Chance is a percent chance to hit this zone.
# I.e. 'Body' might be a 44 percent chance to hit, and 'Head' a 14 percent chance.
# Layer is for the different equipped item layers the UO client uses.
# For example, items that are only equipped on the arms (like platemail arms) are layer 19.
# You may define multiple layers per zone.
#

ArmorZone Body
{
	Name	Body
	Chance	44
	Layer	13
	Layer	20
	Layer	22
	Layer	5
	Layer	17
}

ArmorZone Arms
{
	Name	Arms
	Chance	14
	Layer	19
}

ArmorZone Head
{
	Name	Head
	Chance	14
	Layer	6
}

ArmorZone Legs/Feet
{
	Name	Legs/feet
	Chance	14
	Layer	4
	Layer	3
	Layer	24
}

ArmorZone Neck
{
	Name	Neck
	Chance	7
	Layer	10
}

ArmorZone Hands
{
	Name	Hands
	Chance	7
	Layer	7
}
combatHook.src

Code: Select all

/* $Id: combatHook.src 119 2009-07-19 22:30:25Z AustinHeilman $
 *
 * NOTES:
 *	Return 0 if you want the core to handle the
 *	combat cycle. Return 1 to say that the cycle is over.
 */

use uo;
use os;
use polsys;
use cfgfile;

include ":attributes:attributes";
include ":brainai:npcUtil";
include ":armor:armorZones";
include ":combat:hitScripts";
include ":combat:settings";
include ":damage:damage";
include ":itemutils:itemdesc";
include "include/client";
include "include/facings";

/*
 * Global variables
 * With the way hooks work, these are only set only ONCE
 * and stay the same in every instance the hook gets run.
 */
var g_item_cfg := ReadConfigFile(":*:itemdesc");
var g_settings_cfg := CS_GetSettingsCfgFile();

program Install()
	print("INSTALLING: Combat hook... ");
	return 1;
endprogram

exported function Attack(attacker, defender)
	if ( !g_settings_cfg["Settings"].EnableHook )
		return 0;
	elseif ( !CanAttack(attacker, defender) )
		return 1;
	endif

	var a_info, d_info;
	SetupInfo(attacker, defender, a_info, d_info);

	if ( !DistanceChecks(a_info, d_info) )
		return 1;
	elseif ( !AmmoChecks(a_info, d_info) )
		return 1;
	elseif ( !FacingChecks(attacker, defender) )
		return 1;
	endif
	
	PlayAttackAnimation(a_info);

	var hit_chance := CalcHitDifficulty(a_info, d_info);
	if ( RandomFloat(1.0) < hit_chance )
		var attribute := GetConfigString(a_info.cfginfo, a_info.prefix+"Attribute");
		var gain_flag := GetCombatGainFlags(attacker, defender);
		SkillCheck(attacker, attribute, -1, "", gain_flag);

		PlayHitSound(a_info, d_info);

		var base_damage := CalcBaseDamage(a_info);
		var raw_damage := base_damage;
		ParryChecks(a_info, d_info, raw_damage);

		var armor_hit := GetArmorHit(d_info);
		ArmorChecks(d_info, armor_hit, raw_damage);

		RunWeaponHitScripts(a_info, d_info, armor_hit, base_damage, raw_damage);
		RunArmorHitScripts(a_info, d_info, armor_hit, base_damage, raw_damage);

		ApplyRawDamageEX(d_info.mobile, raw_damage, DMG_FORCED, a_info.mobile);
	else
		PlayMissSound(a_info);
	endif

	return 1;
endfunction

function GetCombatGainFlags(attacker, defender)
	if ( defender.npctemplate )
		return ADV_ALL;
	elseif ( attacker.npctemplate )
		return ADV_ALL;
	elseif ( g_settings_cfg["Settings"].PvPGains )
		return ADV_ALL;
	else
		return ADV_DISABLE;
	endif
endfunction

function CanAttack(attacker, defender)
	if ( attacker == defender )
		return 0;
	elseif ( defender.dead || attacker.dead )
		return 0;
	elseif ( defender.hidden || attacker.hidden )
		return 0;
	elseif ( defender.concealed > attacker.cmdlevel )
		return 0;
	elseif ( !CheckLineOfSight(attacker, defender) )
		return 0;
	elseif ( !attacker.warmode && !g_settings_cfg["Settings"].AutoDefend )
		return 0;
	endif

	return 1;
endfunction

function FacingChecks(attacker, defender)
	if ( g_settings_cfg["Settings"].ForceFacing )
		if ( attacker.x == defender.x && attacker.y == defender.y )
				return 1;
		elseif ( !InPosition(attacker, defender.x, defender.y, FACE_POS_INFRONT) )
			if ( attacker.npctemplate )
				TurnObjectToward(attacker, defender.x, defender.y);
			else
				CombatMsg(attacker, "You need to be facing "+defender.name+" to attack.", "Facing");
				return 0;
			endif
		endif
		// Should only turn the attacker
		//if ( !IsFacing(defender, attacker.x, attacker.y) )
		//	TurnObjectToward(defender, attacker.x, attacker.y);
		//endif
	endif

	return 1;
endfunction

function DistanceChecks(byref a_info, byref d_info)
	var cur_range := Distance(a_info.mobile, d_info.mobile);
	var max_range := GetConfigInt(a_info.cfginfo, a_info.prefix+"MaxRange");
	if ( max_range == error )
		max_range := 1;
	endif

	if ( cur_range > max_range )
		CombatMsg(a_info.mobile, "Opponent is too far away.", "Dist");
		return 0;
	elseif ( cur_range < CInt(GetConfigInt(a_info.cfginfo, a_info.prefix+"MinRange")) )
		CombatMsg(a_info.mobile, "Opponent is too close.", "Dist");
		return 0;
	else
		return 1;
	endif
endfunction

function AmmoChecks(byref a_info, byref d_info)
	var ammo_type := CInt((a_info.cfginfo).ProjectileType);
	if ( !ammo_type )
		return 1;
	endif

	if ( ConsumeSubstance((a_info.mobile).backpack, ammo_type, 1) )
		PlaySoundEffect(a_info.mobile, CInt((a_info.cfginfo).ProjectileSound));
		PlayMovingEffect(a_info.mobile, d_info.mobile, (a_info.cfginfo).ProjectileAnim, 10, 0);
		return 1;
	else
		CombatMsg(a_info.mobile, "You do not have any "+GetObjTypeDesc(ammo_type, 1)+"!", "Ammo");
		return 0;
	endif
endfunction

function PlayAttackAnimation(byref a_info)
	var attack_anim := ANIM_FIDGET_1; // Default attack anim for NPCs.
	if ( (a_info.cfginfo).Anim )
		// Normal weapons
		var anim_list := GetConfigStringArray(a_info.cfginfo, "Anim");
		attack_anim := anim_list[RandomInt(anim_list.Size())+1];
	elseif ( (a_info.cfginfo).AttackAnimation )
		// NPC intrinsic weapons
		var anim_list := GetConfigStringArray(a_info.cfginfo, "AttackAnimation");
		attack_anim := anim_list[RandomInt(anim_list.Size())+1];
	endif

	if ( GetEquipmentbyLayer(a_info.mobile, LAYER_MOUNT) )
		var xlate_cfg := ReadConfigFile("::animxlate");
		if ( xlate_cfg )
			var cfg_elem := xlate_cfg["OnMount"];
			if ( cfg_elem )
				var change := GetConfigInt(cfg_elem, Hex(attack_anim));
				if ( change )
					attack_anim := change;
				endif
			endif
		endif
	endif


	PerformAction(a_info.mobile, CInt(attack_anim));

	return attack_anim;
endfunction

function CalcHitDifficulty(byref a_info, byref d_info)
	//hit_chance = (weapon_attribute + 50.0) / (2.0 * opponent_weapon_attribute + 50.0)
	var a_skill := AP_GetSkill(a_info.mobile, GetConfigString(a_info.cfginfo, a_info.prefix+"Attribute"));
	var d_skill := AP_GetSkill(d_info.mobile, GetConfigString(d_info.cfginfo, d_info.prefix+"Attribute"));

	return ((a_skill + 50.0) / (2.0 * d_skill + 50));
endfunction

function PlayHitSound(byref a_info, byref d_info)
	var hit_sound := GetConfigStringArray(a_info.cfginfo, a_info.prefix+"HitSound");
	hit_sound := hit_sound[RandomInt(hit_sound.Size())+1];
	PlaySoundEffect(a_info.mobile, CInt(hit_sound));

	var damaged_sound;
	if ( (d_info.mobile).npctemplate )
		damaged_sound := GetConfigStringArray(d_info.cfginfo, "DamagedSound");
	else
		case ( (d_info.mobile).gender )
			0:// Male
				damaged_sound := array{341, 342, 343, 345, 346};
				break;
			1://Female
				damaged_sound := array{332, 333, 334, 335, 336};
				break;
		endcase
	endif
	damaged_sound := damaged_sound[RandomInt(damaged_sound.Size())+1];

	PlaySoundEffect(d_info.mobile, CInt(damaged_sound));

	return 1;
endfunction

function PlayMissSound(byref a_info)
	var miss_sound := GetConfigStringArray(a_info.cfginfo, a_info.prefix+"MissSound");
	miss_sound := miss_sound[RandomInt(miss_sound.Size())+1];
	PlaySoundEffect(a_info.mobile, CInt(miss_sound));

	return 1;
endfunction

function CalcBaseDamage(byref a_info)
	var base_dmg := GetConfigString(a_info.cfginfo, a_info.prefix+"Damage");
	base_dmg := RandomDiceRoll(base_dmg);

	var attribute := GetConfigString(a_info.cfginfo, a_info.prefix+"Attribute");
	var dmg_mult := CDbl(AP_GetSkill(a_info.mobile, attribute))+50.0;
	dmg_mult += (CDbl(AP_GetStat(a_info.mobile, STRENGTH)) * 0.2);
	dmg_mult := CDbl(dmg_mult) * 0.01;
	base_dmg *= dmg_mult;

	return CInt(base_dmg);
endfunction

function ParryChecks(byref a_info, byref d_info, byref raw_damage)
	var shield := (d_info.mobile).shield;
	if ( !shield )
		return 0;
	elseif ( !InPosition(d_info.mobile, a_info.mobile.x, a_info.mobile.y, FACE_POS_INFRONT) )
		return 0;
	endif

	var attribute := GetConfigString(a_info.cfginfo, a_info.prefix+"Attribute");
	var parry_elem := g_settings_cfg["Parry"];
	var divisor := CDbl(parry_elem.ParryDivisor);
	var roll := CDbl(parry_elem.ParryRoll);
	var parry_chance := CDbl(AP_GetSkill(a_info.mobile, attribute)) / divisor;

	if ( RandomFloat(roll) < parry_chance )
		PerformAction(d_info.mobile, ANIM_TWIST_DODGE);
		SendSysMessage(d_info.mobile, "You deflect some damage using your shield.");
		raw_damage -= shield.ar;

		var armor_elem := g_settings_cfg["Armor"];
		if ( RandomInt(100)+1 <= armor_elem.WearChance )
			SendSysMessage(d_info.mobile, shield.desc+" takes some damage.");
			shield.hp := shield.hp - 1;

			if ( shield.hp <= 1 )
				MoveObjectToLocation(shield, 1, 1, 1, shield.realm, MOVEOBJECT_FORCELOCATION);
				SendSysMessage(d_info.mobile, shield.desc+" has been destroyed.");
				DestroyItem(shield);
			endif
		endif
	endif

	return 1;
endfunction

function GetArmorHit(byref d_info)
	var hit_zone := CS_GetRandomArmorZone();
	var armor_hit := CS_GetEquipmentInArmorZone(d_info.mobile, hit_zone);

	var best_armor := struct;
	best_armor.name := "nothing-"+hit_zone;
	best_armor.desc := "nothing"+hit_zone;
	best_armor.ar := 0;
	
	if ( armor_hit.Size() < 1 )
		return 0;
	endif

	foreach item in ( armor_hit )
		if ( item.ar > best_armor.ar )
			best_armor := item;
		endif
		SleepMS(2);
	endforeach

	return best_armor;
endfunction

function ArmorChecks(byref d_info, armor_hit, byref raw_damage)
	var blocked := CInt(armor_hit.ar) + CInt((d_info.mobile).ar_mod);
	var absorbed := blocked / 2;
	blocked -= absorbed;
	absorbed += RandomInt(blocked+1);
	raw_damage -= absorbed;

	// Why is this *0.5 part in the pseudo code?
	//if ( raw_damage >= 2.0 )
	//	raw_damage *= 0.5;
	//endif
	raw_damage := CInt(raw_damage);

	if ( !armor_hit.IsA(POLCLASS_ARMOR) )
		return 1;
	endif

	var armor_elem := g_settings_cfg["Armor"];
	if ( RandomInt(100)+1 <= armor_elem.WearChance )
		SendSysMessage(d_info.mobile, armor_hit.desc+" takes some damage.");
		armor_hit.hp := armor_hit.hp - 1;

		if ( armor_hit.hp <= 1 )
			MoveObjectToLocation(armor_hit, 1, 1, 1, armor_hit.realm, MOVEOBJECT_FORCELOCATION);
			SendSysMessage(d_info.mobile, armor_hit.desc+" has been destroyed.");
			DestroyItem(armor_hit);
		endif
	endif

	return 1;
endfunction

function RunWeaponHitScripts(byref a_info, byref d_info, armor_hit, base_damage, raw_damage)
	var weapon_scripts := array{};
	if ( ((a_info.mobile).weapon).intrinsic )
		weapon_scripts := GetWeaponHitScripts(a_info.mobile);
	elseif ( ((a_info.mobile).weapon).IsA(POLCLASS_WEAPON) )
		weapon_scripts := GetWeaponHitScripts((a_info.mobile).weapon);
	endif

	var params := array{a_info.mobile, d_info.mobile, (a_info.mobile).weapon, armor_hit, base_damage, raw_damage};
	foreach hitscript in ( weapon_scripts )
		var script := Start_Script(hitscript, params);
		if ( !script || script.errortext )
			SendSysMessage(a_info.mobile, "*Attacker* Weapon script error starting ["+hitscript+"] :"+script.errortext);
			SendSysMessage(d_info.mobile, "*Attacker* Weapon script error starting ["+hitscript+"] :"+script.errortext);
		endif
		SleepMS(2);
	endforeach

	return 1;
endfunction

function RunArmorHitScripts(byref a_info, byref d_info, armor_hit, base_damage, raw_damage)
	var armor_scripts := GetArmorHitScripts(d_info.mobile, (d_info.mobile).weapon);

	var params := array{a_info.mobile, d_info.mobile, (a_info.mobile).weapon, armor_hit, base_damage, raw_damage};
	foreach hitscript in ( armor_scripts )
		var script := Start_Script(hitscript, params);
		if ( !script || script.errortext )
			SendSysMessage(a_info.mobile, "*Defender* Armor script error starting ["+hitscript+"] :"+script.errortext);
			SendSysMessage(d_info.mobile, "*Defender* Armor script error starting ["+hitscript+"] :"+script.errortext);
		endif
		SleepMS(2);
	endforeach

	return 1;
endfunction

function SetupInfo(attacker, defender, byref a_info, byref d_info)
	a_info := struct;
	a_info.+mobile := attacker;
	if ( attacker.IsA(POLCLASS_NPC) && (attacker.weapon).intrinsic )
		a_info.+prefix := "Attack";
		a_info.+cfginfo := NPC_GetNPCConfig(attacker.npctemplate);
	else
		a_info.+prefix := "";
		a_info.+cfginfo := g_item_cfg[(attacker.weapon).objtype];
	endif

	d_info := struct;
	d_info.+mobile := defender;
	if ( defender.IsA(POLCLASS_NPC) && (defender.weapon).intrinsic )
		d_info.+prefix := "Attack";
		d_info.+cfginfo := NPC_GetNPCConfig(defender.npctemplate);
	else
		d_info.+prefix := "";
		d_info.+cfginfo := g_item_cfg[(defender.weapon).objtype];
	endif

	return 1;
endfunction

function CombatMsg(mobile, text, type:="")
	// This is done just to prevent message spam on fast weapons.
	if ( CInt(GetObjProperty(mobile, "#CH-Msg"+type)) < ReadMillisecondClock() )
		SendSysMessage(mobile, text);
		SetObjProperty(mobile, "#CH-Msg"+type, ReadMillisecondClock()+800);
	endif

	return 1;
endfunction
armorZones.inc

Code: Select all

/* $Id: armorZones.inc 1051 2007-04-25 05:17:47Z tekproxy $
 *
 */

use uo;
use os;
use util;
use cfgfile;


/*
 * CS_GetRandomArmorZone()
 *
 * Purpose
 * Reads in pol/config/armrzone.cfg and based on the 'chance' settings,
 * selects a zone that was hit.
 *
 * Parameters
 * None
 *
 * Return Value
 * String
 *
 */
function CS_GetRandomArmorZone()
	// This function assumes that the 'Chance' totals in
	// armrzone.cfg all add up to 100. You will most likely
	// get the error return value if they do not.

	var cfg_file := ReadConfigFile("::armrzone");

	//last_num stores the max roll for that zone
	var last_num := 0;
	var max_num := 0;

	//Roll determines the layer we hit.
	//In the future, we may want to have anatomy affect this?
	var roll := RandomInt(100)+1;
	foreach zone_name in ( GetConfigStringKeys(cfg_file) )
		var cfg_elem := cfg_file[zone_name];
		last_num := max_num;
		max_num := last_num + CInt(cfg_elem.Chance);
		
		if ( roll > last_num && roll <= max_num  )
			return cfg_elem.Name;
		endif
		SleepMS(2);
	endforeach
	
	return error{"errortext":="Could not select a hit zone!"};
endfunction

/* CS_GetLayersInArmorZone(zone_name)
 *
 * Purpose
 * Retrieves the layer numbers an armor zone.
 *
 * Parameters
 * zone_name:	Name of the armor zone.
 *
 * Return value
 * Array
 *
 */
function CS_GetLayersInArmorZone(zone_name)
	var cfg_file := ReadConfigFile("::armrzone");
	var cfg_elem := cfg_file[zone_name];
	
	return GetConfigStringArray(cfg_elem, "Layer");
endfunction
	
/*
 * CS_GetEquipmentInArmorZone(mobile, zone_name)
 *
 * Purpose
 * Returns a list of all equipment that a mobile is wearing
 * that covers an armor zone.
 *
 * An armor piece can cover layers other than its .layer property
 * which is why this function is here. A chainmail tunic for example,
 * often covers arms and body.
 *
 * Parameters
 * mobile:	Mobile to evaluate the equipment on.
 * zone_name:	Armor zone to find equipment for.
 *
 * Return Value
 * String
 *
 */
function CS_GetEquipmentInArmorZone(mobile, zone_name)
	var itemdesc_cfg := ReadConfigFile(":*:itemdesc");
	var equip_list := array{};
	
	zone_name := Lower(zone_name);
	
	foreach item in ( ListEquippedItems(mobile) )
		var item_info := itemdesc_cfg[item.objtype];
		
		foreach zone in ( GetConfigStringArray(item_info, "Coverage") )
			if ( Lower(zone) == zone_name )
				equip_list.Append(item);
				break;
			endif
			SleepMS(2);
		endforeach
		
		SleepMS(2);
	endforeach
	
	return equip_list;
endfunction
phao
Grandmaster Poster
Posts: 129
Joined: Fri Aug 31, 2007 2:25 pm

Re: armrzone.cfg doubt

Post by phao »

Thank you. Knowing that the CORE doesn't care if names are present or not is very useful.
Post Reply