public override void CalcActualDmgToBeTaken(ICombatList attackers, ICombatList defenders, IBattleRandom random, decimal baseDmg, int attackIndex, out decimal actualDmg) { // Miss chance actualDmg = BattleFormulas.GetDmgWithMissChance(attackers.UpkeepExcludingWaitingToJoinBattle, defenders.UpkeepExcludingWaitingToJoinBattle, baseDmg, random); // Splash dmg reduction actualDmg = BattleFormulas.SplashReduction(this, actualDmg, attackIndex); // if hp is less than 20% of the original total HP(entire group), lastStand kicks in. if (Hp < (Hp + DmgRecv) / 5m) { var lastStandEffects = TroopStub.City.Technologies.GetEffects(EffectCode.LastStand); var percent = lastStandEffects.Where( tech => BattleFormulas.UnitStatModCheck(Stats.Base, TroopBattleGroup.Attack, (string)tech.Value[1])) .DefaultIfEmpty() .Max(x => x == null ? 0 : (int)x.Value[0]); actualDmg = actualDmg * (100 - percent) / 100m; } }
public void TestFewAttackersSingleDefender(Formula formula) { var defender1 = Substitute.For <ICombatGroup>(); defender1.GetEnumerator().Returns(new List <ICombatObject> { Substitute.For <ICombatObject>() }.GetEnumerator()); ICombatList defenders = Substitute.For <ICombatList>(); defenders.GetEnumerator().Returns(new List <ICombatGroup> { defender1 }.GetEnumerator()); var attacker1 = Substitute.For <ICombatGroup>(); attacker1.GetEnumerator().Returns(new List <ICombatObject> { Substitute.For <ICombatObject>() }.GetEnumerator()); var attacker2 = Substitute.For <ICombatGroup>(); attacker2.GetEnumerator().Returns(new List <ICombatObject> { Substitute.For <ICombatObject>(), Substitute.For <ICombatObject>() }.GetEnumerator()); ICombatList attackers = Substitute.For <ICombatList>(); attackers.GetEnumerator().Returns(new List <ICombatGroup> { attacker1, attacker2 }.GetEnumerator()); formula.GetBattleInterval(defenders, attackers).Should().BeInRange(19.22, 19.24); }
private void MainBattleOnAboutToExitBattle(IBattleManager battle, ICombatList attackers, ICombatList defenders) { IStronghold stronghold; if (!gameObjectLocator.TryGetObjects(strongholdId, out stronghold)) { throw new Exception("Stronghold not found"); } var defensiveMeter = battle.GetProperty <decimal>("defense_stronghold_meter"); // Transfer stronghold if // - defensive meter is 0 // - occupied state and there is no one left defending it // - neutral state and the attacker killed the main group var hasDefendingUnitsLeft = stronghold.Troops.StationedHere().Any(p => p.TotalCount > 0); if (defensiveMeter <= 0 || (stronghold.StrongholdState == StrongholdState.Occupied && !hasDefendingUnitsLeft) || (stronghold.StrongholdState == StrongholdState.Neutral && npcGroupKilled)) { strongholdManager.TransferTo(stronghold, stronghold.GateOpenTo); } else { strongholdManager.TribeFailedToTakeStronghold(stronghold, stronghold.GateOpenTo); } }
private void BattleEnterRound(IBattleManager battle, ICombatList atk, ICombatList def, uint round) { var packet = CreatePacket(battle, Command.BattleNewRound); packet.AddUInt32(round); channel.Post(channelName, packet); }
public virtual double GetBattleInterval(ICombatList defenders, ICombatList attackers) { var count = defenders.SelectMany(o => o).Count() + attackers.SelectMany(o => o).Count(); // at 400 objects, the reduction is cap'ed at 20% of the original speed. var ret = Config.battle_turn_interval * 100 / (100 + Math.Min(500, count)); return(Config.server_production ? Math.Max(4, ret) : ret); }
internal static void AddToPacket(ICombatList combatList, Packet packet) { packet.AddInt32(combatList.Count); foreach (var group in combatList) { AddToPacket(group, packet); } }
private void BattleExitTurn(IBattleManager battle, ICombatList attackers, ICombatList defenders, uint turn) { // Remove troop from battle if he is out of stamina, we need to check here because he might have lost // some stamina after knocking down a building if (Stamina == 0) { battle.Remove(CombatGroup, BattleManager.BattleSide.Attack, ReportState.OutOfStamina); } }
public override void CalcActualDmgToBeTaken(ICombatList attackers, ICombatList defenders, IBattleRandom random, decimal baseDmg, int attackIndex, out decimal actualDmg) { actualDmg = baseDmg / 10; }
private void BattleExitBattle(IBattleManager battle, ICombatList atk, ICombatList def) { var packet = CreatePacket(battle, Command.BattleEnded); channel.Post(channelName, packet); // Unsubscribe everyone from this channel channel.Unsubscribe(channelName); }
public override void CalcActualDmgToBeTaken(ICombatList attackers, ICombatList defenders, IBattleRandom random, decimal baseDmg, int attackIndex, out decimal actualDmg) { // Miss chance actualDmg = BattleFormulas.GetDmgWithMissChance(attackers.UpkeepExcludingWaitingToJoinBattle, defenders.UpkeepExcludingWaitingToJoinBattle, baseDmg, random); }
private void BattleEnterRound(IBattleManager battle, ICombatList attackers, ICombatList defenders, uint round) { // Reduce stamina and check if we need to remove this stub Stamina -= 1; if (Stamina == 0) { battle.Remove(CombatGroup, BattleManager.BattleSide.Attack, ReportState.OutOfStamina); } }
private void MainBattleOnEnterBattle(IBattleManager battle, ICombatList attackers, ICombatList defenders) { IStronghold stronghold; if (!gameObjectLocator.TryGetObjects(strongholdId, out stronghold)) { throw new Exception("Stronghold not found"); } battle.BattleReport.AddAccess(new BattleOwner(BattleOwnerType.Tribe, stronghold.GateOpenTo.Id), BattleManager.BattleSide.Attack); }
private void BattleManagerOnExitTurn(IBattleManager battle, ICombatList attackers, ICombatList defenders, uint turn) { var properties = battle.ListProperties(); if (properties.Count == 0) { return; } var packet = CreatePacket(battle, Command.BattlePropertyUpdate); PacketHelper.AddBattleProperties(battle.ListProperties(), packet); channel.Post(channelName, packet); }
public void GetNumberOfHits_WhenDefenderUpkeepUnder200(ICombatObject attacker, ICombatList defenderCombatList) { var objectTypeFactory = Substitute.For <IObjectTypeFactory>(); var fixture = FixtureHelper.Create(); fixture.Register(() => objectTypeFactory); var battleFormulas = fixture.Create <BattleFormulas>(); objectTypeFactory.IsObjectType(string.Empty, 0).ReturnsForAnyArgs(true); attacker.Stats.Splash.Returns((byte)2); defenderCombatList.UpkeepExcludingWaitingToJoinBattle.Returns(199); battleFormulas.GetNumberOfHits(attacker, defenderCombatList).Should().Be(2); }
private void BmExitBattle2(IBattleManager battle, ICombatList atk, ICombatList def) { if (atk.Count > 0) { if (sw != null) { WriteResult(atk[0][0]); WriteResultEnd(deadObject.Count); } } else { WriteResultEnd(def[0].Count); } }
public override void CalcActualDmgToBeTaken(ICombatList attackers, ICombatList defenders, IBattleRandom random, decimal baseDmg, int attackIndex, out decimal actualDmg) { // Miss chance actualDmg = BattleFormulas.GetDmgWithMissChance(attackers.UpkeepExcludingWaitingToJoinBattle, defenders.UpkeepExcludingWaitingToJoinBattle, baseDmg, random); // Splash dmg reduction actualDmg = BattleFormulas.SplashReduction(this, actualDmg, attackIndex); // AP Bonuses if (City.AlignmentPoint >= 90m) { actualDmg *= .1m; } }
private void BattleExitBattle(IBattleManager battle, ICombatList atk, ICombatList def) { ICity city; ITroopObject troopObject; if (!gameObjectLocator.TryGetObjects(cityId, troopObjectId, out city, out troopObject)) { throw new Exception(); } DeregisterBattleListeners(city); troopObject.BeginUpdate(); troopObject.State = GameObjectStateFactory.NormalState(); troopObject.EndUpdate(); troopObject.Stub.BeginUpdate(); troopObject.Stub.State = TroopState.Idle; troopObject.Stub.EndUpdate(); StateChange(ActionState.Completed); }
public void TestAtMaxOrOverMax(int defenderCnt, int attackerCnt, Formula formula) { var defendersCombatObjects = new List <ICombatObject>(); for (var i = 0; i < defenderCnt; i++) { defendersCombatObjects.Add(Substitute.For <ICombatObject>()); } var defender1 = Substitute.For <ICombatGroup>(); defender1.GetEnumerator().Returns(defendersCombatObjects.GetEnumerator()); ICombatList defenders = Substitute.For <ICombatList>(); defenders.GetEnumerator().Returns(new List <ICombatGroup> { defender1 }.GetEnumerator()); var attacker1 = Substitute.For <ICombatGroup>(); var attackersCombatObjects = new List <ICombatObject>(); for (var i = 0; i < attackerCnt; i++) { attackersCombatObjects.Add(Substitute.For <ICombatObject>()); } attacker1.GetEnumerator().Returns(attackersCombatObjects.GetEnumerator()); ICombatList attackers = Substitute.For <ICombatList>(); attackers.GetEnumerator().Returns(new List <ICombatGroup> { attacker1 }.GetEnumerator()); formula.GetBattleInterval(defenders, attackers).Should().Be(4); }
private void AddAlignmentPoint(ICombatList attackers, ICombatList defenders, uint numberOfRounds) { ICity city; if (!gameObjectLocator.TryGetObjects(cityId, out city)) { throw new Exception("City is missing"); } // Subtract the "In Battle" formation of the local troop since that's already // included in our defenders decimal defUpkeep = defenders.UpkeepExcludingWaitingToJoinBattle + city.Troops.Upkeep - city.DefaultTroop.UpkeepForFormation(FormationType.InBattle); decimal atkUpkeep = attackers.UpkeepExcludingWaitingToJoinBattle; if (atkUpkeep == 0 || atkUpkeep <= defUpkeep) { return; } decimal points = Math.Min(defUpkeep == 0 ? Config.ap_max_per_battle : (atkUpkeep / defUpkeep - 1), Config.ap_max_per_battle) * numberOfRounds / 20m; foreach (ITroopStub stub in attackers.Where(p => p is CityOffensiveCombatGroup) .Select(offensiveCombatGroup => ((CityOffensiveCombatGroup)offensiveCombatGroup).TroopObject.Stub)) { stub.City.BeginUpdate(); stub.City.AlignmentPoint -= stub.Upkeep / atkUpkeep * points; stub.City.EndUpdate(); } city.BeginUpdate(); city.AlignmentPoint += points; city.EndUpdate(); }
private void BattleExitTurn(IBattleManager battle, ICombatList atk, ICombatList def, uint turn) { Append("Turn[" + turn + "] Ended with atk_upkeep[" + TotalUpkeep(atk) + "] def_upkeep[" + TotalUpkeep(def) + "]"); Append("Turn[" + turn + "] Ended with atk_upkeep_active[" + atk.UpkeepNotParticipatedInRound(battle.Round) + "] def_upkeep_active[" + def.UpkeepNotParticipatedInRound(battle.Round) + "]\n"); }
public abstract void CalcActualDmgToBeTaken(ICombatList attackers, ICombatList defenders, IBattleRandom random, decimal baseDmg, int attackIndex, out decimal actualDmg);
protected virtual void AttackTarget(ICombatList offensiveCombatList, ICombatList defensiveCombatList, ICombatGroup attackerGroup, ICombatObject attacker, BattleSide sideAttacking, CombatList.Target target, int attackIndex, uint round, out decimal carryOverDmg) { var attackerCount = attacker.Count; var targetCount = target.CombatObject.Count; #region Damage decimal dmg = battleFormulas.GetAttackerDmgToDefender(attacker, target.CombatObject, round); if (target.DamageCarryOverPercentage.HasValue) { dmg *= target.DamageCarryOverPercentage.Value; } decimal actualDmg; target.CombatObject.CalcActualDmgToBeTaken(offensiveCombatList, defensiveCombatList, battleRandom, dmg, attackIndex, out actualDmg); Resource defenderDroppedLoot; int attackPoints; int initialCount = target.CombatObject.Count; carryOverDmg = 0; if (dmg > 0 && target.CombatObject.Hp - actualDmg < 0m) { carryOverDmg = (actualDmg - target.CombatObject.Hp) / actualDmg; } actualDmg = Math.Min(target.CombatObject.Hp, actualDmg); target.CombatObject.TakeDamage(actualDmg, out defenderDroppedLoot, out attackPoints); if (initialCount > target.CombatObject.Count) { UnitCountDecreased(this, sideAttacking == BattleSide.Attack ? BattleSide.Defense : BattleSide.Attack, target.Group, target.CombatObject, initialCount - target.CombatObject.Count); } attacker.DmgDealt += actualDmg; attacker.MaxDmgDealt = (ushort)Math.Max(attacker.MaxDmgDealt, actualDmg); attacker.MinDmgDealt = (ushort)Math.Min(attacker.MinDmgDealt, actualDmg); ++attacker.HitDealt; attacker.HitDealtByUnit += attacker.Count; target.CombatObject.DmgRecv += actualDmg; target.CombatObject.MaxDmgRecv = (ushort)Math.Max(target.CombatObject.MaxDmgRecv, actualDmg); target.CombatObject.MinDmgRecv = (ushort)Math.Min(target.CombatObject.MinDmgRecv, actualDmg); ++target.CombatObject.HitRecv; #endregion #region Loot and Attack Points // NOTE: In the following rewardStrategy calls we are in passing in whatever the existing implementations // of reward startegy need. If some specific implementation needs more info then add more params or change it to // take the entire BattleManager as a param. if (sideAttacking == BattleSide.Attack) { // Only give loot if we are attacking the first target in the list Resource loot; rewardStrategy.RemoveLoot(this, attackIndex, attacker, target.CombatObject, out loot); if (attackPoints > 0 || (loot != null && !loot.Empty)) { rewardStrategy.GiveAttackerRewards(attacker, attackPoints, loot ?? new Resource()); } } else { // Give defender rewards if there are any if (attackPoints > 0 || (defenderDroppedLoot != null && !defenderDroppedLoot.Empty)) { rewardStrategy.GiveDefendersRewards(attacker, attackPoints, defenderDroppedLoot ?? new Resource()); } } #endregion #region Object removal bool isDefenderDead = target.CombatObject.IsDead; ActionAttacked(this, sideAttacking, attackerGroup, attacker, target.Group, target.CombatObject, actualDmg, attackerCount, targetCount); if (isDefenderDead) { bool isGroupDead = target.Group.IsDead(); if (isGroupDead) { // Remove the entire group Remove(target.Group, sideAttacking == BattleSide.Attack ? BattleSide.Defense : BattleSide.Attack, ReportState.Dying); } else if (!target.CombatObject.Disposed) { // Only remove the single object BattleReport.WriteExitingObject(target.Group, sideAttacking != BattleSide.Attack, target.CombatObject); target.Group.Remove(target.CombatObject); } UnitKilled(this, sideAttacking == BattleSide.Attack ? BattleSide.Defense : BattleSide.Attack, target.Group, target.CombatObject); if (!target.CombatObject.Disposed) { target.CombatObject.ExitBattle(); } if (isGroupDead) { GroupKilled(this, target.Group); } } else { if (!target.CombatObject.Disposed) { dbManager.Save(target.CombatObject); } } #endregion }
public override void CalcActualDmgToBeTaken(ICombatList attackers, ICombatList defenders, IBattleRandom random, decimal baseDmg, int attackIndex, out decimal actualDmg) { throw new NotImplementedException(); }
public void BattleEnterRound(IBattleManager battle, ICombatList attackers, ICombatList defenders, uint round) { AddAlignmentPoint(attackers, defenders, 1); }
private void MainBattleOnExitTurn(IBattleManager battle, ICombatList attackers, ICombatList defenders, uint turn) { IStronghold stronghold; if (!gameObjectLocator.TryGetObjects(strongholdId, out stronghold)) { throw new Exception("Stronghold not found"); } var defensiveMeter = battle.GetProperty <decimal>("defense_stronghold_meter"); var offensiveMeter = battle.GetProperty <decimal>("offense_stronghold_meter"); // Make copy since defenders may change var defendersLoopCopy = defenders.ToList(); // Remove defenders if: // defensive meter is 0 // stronghold is still neutral (no tribe) // the defender isnt part of the tribe that owns the stronghold foreach (var defender in defendersLoopCopy) { var cityCombatGroup = defender as CityDefensiveCombatGroup; // If cityCombatGroup is null then we're dealing w/ a NPC unit if (cityCombatGroup == null) { if (defensiveMeter > 0) { continue; } battle.Remove(defender, BattleManager.BattleSide.Defense, ReportState.OutOfStamina); } // Else we're dealing w/ a player unit else { if (defensiveMeter > 0 && stronghold.Tribe != null && defender.Tribe == stronghold.Tribe) { continue; } battle.Remove(cityCombatGroup, BattleManager.BattleSide.Defense, ReportState.OutOfStamina); // Dead troops should just be removed immediately if (cityCombatGroup.TroopStub.TotalCount == 0) { stronghold.Troops.RemoveStationed(cityCombatGroup.TroopStub.StationTroopId); cityCombatGroup.TroopStub.City.Troops.Remove(cityCombatGroup.TroopStub.TroopId); continue; } // Defenders need to manually be sent back cityCombatGroup.TroopStub.BeginUpdate(); cityCombatGroup.TroopStub.State = TroopState.Stationed; cityCombatGroup.TroopStub.EndUpdate(); var troopInitializer = troopInitializerFactory.CreateStationedTroopObjectInitializer(cityCombatGroup.TroopStub); var retreatChainAction = actionFactory.CreateRetreatChainAction(cityCombatGroup.TroopStub.City.Id, troopInitializer); var result = cityCombatGroup.TroopStub.City.Worker.DoPassive(cityCombatGroup.TroopStub.City, retreatChainAction, true); if (result != Error.Ok) { throw new Exception("Unexpected failure when retreating a unit from stronghold"); } } } // Remove attackers that have quit the tribe or have low meter // Make copy since attackers will be changing var attackerLoopCopy = attackers.ToList(); foreach (var attacker in attackerLoopCopy.Where(attacker => offensiveMeter <= 0 || attacker.Tribe != stronghold.GateOpenTo)) { // Remove from battle, no need to send them back since attacking troops have actions to handle that battle.Remove(attacker, BattleManager.BattleSide.Attack, ReportState.OutOfStamina); } }
/// <summary> /// Returns the next object from the primary group that should attack. /// If primary group has no one able to attack, it will look into the secondary group instead. /// </summary> /// <returns>True if got an object from the current round. False if had to look into next round.</returns> public bool NextObject(uint round, ICombatList attacker, ICombatList defender, out ICombatObject outCombatObject, out ICombatGroup outCombatGroup, out BattleManager.BattleSide foundInGroup) { var attackerUpkeep = attacker.UpkeepNotParticipatedInRound(round); var defenderUpkeep = defender.UpkeepNotParticipatedInRound(round); if (attackerUpkeep == 0 && defenderUpkeep == 0) { attackerUpkeep = attacker.UpkeepNotParticipatedInRound(round + 1); defenderUpkeep = defender.UpkeepNotParticipatedInRound(round + 1); } BattleManager.BattleSide sideAttack = random.Next(attackerUpkeep + defenderUpkeep) < attackerUpkeep ? BattleManager.BattleSide.Attack : BattleManager.BattleSide.Defense; var offensiveCombatList = sideAttack == BattleManager.BattleSide.Attack ? attacker : defender; var defensiveCombatList = sideAttack == BattleManager.BattleSide.Attack ? defender : attacker; var offensiveSide = sideAttack; var defensiveSide = sideAttack == BattleManager.BattleSide.Attack ? BattleManager.BattleSide.Defense : BattleManager.BattleSide.Attack; // Look into offenside combat list first ICombatObject outCombatObjectAttacker; ICombatGroup outCombatGroupAttacker; if (NextObjectFromList(round, offensiveCombatList, out outCombatObjectAttacker, out outCombatGroupAttacker)) { foundInGroup = offensiveSide; outCombatGroup = outCombatGroupAttacker; outCombatObject = outCombatObjectAttacker; return(true); } // Couldnt find in the attacker so look in defense ICombatObject outCombatObjectDefender; ICombatGroup outCombatGroupDefender; if (NextObjectFromList(round, defensiveCombatList, out outCombatObjectDefender, out outCombatGroupDefender)) { foundInGroup = defensiveSide; outCombatGroup = outCombatGroupDefender; outCombatObject = outCombatObjectDefender; return(true); } // Okay looks like both sides are done for this round. If we had an attacker // then we return that, otherwise go to the defender if (outCombatObjectAttacker != null) { foundInGroup = offensiveSide; outCombatGroup = outCombatGroupAttacker; outCombatObject = outCombatObjectAttacker; } else if (outCombatObjectDefender != null) { foundInGroup = defensiveSide; outCombatGroup = outCombatGroupDefender; outCombatObject = outCombatObjectDefender; } // If this happens then it means there is no one in the battle or the battle is prolly over else { outCombatGroup = null; outCombatObject = null; foundInGroup = offensiveSide; return(true); } return(false); }
private int TotalUpkeep(ICombatList combatList) { return(combatList.AllCombatObjects().Sum(p => p.Upkeep)); }
private void BattleEnterRound(IBattleManager battle, ICombatList atk, ICombatList def, uint round) { Append("Round[" + round + "] Started with atk_upkeep[" + TotalUpkeep(atk) + "] def_upkeep[" + TotalUpkeep(def) + "]\n"); }
public virtual int GetNumberOfHits(ICombatObject currentAttacker, ICombatList defenderCombatList) { int splashEvery200 = objectTypeFactory.IsObjectType("SplashEvery200", currentAttacker.Type) ? (Math.Min(defenderCombatList.UpkeepExcludingWaitingToJoinBattle, 4000) / 200) : 0; return(currentAttacker.Stats.Splash == 0 ? 1 : currentAttacker.Stats.Splash + splashEvery200); }
private void BattleEnterBattle(IBattleManager battle, ICombatList atk, ICombatList def) { Append("Battle Started with atk_upkeep[" + TotalUpkeep(atk) + "] def_upkeep[" + TotalUpkeep(def) + "]\n"); }