Пример #1
0
        private Monster AssignMonsterStats(Monster monster, CombatContext combatContext)
        {
            monster.BaseStats = _statsGenerator.GenerateStats(monster.Class);

            monster.SetActionSelector(_actionSelector);
            monster.SetCombatContext(combatContext);

            monster.CurrentHitPoints = monster.MaxHitPoints;
            monster.CurrentEnergy = monster.MaxEnergy;

            var bossMultiplier = monster.IsBoss ? 4 : 1;

            monster.Gold = monster.Level * _dice.Roll(3 * bossMultiplier, 6);
            monster.Experience = (monster.Level * _dice.Roll(2 * bossMultiplier, 6));

            if (monster.IsBoss)
            {
                monster.Weapon = weaponFactory.CreateWeapon(monster.Level + 1, true);
            }
            else
            {
                if (_dice.Random(1, 6) == 1)
                {  // 1/6 monsters will have a weapon
                    monster.Weapon = weaponFactory.CreateWeapon(monster.Level, false);
                }
            }

            monster = Itemize(monster);

            return monster;
        }
Пример #2
0
        public ICombatAction SelectAction(CombatContext combatContext, IEnumerable<ICombatAction> allowedActions)
        {
            // Get a list of all possible actions that the monster can make
            var allowedActionsList = allowedActions.ToList();

            // Calculate the weighted results based on past performance (or expected performance if action not yet exectued in the current combat)
            // This will return a Dictionary of Action Hashcode (int) to Weight (int)
            var evaluation = GetWeights(combatContext, allowedActionsList);

            // Sum the weights, and generate a number between 1 and the sum of the weights.
            // Example: If we have two actions weighted 20 and 80, we will generate a number between 1 and 100 (20+80)
            var sumOfWeights = evaluation.Sum(x => x.Value);
            var randomNumberInWeightedRange = dice.Random(1, sumOfWeights);

            // Use the randomly selected weight to choose the correct action.
            // Example: If we have two actions weighted 20 and 80, a random number of 1-20 will select the first option, and 21-100 will select the second option.
            int runningTotal = 0;
            foreach (var possibleAction in evaluation)
            {
                if (possibleAction.Value + runningTotal >= randomNumberInWeightedRange)
                {
                    return allowedActionsList.Single(x => x.CreateHash() == possibleAction.Key);
                }
                else
                {
                    runningTotal += possibleAction.Value;
                }
            }

            throw new InvalidOperationException();
        }
Пример #3
0
        public Dictionary<int, int> GetWeights(CombatContext combatContext, IEnumerable<ICombatAction> allowedActions)
        {
            // Examine the combat log and create a lookup of ICombatAction (using it's hashcode (int)) to CombatLogEntry
            var monsterActionsFromCombat = combatContext.CombatLog.Where(entry => entry.Attacker == combatContext.Monster && entry is CombatLogEntryFromAction).Cast<CombatLogEntryFromAction>();
            ILookup<int, CombatLogEntryFromAction> lookups = monsterActionsFromCombat.ToLookup(x => x.Type.CreateHash(x.Name));

            // Setup a dictionary to hold the weights of Action Hashcode (int) to Weight (int)
            var weights = new Dictionary<int, int>();

            // Iterate through each action the monster can perform right now

            bool first = true;
            foreach (var allowedAction in allowedActions)
            {
                int weight = 0;
                // If the action's hashcode exists in the lookup table, average the historical outcomes to create the weight of that action
                if (lookups[allowedAction.CreateHash()].Any())
                {
                    weight = (int)lookups[allowedAction.CreateHash()].Average(x => x.CombatEffect.Damage + x.CombatEffect.Healing);
                }
                // If it doesn't exist in the lookup table, use the expected performance as the weight
                else
                {
                    var potentialEffect = allowedAction.GetPotentialCombatOutcome(combatContext.Monster);
                    weight = potentialEffect.Damage + potentialEffect.Healing;
                }

                // The first action must have a minimum weight of 1
                weights[allowedAction.CreateHash()] = first ? Math.Max(1, weight) : weight;
                first = false;
            }

            return weights;
        }
Пример #4
0
 public Monster CreateMonster(GameWorld gameWorld, CombatContext combatContext)
 {
     bool isBoss = gameWorld.NumberOfMonstersDefeatedInCurrentDungeonLevel >= gameWorld.RequiredNumberOfMonstersInCurrentDungeonLevelBeforeBoss;
     var monster = CreateMonster(gameWorld.CurrentDungeonLevel, isBoss, combatContext);
     Debug.WriteLine("Created {0} (Level {1}) HP:{6} ATK:{2} DEF:{3}. Player ATK:{4} DEF:{5}", monster.Name, monster.Level, monster.ToHitAttack, monster.ToHitDefense, combatContext.Player.ToHitAttack, combatContext.Player.ToHitDefense, monster.MaxHitPoints);
     if (monster.Weapon != null) Debug.WriteLine("Monster is wielding a {0} ({1})", monster.Weapon.GetLeveledName(), monster.Weapon.Damage);
     return monster;
 }
Пример #5
0
 public CombatEngine(GameWorld gameWorld, IMonsterFactory monsterFactory, ICombatantSelector combantSelector, IDice dice)
 {
     _gameWorld = gameWorld;
     _combantSelector = combantSelector;
     _dice = dice;
     _combatContext = new CombatContext();
     _combatContext.Player = _gameWorld.Player;
     _combatContext.Monster = monsterFactory.CreateMonster(_gameWorld, _combatContext);
     _combatContext.Player.HasFledCombat = false;
     _combatContext.Monster.HasFledCombat = false;
 }
        public void GetWeights_NoActionsHaveTakenPlaceInCombat_DamageAndHealingHaveEqualWeights()
        {
            var dice = new Mock<IDice>();
            var selector = new MonsterActionSelector(dice.Object);
            var context = new CombatContext();
            var monster = new Monster();
            monster.SetCombatContext(context);
            monster.SetActionSelector(selector);
            context.Monster = monster;
            var actionMock1 = new Mock<ICombatAction>();
            var actionMock2 = new Mock<ICombatAction>();
            actionMock1.Setup(m => m.GetPotentialCombatOutcome(It.IsAny<Combatant>())).Returns(new CombatOutcome() { Damage = 0, Healing = 10 });
            actionMock1.SetupGet(m => m.Name).Returns("Action 1");
            actionMock2.Setup(m => m.GetPotentialCombatOutcome(It.IsAny<Combatant>())).Returns(new CombatOutcome() { Damage = 10, Healing = 0 });
            actionMock2.SetupGet(m => m.Name).Returns("Action 2");

            var weights = selector.GetWeights(context, new[] { actionMock1.Object, actionMock2.Object });

            Assert.AreEqual(weights[actionMock2.Object.CreateHash()], weights[actionMock1.Object.CreateHash()]);
        }
Пример #7
0
        public Monster CreateMonster(int dungeonLevel, bool boss, CombatContext combatContext)
        {
            Monster newMonster;
            if (boss)
            {
                int bossLevel = dungeonLevel.GetBossLevel();
                newMonster = CreateArchetypedMonster(bossLevel, bossLevel);
                newMonster.Name = newMonster.Name.ToUpper();
                newMonster.Class = MonsterClass.Boss;
            }
            else
            {
                int minLevel = Convert.ToInt32((dungeonLevel * 1.2) / 2);
                int maxLevel = dungeonLevel;

                newMonster  = CreateArchetypedMonster(minLevel, maxLevel);
                newMonster.Class = MonsterClass.Monster;
            }

            return AssignMonsterStats(newMonster, combatContext);
        }
        public void SelectAction_DiceRollRequestedIsTheTotalOfAllWeights()
        {
            var dice = new Mock<IDice>();
            int minCalled = -1;
            int maxCalled = -1;
            dice.Setup(x => x.Random(It.IsAny<int>(), It.IsAny<int>())).Returns((int min, int max) =>
                {
                    minCalled = min;
                    maxCalled = max;
                    return min;
                });
            var selector = new MonsterActionSelector(dice.Object);
            var context = new CombatContext();
            var monster = new Monster();
            monster.SetCombatContext(context);
            monster.SetActionSelector(selector);
            context.Monster = monster;
            var actionMock1 = new Mock<ICombatAction>();
            var actionMock2 = new Mock<ICombatAction>();
            actionMock1.Setup(m => m.GetPotentialCombatOutcome(It.IsAny<Combatant>())).Returns(new CombatOutcome() { Damage = 10, Healing = 20 });
            actionMock1.SetupGet(m => m.Name).Returns("Action 1");
            actionMock2.Setup(m => m.GetPotentialCombatOutcome(It.IsAny<Combatant>())).Returns(new CombatOutcome() { Damage = 60, Healing = 0 });
            actionMock2.SetupGet(m => m.Name).Returns("Action 2");

            var weights = selector.SelectAction(context, new[] { actionMock1.Object, actionMock2.Object });

            Assert.AreEqual(1, minCalled);
            Assert.AreEqual(90, maxCalled);
        }
        public void SelectAction_NoActionsHaveTakenPlaceInCombat_DiceRollCorrespondsToSelection()
        {
            // Arrange
            var actionMock1 = new Mock<ICombatAction>();
            var actionMock2 = new Mock<ICombatAction>();
            var actionMock3 = new Mock<ICombatAction>();
            actionMock1.Setup(m => m.GetPotentialCombatOutcome(It.IsAny<Combatant>())).Returns(new CombatOutcome() { Damage = 3, Healing = 0 });
            actionMock1.SetupGet(m => m.Name).Returns("Action 1");
            actionMock2.Setup(m => m.GetPotentialCombatOutcome(It.IsAny<Combatant>())).Returns(new CombatOutcome() { Damage = 5, Healing = 0 });
            actionMock2.SetupGet(m => m.Name).Returns("Action 2");
            actionMock3.Setup(m => m.GetPotentialCombatOutcome(It.IsAny<Combatant>())).Returns(new CombatOutcome() { Damage = 2, Healing = 0 });
            actionMock3.SetupGet(m => m.Name).Returns("Action 3");
            var diceRollToAction = new Dictionary<int, string>();

            for (int i = 0; i < 10; i++)
            {
                // Arrange
                var dice = new Mock<IDice>();
                dice.Setup(x => x.Random(It.IsAny<int>(), It.IsAny<int>())).Returns((int min, int max) => min + i);
                var selector = new MonsterActionSelector(dice.Object);
                var context = new CombatContext();
                var monster = new Monster();
                monster.SetCombatContext(context);
                monster.SetActionSelector(selector);
                context.Monster = monster;

                // Act
                diceRollToAction.Add(i, selector.SelectAction(context, new[] { actionMock1.Object, actionMock2.Object, actionMock3.Object }).Name);
            }

            // Assert
            Assert.AreEqual(diceRollToAction[0], "Action 1");
            Assert.AreEqual(diceRollToAction[1], "Action 1");
            Assert.AreEqual(diceRollToAction[2], "Action 1");
            Assert.AreEqual(diceRollToAction[3], "Action 2");
            Assert.AreEqual(diceRollToAction[4], "Action 2");
            Assert.AreEqual(diceRollToAction[5], "Action 2");
            Assert.AreEqual(diceRollToAction[6], "Action 2");
            Assert.AreEqual(diceRollToAction[7], "Action 2");
            Assert.AreEqual(diceRollToAction[8], "Action 3");
            Assert.AreEqual(diceRollToAction[9], "Action 3");
        }
Пример #10
0
 public void SetCombatContext(CombatContext combatContext)
 {
     _combatContext = combatContext;
 }