好的,多重箭的伤害已经整好了,来说一下是怎么处理的。 首先,让我们回顾一下在实现多重箭功能时遇到的问题,以及这些问题如何导致了伤害计算不准确。问题的根源在于使用了 Mission.Current.AddCustomMissile 方法,该方法仅计算了箭的伤害,而忽略了弓的伤害。这导致在构建 AttackCollisionData 的 MissileTotalDamage 值时,传入了一个仅基于箭伤害的低估值。 对比一代,addmissile 方法考虑了射击武器的多个参数:使用的武器、武器的属性、发射的投射物、投射物的属性。而二代的实现仅考虑了发射的投射物,忽略了武器本身的贡献。关键问题在于,我在代码中寻找了很久,未找到直接的修改点来解决这个问题。因此,我花费了相当长的时间尝试用一种较为复杂的方法来正确计算伤害。 现在知道了他是怎么错的,那么我们就给他改成对的。但是呢,看了一遍游戏的源码,即使是最早的一个回调函数也已经是在他完成了伤害值的计算后才会进行调用。所以,现在我们需要干两个事:一个是构造新的参数正确的AttackCollisionData ,二是拿这个ACD去完成一遍伤害计算流程。完成这两个事后,就可以获取到正确的箭矢伤害了。 构造一个新的参数正确的AttackCollisionData:首先获取正确的MissileTotalDamage 值,这个值应该等于弓弩的伤害加上箭矢的伤害,装备词条的加值也要带上。借用一下咱们之前写的代码简单的完成一下。使用时,只需要传入agent,就能输出一个整数值,代表弓弩/投掷武器的MissileTotalDamage 。(顺便输出了一下agent手持的武器,如果不需要这个功能,直接删掉out MissionWeapon mainHandEquipmentElement这段)
- public int GetAgentCurrentMissionWeaponDamage(Agent agent,out MissionWeapon mainHandEquipmentElement)
- {
- mainHandEquipmentElement=MissionWeapon.Invalid;
- if (agent.Equipment != null && agent.Equipment[0].Item != null)
- {
-
- // 获取Agent主手中武器的Index索引
- EquipmentIndex mainHandIndex = agent.GetWieldedItemIndex(Agent.HandIndex.MainHand);
- // EquipmentIndex转MissionWeapon
- mainHandEquipmentElement = agent.Equipment[mainHandIndex];
- if (agent.Equipment[mainHandIndex].CurrentUsageItem.IsRangedWeapon && agent.Equipment[mainHandIndex].CurrentUsageItem.IsConsumable)
- {//如果是投掷武器,直接返回投掷武器的伤害
- // 从Agent的装备中获取主手的装备元素
- mainHandEquipmentElement = agent.Equipment[mainHandIndex];
- return mainHandEquipmentElement.GetModifiedMissileDamageForCurrentUsage();
- }
- else if (agent.Equipment[mainHandIndex].CurrentUsageItem.IsRangedWeapon)
- {//如果是弓弩,返回弓弩+弹药的伤害
- // 从Agent的装备中获取主手的装备元素,并且获得对应的弹药
- mainHandEquipmentElement = agent.Equipment[mainHandIndex].AmmoWeapon;
- return mainHandEquipmentElement.GetModifiedMissileDamageForCurrentUsage() + agent.Equipment[mainHandIndex].GetModifiedMissileDamageForCurrentUsage();
- }
- else
- {
- //非远程武器,返回0
- }
-
-
- }
- return 0;
- }
复制代码
获取到正确的MTD值后,借用原来MTD值错误的AttackCollisionData,来构造一个新的AttackCollisionData。 构造时,MTD值改为传入刚才函数输出的数值,其他的参数因为没有出错或者是后续操作时会被覆盖,所以都从旧的AttackCollisionData里获取。构造新ACD的方法可以用AttackCollisionData类中的GetAttackCollisionDataForDebugPurpose方法,因为本身ACD类的构造函数是私有方法,外界无法调用,但是看了下源码发现类里面留了GetAttackCollisionDataForDebugPurpose方法会去调用ACD的构造函数,所以就可以用这个方法间接的完成ACD的手动创建。 MTD值错误的AttackCollisionData可以从OnMissileHit、OnAgentHit、OnRegisterBlow等回调函数里获取,咱们的代码也可以写在这些回调函数中。
- //用自己写的脚本获取一下新的MissileTotalDamage投射物总伤害
- int MTD = WoW_Scripts.GetAgentCurrentMissionWeaponDamage(affectorAgent, out missionWeapon);
- //用输入进来的acd重新构造一个MissileTotalDamage正确的acd
- AttackCollisionData attackCD = AttackCollisionData.GetAttackCollisionDataForDebugPurpose(attackCollisionData.AttackBlockedWithShield, attackCollisionData.CorrectSideShieldBlock, attackCollisionData.IsAlternativeAttack, attackCollisionData.IsColliderAgent, attackCollisionData.CollidedWithShieldOnBack, attackCollisionData.IsMissile, attackCollisionData.MissileBlockedWithWeapon, attackCollisionData.MissileHasPhysics, attackCollisionData.EntityExists, attackCollisionData.ThrustTipHit, attackCollisionData.MissileGoneUnderWater, attackCollisionData.MissileGoneOutOfBorder, attackCollisionData.CollisionResult, attackCollisionData.AffectorWeaponSlotOrMissileIndex, attackCollisionData.StrikeType, attackCollisionData.DamageType, attackCollisionData.CollisionBoneIndex, attackCollisionData.VictimHitBodyPart, attackCollisionData.AttackBoneIndex, attackCollisionData.AttackDirection, attackCollisionData.PhysicsMaterialIndex, attackCollisionData.CollisionHitResultFlags, attackCollisionData.AttackProgress, attackCollisionData.CollisionDistanceOnWeapon, attackCollisionData.AttackerStunPeriod, attackCollisionData.DefenderStunPeriod, MTD, attackCollisionData.MissileStartingBaseSpeed, attackCollisionData.ChargeVelocity, attackCollisionData.FallSpeed, attackCollisionData.WeaponRotUp, attackCollisionData.WeaponBlowDir, attackCollisionData.CollisionGlobalPosition, attackCollisionData.MissileVelocity, attackCollisionData.MissileStartingPosition, attackCollisionData.VictimAgentCurVelocity, Vec3.Up);
复制代码
拿这个ACD去完成一遍伤害计算流程:在获取了多重箭的正确的ACD后,我们可以仿照游戏源码,拿ACD去完成伤害计算,接着用带有伤害数据的ACD去构造Blow,最后让受击的agent去应用Blow和ACD。 但这里就又有一个小问题,计算伤害的方法GetAttackCollisionResults,他是Mission类中的私有方法,老样子外界无法调用,构造Blow的方法CreateMissileBlow也是这样。好在这两个方法都是在方法内部没有继续使用私有方法的,所以我们直接把游戏源码复制出来用。如果源码里有些使用了this的地方需要改成Mission.Current。
- public CombatLogData GetAttackCollisionResults(Agent attackerAgent, Agent victimAgent, GameEntity hitObject, float momentumRemaining, in MissionWeapon attackerWeapon, bool crushedThrough, bool cancelDamage, bool crushedThroughWithoutAgentCollision, ref AttackCollisionData attackCollisionData, out WeaponComponentData shieldOnBack, out CombatLogData combatLog)
- {//核心的伤害计算代码,这里完成了伤害和减伤的计算,放在attackCollisionData里。构造blow的时候直接取值。
- AttackInformation attackInformation = new AttackInformation(attackerAgent, victimAgent, hitObject, attackCollisionData, attackerWeapon);
- shieldOnBack = attackInformation.ShieldOnBack;
- int num;
- MissionCombatMechanicsHelper.GetAttackCollisionResults(attackInformation, crushedThrough, momentumRemaining, attackerWeapon, cancelDamage, ref attackCollisionData, out combatLog, out num);
- float num2 = (float)attackCollisionData.InflictedDamage;
- if (num2 > 0f)
- {
- float num3 = MissionGameModels.Current.AgentApplyDamageModel.CalculateDamage(attackInformation, attackCollisionData, attackerWeapon, num2);
- combatLog.ModifiedDamage = TaleWorlds.Library.MathF.Round(num3 - num2);
- attackCollisionData.InflictedDamage = TaleWorlds.Library.MathF.Round(num3);
- }
- else
- {
- combatLog.ModifiedDamage = 0;
- attackCollisionData.InflictedDamage = 0;
- }
- if (!attackCollisionData.IsFallDamage && attackInformation.IsFriendlyFire)
- {
- if (!attackInformation.IsAttackerAIControlled && GameNetwork.IsSessionActive)
- {
- int num4 = (attackCollisionData.IsMissile ? MultiplayerOptions.OptionType.FriendlyFireDamageRangedSelfPercent.GetIntValue(MultiplayerOptions.MultiplayerOptionsAccessMode.CurrentMapOptions) : MultiplayerOptions.OptionType.FriendlyFireDamageMeleeSelfPercent.GetIntValue(MultiplayerOptions.MultiplayerOptionsAccessMode.CurrentMapOptions));
- attackCollisionData.SelfInflictedDamage = TaleWorlds.Library.MathF.Round((float)attackCollisionData.InflictedDamage * ((float)num4 * 0.01f));
- int num5 = (attackCollisionData.IsMissile ? MultiplayerOptions.OptionType.FriendlyFireDamageRangedFriendPercent.GetIntValue(MultiplayerOptions.MultiplayerOptionsAccessMode.CurrentMapOptions) : MultiplayerOptions.OptionType.FriendlyFireDamageMeleeFriendPercent.GetIntValue(MultiplayerOptions.MultiplayerOptionsAccessMode.CurrentMapOptions));
- attackCollisionData.InflictedDamage = TaleWorlds.Library.MathF.Round((float)attackCollisionData.InflictedDamage * ((float)num5 * 0.01f));
- combatLog.InflictedDamage = attackCollisionData.InflictedDamage;
- }
- combatLog.IsFriendlyFire = true;
- }
- if (attackCollisionData.AttackBlockedWithShield && attackCollisionData.InflictedDamage > 0 && (int)attackInformation.VictimShield.HitPoints - attackCollisionData.InflictedDamage <= 0)
- {
- attackCollisionData.IsShieldBroken = true;
- }
- if (!crushedThroughWithoutAgentCollision)
- {
- combatLog.BodyPartHit = attackCollisionData.VictimHitBodyPart;
- combatLog.IsVictimEntity = hitObject != null;
- }
- return combatLog;
- }
- public Blow CreateMissileBlow(Agent attackerAgent, in AttackCollisionData collisionData, in MissionWeapon attackerWeapon, Vec3 missilePosition, Vec3 missileStartingPosition)
- {
- Blow blow = new Blow((attackerAgent != null) ? attackerAgent.Index : (-1));
- MissionWeapon missionWeapon = attackerWeapon;
- blow.BlowFlag = (missionWeapon.CurrentUsageItem.WeaponFlags.HasAnyFlag(WeaponFlags.CanKnockDown) ? BlowFlags.KnockDown : BlowFlags.None);
- AttackCollisionData attackCollisionData = collisionData;
- blow.Direction = attackCollisionData.MissileVelocity.NormalizedCopy();
- blow.SwingDirection = blow.Direction;
- attackCollisionData = collisionData;
- blow.GlobalPosition = attackCollisionData.CollisionGlobalPosition;
- attackCollisionData = collisionData;
- blow.BoneIndex = attackCollisionData.CollisionBoneIndex;
- attackCollisionData = collisionData;
- blow.StrikeType = (StrikeType)attackCollisionData.StrikeType;
- attackCollisionData = collisionData;
- blow.DamageType = (DamageTypes)attackCollisionData.DamageType;
- attackCollisionData = collisionData;
- blow.VictimBodyPart = attackCollisionData.VictimHitBodyPart;
- sbyte b;
- if (attackerAgent == null)
- {
- b = -1;
- }
- else
- {
- Monster monster = attackerAgent.Monster;
- missionWeapon = attackerWeapon;
- b = monster.GetBoneToAttachForItemFlags(missionWeapon.Item.ItemFlags);
- }
- sbyte b2 = b;
- missionWeapon = attackerWeapon;
- ItemObject item = missionWeapon.Item;
- missionWeapon = attackerWeapon;
- WeaponComponentData currentUsageItem = missionWeapon.CurrentUsageItem;
- attackCollisionData = collisionData;
- int affectorWeaponSlotOrMissileIndex = attackCollisionData.AffectorWeaponSlotOrMissileIndex;
- sbyte weaponAttachBoneIndex = b2;
- attackCollisionData = collisionData;
- blow.WeaponRecord.FillAsMissileBlow(item, currentUsageItem, affectorWeaponSlotOrMissileIndex, weaponAttachBoneIndex, missileStartingPosition, missilePosition, attackCollisionData.MissileVelocity);
- blow.BaseMagnitude = collisionData.BaseMagnitude;
- blow.MovementSpeedDamageModifier = collisionData.MovementSpeedDamageModifier;
- blow.AbsorbedByArmor = (float)collisionData.AbsorbedByArmor;
- blow.InflictedDamage = collisionData.InflictedDamage;
- blow.SelfInflictedDamage = collisionData.SelfInflictedDamage;
- blow.DamageCalculated = true;
- return blow;
- }
复制代码
把方法复制出来后,开始重新走一遍伤害计算流程和应用伤害的流程。现在,当多重箭命中目标后,就会去造成一个正常的伤害。(因为我是在OnAgentHit里面写的代码,这个回调函数是在agent.RegisterBlow方法里被调用,所以为了避免无限递龟(乐)所以直接把RegisterBlow的代码也复制出来用了。)
- WoW_Scripts.GetAttackCollisionResults(affectorAgent, affectedAgent, null, 1f, missionWeapon, false, false, false, ref attackCD, out weaponComponentData, out combatLogData);
- Blow b = WoW_Scripts.CreateMissileBlow(affectorAgent, attackCD, missionWeapon, attackCD.CollisionGlobalPosition, attackCD.MissileStartingPosition);
- float health = affectedAgent.Health;
- float num = (((float)b.InflictedDamage > health) ? health : ((float)b.InflictedDamage));
- float num2 = health - num;
- if (num2 < 0f)
- {
- num2 = 0f;
- }
- if (affectedAgent.CurrentMortalityState != Agent.MortalityState.Immortal && !affectedAgent.Mission.DisableDying)
- {
- affectedAgent.Health = num2;
- InformationManager.DisplayMessage(new InformationMessage($"missile deal {num} damage"));
- }
- if (affectedAgent.Health < 1f)
- {
- Agent.KillInfo overrideKillInfo = (b.IsFallDamage ? Agent.KillInfo.Gravity : Agent.KillInfo.Invalid);
- affectedAgent.Die(b, overrideKillInfo);
- InformationManager.DisplayMessage(new InformationMessage("missile kill an agent"));
- }
复制代码
完整的OnAgentHit代码
- public override void OnAgentHit(Agent affectedAgent, Agent affectorAgent, in MissionWeapon affectorWeapon, in Blow blow, in AttackCollisionData attackCollisionData)
- {
- base.OnAgentHit(affectedAgent, affectorAgent, affectorWeapon, blow, attackCollisionData);
-
- //自己写点if语句,筛选出来是AddCustomMissile生成的投射物。比如attackCollisionData里的MissileTotalDamage数值和实际手上投射物总伤害对不上之类的。(如果按照这个写法,切武器后也会触发这个代码,总之自己多写点if)
- //创建几个一会会被out出来的数值避免报错
- WeaponComponentData weaponComponentData;
- CombatLogData combatLogData;
- MissionWeapon missionWeapon;
- //用自己写的脚本获取一下新的MissileTotalDamage投射物总伤害
- int MTD = WoW_Scripts.GetAgentCurrentMissionWeaponDamage(affectorAgent, out missionWeapon);
- if (MTD!= attackCollisionData.MissileTotalDamage&&MTD!=0&& attackCollisionData.IsMissile)
- {
- //用输入进来的acd重新构造一个MissileTotalDamage正确的acd
- AttackCollisionData attackCD = AttackCollisionData.GetAttackCollisionDataForDebugPurpose(attackCollisionData.AttackBlockedWithShield, attackCollisionData.CorrectSideShieldBlock, attackCollisionData.IsAlternativeAttack, attackCollisionData.IsColliderAgent, attackCollisionData.CollidedWithShieldOnBack, attackCollisionData.IsMissile, attackCollisionData.MissileBlockedWithWeapon, attackCollisionData.MissileHasPhysics, attackCollisionData.EntityExists, attackCollisionData.ThrustTipHit, attackCollisionData.MissileGoneUnderWater, attackCollisionData.MissileGoneOutOfBorder, attackCollisionData.CollisionResult, attackCollisionData.AffectorWeaponSlotOrMissileIndex, attackCollisionData.StrikeType, attackCollisionData.DamageType, attackCollisionData.CollisionBoneIndex, attackCollisionData.VictimHitBodyPart, attackCollisionData.AttackBoneIndex, attackCollisionData.AttackDirection, attackCollisionData.PhysicsMaterialIndex, attackCollisionData.CollisionHitResultFlags, attackCollisionData.AttackProgress, attackCollisionData.CollisionDistanceOnWeapon, attackCollisionData.AttackerStunPeriod, attackCollisionData.DefenderStunPeriod, MTD, attackCollisionData.MissileStartingBaseSpeed, attackCollisionData.ChargeVelocity, attackCollisionData.FallSpeed, attackCollisionData.WeaponRotUp, attackCollisionData.WeaponBlowDir, attackCollisionData.CollisionGlobalPosition, attackCollisionData.MissileVelocity, attackCollisionData.MissileStartingPosition, attackCollisionData.VictimAgentCurVelocity, Vec3.Up);
- //重新走一遍伤害计算流程和应用伤害的流程
- WoW_Scripts.GetAttackCollisionResults(affectorAgent, affectedAgent, null, 1f, missionWeapon, false, false, false, ref attackCD, out weaponComponentData, out combatLogData);
- Blow b = WoW_Scripts.CreateMissileBlow(affectorAgent, attackCD, missionWeapon, attackCD.CollisionGlobalPosition, attackCD.MissileStartingPosition);
- float health = affectedAgent.Health;
- float num = (((float)b.InflictedDamage > health) ? health : ((float)b.InflictedDamage));
- float num2 = health - num;
- if (num2 < 0f)
- {
- num2 = 0f;
- }
- if (affectedAgent.CurrentMortalityState != Agent.MortalityState.Immortal && !affectedAgent.Mission.DisableDying)
- {
- affectedAgent.Health = num2;
- InformationManager.DisplayMessage(new InformationMessage($"missile deal {num} damage"));
- }
- if (affectedAgent.Health < 1f)
- {
- Agent.KillInfo overrideKillInfo = (b.IsFallDamage ? Agent.KillInfo.Gravity : Agent.KillInfo.Invalid);
- affectedAgent.Die(b, overrideKillInfo);
- InformationManager.DisplayMessage(new InformationMessage("missile kill an agent"));
- }
-
- }
- }
复制代码
至此呢,多重箭已经算是完成了,只剩一个无法解决的问题:那个伤害值错误的箭输出的伤害信息,我们是屏蔽不掉的,很难受。只能凑合了,毕竟归根结底这个多重箭做的这么麻烦,是因为现在的AddCustomMissile他缺少参数,没办法输入弓弩的伤害,官方不改的话我们只能绕远路。 |