useful to implement things such as the aos combat abilities (double strike, moving shot etc)
Mobile.swing()
Mobile.swing()
title is not so clear
but i mean a method for mobiles that causes an attack with the weapon (not the animation only)(without waiting the delay and/or movement check for projectables), just like when you are in warmode and swing with your weapon...
useful to implement things such as the aos combat abilities (double strike, moving shot etc)
useful to implement things such as the aos combat abilities (double strike, moving shot etc)
Last edited by Bracco on Sun Jan 14, 2007 8:30 am, edited 1 time in total.
Code: Select all
use uo;
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
PerformAction(a_info.mobile, CInt(attack_anim));
return attack_anim;
endfunctionhm? i mean a REAL swing, not just the animation...SMJ wrote:Copy-pasted from 096 combatHook.srcCode: Select all
use uo; 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 PerformAction(a_info.mobile, CInt(attack_anim)); return attack_anim; endfunction
Then add the damage, too? I'm sorry, but this already exists in the form of:
And the suggestion would limit, rather than expand, POL's combat aesthetics. Every weapon has 9 attack animations, but the coders decide which ones to utilize; by adding them to a custom exported Attack(byref attacker, byref defender) function.
Code: Select all
Attack( byref attacker, byref defender );i dont get itSMJ wrote:Then add the damage, too? I'm sorry, but this already exists in the form of:
And the suggestion would limit, rather than expand, POL's combat aesthetics. Every weapon has 9 attack animations, but the coders decide which ones to utilize; by adding them to a custom exported Attack(byref attacker, byref defender) function.Code: Select all
Attack( byref attacker, byref defender );
i need to cause a physical attack, just as when i am hitting someone with my executioner's axe... it can't be done with attackhook/hitscript, i need to be able to actually cause the attack to happen, not wait till the normal one happens
That's called "externalizing" and can be very easily done with an include function. All it really means is that you take out all of the contents of a program, stick it in a function, then make a call to it in the function, so that several different scripts can perform identical purposes without copy-pasting too much content.
Code: Select all
program DoStuff(who)
return DoStuffFunc(who);
endprogram
Code: Select all
function DoStuffFunc(who)
// Do all the program's functions
endfunction
hm... i think you totally misunderstood what i'm saying.SMJ wrote:That's called "externalizing" and can be very easily done with an include function. All it really means is that you take out all of the contents of a program, stick it in a function, then make a call to it in the function, so that several different scripts can perform identical purposes without copy-pasting too much content.
Code: Select all
program DoStuff(who) return DoStuffFunc(who); endprogramCode: Select all
function DoStuffFunc(who) // Do all the program's functions endfunction
i DON'T want to hook the attack, i want to MAKE IT HAPPEN.
also, i know what hooks, exported functions and such things are.
let me explain in a more ground-to-ground way:
i want to implement, for example, AOS combat ability "Double Strike"
what it does is, basically, to attack 2 times in a row, so when your attack lands another one is instantly fired.
how do you do this nowadays? you can't, unless you do it some tricky way, making a fake attack in an hitscript, but maybe, as you say, you have some attackhook/hitscript you want to run before the new attack lands (if it lands)
not to mention that the movement-check for projectable weapons is done by core, so you won't have anything to hook to implement "Moving Shot" (ofc you can make a "fake attack", but it will result in an heavy script to make it work just as the core does)
I know exactly what you're saying. You want it to fire twice? Externalize it to a function, and fire it twice. You want it to wait until you would swing normally? Make the command put a cprop on the attacker, and then loop it in the syshook; that way it'll skip the attack delay entirely. I haven't tried using projectiles in a syshook myself, but I don't see why "moving shot" couldn't be done. I don't know exactly how AOS "Moving Shot" works, but if it just means that you use it to shoot while moving; just link a script to it. And if you're worried about a syshook stalling the server, just make the packethook immediately Start_Script another file; and you've solved the problem.
As far as a "heavy script", I don't see where you got that conclusion. 096 AND 097 have a syshook, WOD used a syshook, SxC uses a syshook, I use a syshook, and I have never heard of anyone complaining about ANY of them being slow or heavy.
There is no limit to what you can and cannot do; just a limit to creativity, and what you're willing to try.
As far as a "heavy script", I don't see where you got that conclusion. 096 AND 097 have a syshook, WOD used a syshook, SxC uses a syshook, I use a syshook, and I have never heard of anyone complaining about ANY of them being slow or heavy.
There is no limit to what you can and cannot do; just a limit to creativity, and what you're willing to try.
meh, lets suppose that i use a script to make a "fake" attack while moving.SMJ wrote: I don't know exactly how AOS "Moving Shot" works, but if it just means that you use it to shoot while moving; just link a script to it.
i put a start_script in my packethook that fires the "fake attack" script
so i must do something like this
Code: Select all
if( attackhook )
call hook
if hook returned 1, exit
endif
if( weapon is projectile )
try to comsume projectile
if none, exit
exit
if( weapon is projected )
play sound and animations
else
play attack anim
endif
if( combat advancement hook exists )
call it
endif
hit_chance = (weapon_attribute + 50.0) / (2.0 * opponent_weapon_attribute + 50.0)
if( random_float(1.0) < hitchance )
play hit sounds and anims
damage weapon (1 in 100 chance to lose 1 hp)
damage = random_weapon_die_damage
damage_multiplier = tactics + 50
damage_multiplier += strength * 0.2
damage_multiplier *= 0.01
damage *= damage_multiplier
if( opponent has shield )
call parry advancement hook
parry_chance = opponent_parry_skill / 200.0
if( random_float(1.0) < parry_chance )
display parry success
damage -= opponent_shield_ar
endif
endif
if( weapon has no hit script )
choose armor piece hit based on zone coverage percentage
blocked = armor piece ar + character ar_mod
absorbed = blocked / 2
blocked -= absorbed
absorbed += random_int(blocked+1)
damage -= absorbed
if( damage >= 2.0 )
damage *= 0.5
endif
1 in 100 chance for armor piece to lose 1 hp
play hit animation
applydamage(damage)
else
choose armor piece hit based on zone coverage percentage
calc base & raw damage (exactly like above if no hit script)
run hit script
(Core doesn't damage armor if hitscript, do it in the script)
endif
else
play weapon miss sound
endif
sooo its good to make such a thing in a script that probably will run continuosly during pvp battles?
no its not. moreover, there's really no reason to just make a script clone of an internal core function.
and tbh i dont like filling the scriptbase with intricated, tricky things just to fake out something that it's already been done internally
Guess what?
The distro already does it. And nobody's complained. And it really doesn't get that much overhead.
Either apply my advice, or ignore it. I'm never going to use AOS systems; I wasn't particularly impressed with them; but I have seen entire shards pull this kind of thing off, and the distro actually uses it by default. If you get complaints about this being slow, then it's your computer, not POL.
Code: Select all
/* $Id: combatHook.src 788 2006-09-05 01:25:54Z 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: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;
endif
FacingChecks(attacker, defender);
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, 0, gain_flag);
PlayHitSound(a_info, d_info);
var base_damage := CalcBaseDamage(a_info);
var raw_damage := base_damage;
ParryChecks(attacker, defender, 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)
// These first two checks are handled by the core.
//if ( attacker == defender )
// return 0;
//elseif ( !CheckLineOfSight(attacker, defender) )
// return 0;
//
if ( !attacker.warmode && !g_settings_cfg["Settings"].AutoDefend )
return 0;
endif
return 1;
endfunction
function FacingChecks(attacker, defender)
if ( g_settings_cfg["Settings"].ForceFacing )
if ( !IsFacing(attacker, defender.x, defender.y) )
TurnObjectToward(attacker, defender.x, defender.y);
endif
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)
// Core handles distance checks before starting the hook.
return 1;
/*
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. ["+max_range+"]", "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
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 dmg_mult := CDbl(AP_GetSkill(a_info.mobile, TACTICS))+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;
endif
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, PARRY)) / 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 -= 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);
if ( armor_hit.Size() < 1 )
return 0;
endif
var best_armor := 0;
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) + (d_info.mobile).ar_mod;
var absorbed := blocked / 2;
blocked := blocked - absorbed;
absorbed := absorbed + RandomInt(blocked+1)+1;
raw_damage := raw_damage - absorbed;
if ( raw_damage >= 2.0 )
raw_damage := 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 -= 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 := GetObjProperty(a_info.mobile, "HitScripts");
elseif ( ((a_info.mobile).weapon).IsA(POLCLASS_WEAPON) )
weapon_scripts := GetObjProperty((a_info.mobile).weapon, "HitScripts");
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 body_scripts := GetObjProperty(d_info.mobile, "ArmorHitScripts");
var armor_scripts := GetObjProperty(armor_hit, "ArmorHitScripts");
if ( !body_scripts )
body_scripts := array{};
endif
if ( !armor_scripts )
armor_scripts := array{};
endif
armor_scripts := armor_scripts + body_scripts;
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;
endfunctionEither apply my advice, or ignore it. I'm never going to use AOS systems; I wasn't particularly impressed with them; but I have seen entire shards pull this kind of thing off, and the distro actually uses it by default. If you get complaints about this being slow, then it's your computer, not POL.
Last edited by SMJ on Sun Jan 14, 2007 9:52 am, edited 1 time in total.
fine, so you're telling there's no reason to have it core side no?
well, i disagree. have you ever seen how such scripts can suck up resources when there are a coulpe hundreds ppl online fighting continuously?
i have, and i didn't like the result
edit: advice what? you just came here and sentenced it itsn't necessary. also you say that its good to use crappy methods, just pump up the hardware (oh, just to point it out, hooking isnt a crappy method, faking an entire existing system is)
btw, enough with discussion. let devs decide if this is worth implementing or not
well, i disagree. have you ever seen how such scripts can suck up resources when there are a coulpe hundreds ppl online fighting continuously?
i have, and i didn't like the result
edit: advice what? you just came here and sentenced it itsn't necessary. also you say that its good to use crappy methods, just pump up the hardware (oh, just to point it out, hooking isnt a crappy method, faking an entire existing system is)
btw, enough with discussion. let devs decide if this is worth implementing or not
The nice thing about hooked (exported) scripts is that the core caches.. sort of.. remembers the code. This is why they cant be unloaded and reloaded, too.
It just goes through and refreshes the data it is instructed to.. very optimized part of the execution engine. Only one instance of a hook can run at a time but they run so quickly and in their own threads, that they it isn't too big a deal.
It just goes through and refreshes the data it is instructed to.. very optimized part of the execution engine. Only one instance of a hook can run at a time but they run so quickly and in their own threads, that they it isn't too big a deal.
Actually for aos combat systems like the double strike, etc, it IS doable without hooking the core combat. To the best of my knowledge about the only thing not doable is the Lightning Strike of Bushido because it enhances your ability to hit the opponent (totally core controlled). The others are modifiers to the successful attack. Others are speed mods, which .delay can be used for this now with core combat without hooking. I have not messed with combat hooks, so don't know personally if you can set the hit percentage chance in the script or not. If so, then even lightning strike is done.
From my personal pvp/pvm on OSI, with all the basic skills, Spellweaving, Bushido, Necro, Chivalry, etc (all cept ninjitsu), including the combat book mods, it's all doable in the hitscripts and so forth without hardly any overhead required at all. Only if you must hook core combat might that make more overhead than desired I would think.
From my personal pvp/pvm on OSI, with all the basic skills, Spellweaving, Bushido, Necro, Chivalry, etc (all cept ninjitsu), including the combat book mods, it's all doable in the hitscripts and so forth without hardly any overhead required at all. Only if you must hook core combat might that make more overhead than desired I would think.