Live-like Throw Weapon

A place to submit .patch fixes for the DOL SVN

Moderator: Developer Team

Live-like Throw Weapon

Postby LifeFlight » Sat Mar 07, 2009 7:59 am

A thrown weapon can now miss, be parried, blocked, evaded, etc..
A thrown weapon that does not successfully hit the target does not disarm the user, and the user can throw weapon again based on their attack speed.
Combat animation and texts are all corrected.

The disable from thrown weapon is now a separate spell that is cast via the script. I did this because it was the best way to disable the player only when their attack was successful and provide a spell effect to notify them they were disabled.

These are the code changes to Battlemaster.cs

Need to add some 'using' statements to the top of Battlemaster.cs
Code: Select all
using DOL.GS;
using DOL.Language;
using DOL.AI.Brain;
This part just replaces the older ThrowWeapon Handler in Battlemaster.cs
Code: Select all
#region Battlemaster-6
//Eden-Darwin
//Lifeflight
[SpellHandlerAttribute("ThrowWeapon")]
public class ThrowWeaponSpellHandler : DirectDamageSpellHandler
{
public const string DISABLE = "ThrowWeapon.Shortened.Disable.Timer";
public const int DISARMSPELLID = 7357;
public const string DISARMSPELLLINE = "Combat Style Effects";

public override bool CheckBeginCast(GameLiving selectedTarget)
{
GamePlayer player = Caster as GamePlayer;
if(player == null)
return false;

if (player.IsDisarmed)
{
MessageToCaster("You are disarmed and can't use this spell!", eChatType.CT_YouHit);
return false;
}

InventoryItem weapon = null;

//assign the weapon the player is using, it can be a twohanded or a standard slot weapon
if (player.ActiveWeaponSlot.ToString() == "TwoHanded")
weapon = player.Inventory.GetItem((eInventorySlot)12);
if (player.ActiveWeaponSlot.ToString() == "Standard")
weapon = player.Inventory.GetItem((eInventorySlot)10);

//if the weapon is null, ie. they don't have an appropriate weapon active
if(weapon == null)
{
MessageToCaster("Equip a weapon before using this spell!",eChatType.CT_SpellResisted);
return false;
}

return base.CheckBeginCast(selectedTarget);
}

//Throw Weapon does not "resist"
public override int CalculateSpellResistChance(GameLiving target)
{
return 0;
}

public override int OnEffectExpires(GameSpellEffect effect, bool noMessages)
{
return base.OnEffectExpires(effect, noMessages);
}


public override void OnDirectEffect(GameLiving target, double effectiveness)
{
if (target == null) return;

if (!target.IsAlive || target.ObjectState != GameLiving.eObjectState.Active) return;

// calc damage
AttackData ad = CalculateDamageToTarget(target, effectiveness);
DamageTarget(ad, true);
SendDamageMessages(ad);
target.StartInterruptTimer(SPELL_INTERRUPT_DURATION, ad.AttackType, Caster);
}


public override void DamageTarget(AttackData ad, bool showEffectAnimation)
{
InventoryItem weapon = null;
weapon = ad.Weapon;

if (showEffectAnimation && ad.Target != null)
{
byte resultByte = 0;
int attackersWeapon = (weapon == null) ? 0 : weapon.Model;
int defendersWeapon = 0;

switch (ad.AttackResult)
{
case GameLiving.eAttackResult.Missed: resultByte = 0; break;
case GameLiving.eAttackResult.Evaded: resultByte = 3; break;
case GameLiving.eAttackResult.Fumbled: resultByte = 4; break;
case GameLiving.eAttackResult.HitUnstyled: resultByte = 10; break;
case GameLiving.eAttackResult.HitStyle: resultByte = 11; break;
case GameLiving.eAttackResult.Parried:
resultByte = 1;
if (ad.Target != null && ad.Target.AttackWeapon != null)
{
defendersWeapon = ad.Target.AttackWeapon.Model;
}
break;
case GameLiving.eAttackResult.Blocked:
resultByte = 2;
if (ad.Target != null && ad.Target.Inventory != null)
{
InventoryItem lefthand = ad.Target.Inventory.GetItem(eInventorySlot.LeftHandWeapon);
if (lefthand != null && lefthand.Object_Type == (int)eObjectType.Shield)
{
defendersWeapon = lefthand.Model;
}
}
break;
}

foreach (GamePlayer player in ad.Target.GetPlayersInRadius(WorldMgr.VISIBILITY_DISTANCE))
{
if (player == null) continue;
int animationId;
switch (ad.AnimationId)
{
case -1:
animationId = player.Out.OneDualWeaponHit;
break;
case -2:
animationId = player.Out.BothDualWeaponHit;
break;
default:
animationId = ad.AnimationId;
break;
}
//We don't need to send the animiation for the throwning, thats been done earlier.

//this is for the defender, which should show the appropriate animation
player.Out.SendCombatAnimation(null, ad.Target, (ushort)attackersWeapon, (ushort)defendersWeapon, animationId, 0, resultByte, ad.Target.HealthPercent);

}
}

// send animation before dealing damage else dead livings show no animation
ad.Target.OnAttackedByEnemy(ad);
ad.Attacker.DealDamage(ad);
if (ad.Damage == 0 && ad.Target is GameNPC)
{
IAggressiveBrain aggroBrain = ((GameNPC)ad.Target).Brain as IAggressiveBrain;
if (aggroBrain != null)
aggroBrain.AddToAggroList(Caster, 1);
}

}

public override void SendDamageMessages(AttackData ad)
{
GameObject target = ad.Target;
InventoryItem weapon = ad.Weapon;
GamePlayer player = Caster as GamePlayer;

switch (ad.AttackResult)
{
case GameLiving.eAttackResult.TargetNotVisible: player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.NotInView", ad.Target.GetName(0, true)), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); break;
case GameLiving.eAttackResult.OutOfRange: player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.TooFarAway", ad.Target.GetName(0, true)), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); break;
case GameLiving.eAttackResult.TargetDead: player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.AlreadyDead", ad.Target.GetName(0, true)), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); break;
case GameLiving.eAttackResult.Blocked: player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.Blocked", ad.Target.GetName(0, true)), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); break;
case GameLiving.eAttackResult.Parried: player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.Parried", ad.Target.GetName(0, true)), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); break;
case GameLiving.eAttackResult.Evaded: player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.Evaded", ad.Target.GetName(0, true)), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); break;
case GameLiving.eAttackResult.NoTarget: player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.NeedTarget"), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); break;
case GameLiving.eAttackResult.NoValidTarget: player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.CantBeAttacked"), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); break;
case GameLiving.eAttackResult.Missed: player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.Miss"), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); break;
case GameLiving.eAttackResult.Fumbled: player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.Fumble"), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow); break;
case GameLiving.eAttackResult.HitStyle:
case GameLiving.eAttackResult.HitUnstyled:
string modmessage = "";
if (ad.Modifier > 0) modmessage = " (+" + ad.Modifier + ")";
if (ad.Modifier < 0) modmessage = " (" + ad.Modifier + ")";

string hitWeapon = "";

switch (ServerProperties.Properties.SERV_LANGUAGE)
{
case "EN":
if (weapon != null)
hitWeapon = GlobalConstants.NameToShortName(weapon.Name);
break;
case "DE":
if (weapon != null)
hitWeapon = weapon.Name;
break;
default:
if (weapon != null)
hitWeapon = GlobalConstants.NameToShortName(weapon.Name);
break;
}

if (hitWeapon.Length > 0)
hitWeapon = " " + LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.WithYour") + " " + hitWeapon;

string attackTypeMsg = LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.YouAttack");

// intercept messages
if (target != null && target != ad.Target)
{
player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.Intercepted", ad.Target.GetName(0, true), target.GetName(0, false)), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow);
player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.InterceptedHit", attackTypeMsg, target.GetName(0, false), hitWeapon, ad.Target.GetName(0, false), ad.Damage, modmessage), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow);
}
else
player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.InterceptHit", attackTypeMsg, ad.Target.GetName(0, false), hitWeapon, ad.Damage, modmessage), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow);

// critical hit
if (ad.CriticalDamage > 0)
player.Out.SendMessage(LanguageMgr.GetTranslation(player.Client, "GamePlayer.Attack.Critical", ad.Target.GetName(0, false), ad.CriticalDamage), eChatType.CT_YouHit, eChatLoc.CL_SystemWindow);
break;
}
}

public override void FinishSpellCast(GameLiving target)
{
base.FinishSpellCast(target);

//we need to make sure the spell is only disabled if the attack was a success
int isDisabled = Caster.TempProperties.getIntProperty(DISABLE, 0);

//if this value is greater than 0 then we know that their weapon did not damage the target
//the skill's disable timer should be set to their attackspeed
if (isDisabled > 0)
{
Caster.DisableSkill(Spell, isDisabled);

//remove the temp property
Caster.TempProperties.removeProperty(DISABLE);
}
else
{
//they disarm them selves.
ISpellHandler spellHandler = ScriptMgr.CreateSpellHandler(Caster, SkillBase.GetSpellByID(DISARMSPELLID), SkillBase.GetSpellLine(DISARMSPELLLINE));
if (spellHandler != null)
{
//TargetObject = target;
spellHandler.StartSpell(Caster);
}
//remove the temp property
Caster.TempProperties.removeProperty(DISABLE);

}
}

public override void ApplyEffectOnTarget(GameLiving target, double effectiveness)
{
GamePlayer player = target as GamePlayer;

foreach (GamePlayer visPlayer in Caster.GetPlayersInRadius((ushort)WorldMgr.VISIBILITY_DISTANCE))
{
visPlayer.Out.SendCombatAnimation(Caster, target, 0x0000, 0x0000, (ushort)408, 0, 0x00, target.HealthPercent);
}

OnDirectEffect(target, effectiveness);

}

public override AttackData CalculateDamageToTarget(GameLiving target, double effectiveness)
{
GamePlayer player = Caster as GamePlayer;

if (player == null)
return null;

InventoryItem weapon = null;

if (player.ActiveWeaponSlot.ToString() == "TwoHanded")
weapon = player.Inventory.GetItem((eInventorySlot)12);
if (player.ActiveWeaponSlot.ToString() == "Standard")
weapon = player.Inventory.GetItem((eInventorySlot)10);

if (weapon == null)
return null;

//create the AttackData
AttackData ad = new AttackData();
ad.Attacker = player;
ad.Target = target;
ad.Damage = 0;
ad.CriticalDamage = 0;
ad.WeaponSpeed = player.AttackSpeed(weapon) / 100;
ad.DamageType = player.AttackDamageType(weapon);
ad.Weapon = weapon;
ad.IsOffHand = weapon.Hand == 2;
//we need to figure out which armor piece they are going to hit.



//figure out the attacktype
switch (weapon.Item_Type)
{
default:
case Slot.RIGHTHAND:
case Slot.LEFTHAND:
ad.AttackType = AttackData.eAttackType.MeleeOneHand;
break;
case Slot.TWOHAND:
ad.AttackType = AttackData.eAttackType.MeleeTwoHand;
break;
}

//Throw Weapon is subject to all the conventional attack results, parry, evade, block, etc.
ad.AttackResult = ad.Target.CalculateEnemyAttackResult(ad, weapon);

if (ad.AttackResult == GameLiving.eAttackResult.HitUnstyled || ad.AttackResult == GameLiving.eAttackResult.HitStyle)
{
//we only need to calculate the damage if the attack was a success.
double damage = player.AttackDamage(weapon) * effectiveness;

if (target is GamePlayer)
ad.ArmorHitLocation = ((GamePlayer)target).CalculateArmorHitLocation(ad);

InventoryItem armor = null;
if (target.Inventory != null)
armor = target.Inventory.GetItem((eInventorySlot)ad.ArmorHitLocation);

//calculate the lowerboundary of the damage
int lowerboundary = (player.WeaponSpecLevel(weapon) - 1) * 50 / (ad.Target.EffectiveLevel + 1) + 75;
lowerboundary = Math.Max(lowerboundary, 75);
lowerboundary = Math.Min(lowerboundary, 125);

damage *= (player.GetWeaponSkill(weapon) + 90.68) / (ad.Target.GetArmorAF(ad.ArmorHitLocation) + 20 * 4.67);

//If they have badge of Valor, we need to modify the damage
if (ad.Attacker.EffectList.GetOfType(typeof(BadgeOfValorEffect)) != null)
damage *= 1.0 + Math.Min(0.85, ad.Target.GetArmorAbsorb(ad.ArmorHitLocation));
else
damage *= 1.0 - Math.Min(0.85, ad.Target.GetArmorAbsorb(ad.ArmorHitLocation));

damage *= (lowerboundary + Util.Random(50)) * 0.01;

ad.Modifier = (int)(damage * (ad.Target.GetResist(ad.DamageType) + SkillBase.GetArmorResist(armor, ad.DamageType)) * -0.01);

damage += ad.Modifier;

int resist = (int)(damage * ad.Target.GetDamageResist(target.GetResistTypeForDamage(ad.DamageType)) * -0.01);
eProperty property = ad.Target.GetResistTypeForDamage(ad.DamageType);
int secondaryResistModifier = ad.Target.SpecBuffBonusCategory[(int)property];
int resistModifier = 0;
resistModifier += (int)((ad.Damage + (double)resistModifier) * (double)secondaryResistModifier * -0.01);
damage += resist;
damage += resistModifier;
ad.Modifier += resist;
ad.Damage = (int)damage;
ad.UncappedDamage = ad.Damage;
ad.Damage = Math.Min(ad.Damage, (int)(player.UnstyledDamageCap(weapon) * effectiveness));
ad.Damage = (int)((double)ad.Damage * ServerProperties.Properties.PVP_DAMAGE);
if (ad.Damage == 0) ad.AttackResult = DOL.GS.GameLiving.eAttackResult.Missed;
ad.CriticalDamage = player.CalculateCriticalDamage(ad, weapon);
}
else
{
//They failed, do they do not get disarmed, and the spell is not disabled for the full duration,
//just the modified swing speed, this is in milliseconds
int attackSpeed = player.AttackSpeed(weapon);
player.TempProperties.setProperty(DISABLE, attackSpeed);
}
return ad;
}

public ThrowWeaponSpellHandler(GameLiving caster,Spell spell,SpellLine line) : base(caster,spell,line) {}
}
#endregion
And here are sql scripts for the spells and linexspell.

The spells:
Code: Select all
INSERT INTO spell
(`Spell_ID`, `SpellID`, `ClientEffect`, `Icon`, `Name`, `Description`, `Target`, `Range`, `Power`, `CastTime`, `Damage`, `DamageType`, `Type`, `Duration`, `Frequency`, `Pulse`, `PulsePower`, `Radius`, `RecastDelay`, `ResurrectHealth`, `ResurrectMana`, `Value`, `Concentration`, `LifeDrainReturn`, `AmnesiaChance`, `Message1`, `Message2`, `Message3`, `Message4`, `InstrumentRequirement`, `SpellGroup`, `EffectGroup`, `SubSpellID`, `MoveCast`, `Uninterruptible`, `IsPrimary`, `IsSecondary`, `AllowBolt`, `SharedTimerGroup`, `PackageID`, `IsFocus`)
VALUES
('ML_Battlemaster_6', 7293, 7293, 7293, 'Throw Weapon', 'A ranged attack using melee damage formulas; cannot attack with normal melee for 10 seconds afterwards.', 'Enemy', 700, 0, 0, 0, 0, 'ThrowWeapon', 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, '', '', '', '', 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, NULL, 0);

INSERT INTO spell
(`Spell_ID`, `SpellID`, `ClientEffect`, `Icon`, `Name`, `Description`, `Target`, `Range`, `Power`, `CastTime`, `Damage`, `DamageType`, `Type`, `Duration`, `Frequency`, `Pulse`, `PulsePower`, `Radius`, `RecastDelay`, `ResurrectHealth`, `ResurrectMana`, `Value`, `Concentration`, `LifeDrainReturn`, `AmnesiaChance`, `Message1`, `Message2`, `Message3`, `Message4`, `InstrumentRequirement`, `SpellGroup`, `EffectGroup`, `SubSpellID`, `MoveCast`, `Uninterruptible`, `IsPrimary`, `IsSecondary`, `AllowBolt`, `SharedTimerGroup`, `PackageID`, `IsFocus`)
VALUES
('ML_Battlemaster_6_Disarm', 7357, 10596, 10596, 'Disarm', 'Target is disarmed during spell duration.', 'Self', 0, 0, 0, 0, 0, 'Disarm', 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL);
LinexSpell:
Code: Select all
INSERT INTO linexspell
(`LineXSpell_ID`, `LineName`, `SpellID`, `Level`, `PackageID`)
VALUES
('ML_xBattlemaster_6', 'ML6 Battlemaster', 7293, 50, NULL);

INSERT INTO linexspell
(`LineXSpell_ID`, `LineName`, `SpellID`, `Level`, `PackageID`)
VALUES
('ML_xbattlemaster_6_disarm', 'Combat Style Effects', 7357, 0, NULL);
LifeFlight's PvP.
LifeFlight
Contributor
 
Posts: 114
Joined: Wed Jan 03, 2007 6:07 am
Website: http://lifeflight.utpdr.com
Location: Texas

Re: Live-like Throw Weapon

Postby Graveen » Sat Mar 07, 2009 8:52 am

Please, hand me this is as patch with a current svn ;)

Why i need this:

* integration is taking huge time, patching allows me to do this process far more quickly (integration, testing, reviewing)
* the patch is mainly removing all integration operation :)
Simply take your modified file under SVN control, and right clic -> tortoise -> create a patch file

Thx a lot ! :)
Image
* pm me to contribute in Dawn of Light: code, database *
User avatar
Graveen
Project Leader
 
Posts: 12660
Joined: Fri Oct 19, 2007 9:22 pm
Location: France

Re: Live-like Throw Weapon

Postby LifeFlight » Sat Mar 07, 2009 9:34 am

Alright, will do, gimmi a bit.
LifeFlight's PvP.
LifeFlight
Contributor
 
Posts: 114
Joined: Wed Jan 03, 2007 6:07 am
Website: http://lifeflight.utpdr.com
Location: Texas

Re: Live-like Throw Weapon

Postby LifeFlight » Sat Mar 07, 2009 10:17 am

http://lifeflight.utpdr.com/contribute/ ... ster.patch

There were some other updates to Battlemaster.cs that are included in this.

Spelling changes to the battlemaster ml1 spellhandler: from endudrain to endodrain.
I don't know if this is something that should be done, but since Endo is the proper shortening for endurance I changed it on my server.

Also added some text feedback to the battlemaster ml3 power drain.
LifeFlight's PvP.
LifeFlight
Contributor
 
Posts: 114
Joined: Wed Jan 03, 2007 6:07 am
Website: http://lifeflight.utpdr.com
Location: Texas

Re: Live-like Throw Weapon

Postby Graveen » Sat Mar 07, 2009 6:30 pm

Accepted, soon in SVN, thank you

- i don't change the name, but it should not have any impact since the handler name is the attribute
- here is the diff file with the 1559 svn
Attachments
bm-lifeflight-changes.zip
(4.49 KiB) Downloaded 10 times
Image
* pm me to contribute in Dawn of Light: code, database *
User avatar
Graveen
Project Leader
 
Posts: 12660
Joined: Fri Oct 19, 2007 9:22 pm
Location: France

Re: Live-like Throw Weapon

Postby Roozzz » Sat Mar 07, 2009 7:00 pm

Go go LifeFlight :!:

A lot of fixes from you the past couple of days 8)
Quidquid latine dictum sit, altum videtur
Roozzz
Database Team
 
Posts: 1943
Joined: Wed Dec 06, 2006 11:00 am

Re: Live-like Throw Weapon

Postby Dinberg » Sun Mar 08, 2009 10:51 am

Aye keep up the good work :D
The Marvelous Contraption begins to stir...
User avatar
Dinberg
Inactive Staff Member
 
Posts: 4695
Joined: Sat Mar 10, 2007 9:47 am
Yahoo Messenger: dinberg_darktouch
Location: Jordheim


Return to “%s” DOL Code Contributions

Who is online

Users browsing this forum: No registered users and 1 guest