Skip to content

Commit

Permalink
Inventory (#27)
Browse files Browse the repository at this point in the history
* Add player and monster health regeneration.
Tweaked monster health and attack values to make early game easier.

* When the game world executes a command it now also passes the command result into the radio comms class, which can probe the result and create a radio comms message.
A radio comms message is now shown when picking up ship parts.

* Replace inventory item paragraphs with panels in preparation for showing hover and showing more info/graphics in the item description.

* Inventory items highlight when hovering mouse over them

* Improve inventory UI - description is now visible when hovering, able to select with keys, able to equip, unequip and remove when an item has context, able to use mouse on inventory items.
Currently has a bug where game view processes a click event when clicking an inventory item. Could fix by using our own mouse input provider to GeonBit.UI or can be worked around with changes to the input handlers library. Changes to the input handlers library would be useful so going with that for now.

* Inventory item description can be hidden by pressing escape.
Several bug fixes.

* Add buttons for equip, unequip and drop

* Change highlight colour from aqua to yellow

* Add item image to inventory item list

* Do not call update or draw if game window is not in focus

* Change texture2d method to a get property

* unequip hotkey and text should be remove

* Add descriptions for items.
Trigger item description panel as dirty when a new description is set on it.

* Add more description details to items

* Fix escape key not closing video options window when in-game

* Can now use right mouse to look around in replay view.
Replay view now follows player when moving.

* Use existing action map keys for inventory commands

* Items can be applied.
Items start with a recharge delay of 0.

* Add tests for healing bots and shield commands

* Fix bug where items are greyed out when applying even though they are apply-able.
Fix enchantment level showing -0 instead of +0 and showing wrong colour.
Fix save game after a game has been loaded where monsters are dead.

* Damage now applies against shields before health.
Save data for attack commands can now theoretically work after a load.

* Shield is now displayed as a blended progress bar on top of the health bar

* Shield recharge delays are now shown in the inventory text and description

* working on recharging items

* Items recharge over time.
Message is logged when an item has recharged.

* Fix issue where if player won or lost they could still move around with the mouse.
Change item descriptions so it only shows the status in the inventory and inventory long description.
Add a startup flag to suppress modal popups for radio comms.
Fix issue where mouse moving to the ship would constantly repeat the move.
Console command for spawning items can now spawn direct to player's inventory.

* Show whether item is equipped in inventory screen

* Fix monster panel drawing panels in the wrong position on the first frame

* Remove spawning of objects into inventory used for debugging.

---------

Co-authored-by: David Fidge <[email protected]>
  • Loading branch information
DavidFidge and David Fidge authored Mar 17, 2023
1 parent 714300f commit e108d28
Show file tree
Hide file tree
Showing 107 changed files with 3,075 additions and 848 deletions.
44 changes: 44 additions & 0 deletions MarsUndiscovered.Tests/Commands/ApplyHealingBotsCommandTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using FrigidRogue.MonoGame.Core.Components;

using MarsUndiscovered.Components;
using MarsUndiscovered.Components.Factories;
using MarsUndiscovered.Tests.Components;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using SadRogue.Primitives;

namespace MarsUndiscovered.Tests.Commands
{
[TestClass]
public class ApplyHealingBotsCommandTests : BaseGameWorldIntegrationTests
{
[TestMethod]
public void ApplyHealingBotsCommand_Should_Heal_Player_And_Increase_Max_Health()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();
_gameWorld.Player.Position = new Point(0, 0);
_gameWorld.SpawnItem(new SpawnItemParams().WithItemType(ItemType.HealingBots).InInventory(_gameWorld.Inventory));
var item = _gameWorld.Items.First().Value;
_gameWorld.Player.Health = 1;
var currentMaxHealth = _gameWorld.Player.MaxHealth;

var commandFactory = Container.Resolve<ICommandFactory>();

var applyHealingBotsCommand = commandFactory.CreateApplyHealingBotsCommand(_gameWorld);
applyHealingBotsCommand.Initialise(item, _gameWorld.Player);

// Act
var result = applyHealingBotsCommand.Execute();

// Assert
Assert.AreEqual(CommandResultEnum.Success, result.Result);

Assert.AreEqual(currentMaxHealth + item.MaxHealthIncrease, _gameWorld.Player.MaxHealth);
Assert.AreEqual(_gameWorld.Player.MaxHealth, _gameWorld.Player.Health);
Assert.AreEqual(100, item.HealPercentOfMax);
Assert.AreEqual("You feel healthier. All your ailments are cured and your max health has increased.", result.Messages[0]);
}
}
}
176 changes: 176 additions & 0 deletions MarsUndiscovered.Tests/Commands/ApplyItemCommandTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using FrigidRogue.MonoGame.Core.Components;
using FrigidRogue.MonoGame.Core.Extensions;
using MarsUndiscovered.Commands;
using MarsUndiscovered.Components;
using MarsUndiscovered.Components.Factories;
using MarsUndiscovered.Tests.Components;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using SadRogue.Primitives;

namespace MarsUndiscovered.Tests.Commands
{
[TestClass]
public class ApplyItemCommandTests : BaseGameWorldIntegrationTests
{
[TestMethod]
public void ApplyItemCommand_Should_Not_Apply_Other_Types_Of_Items()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();
_gameWorld.Player.Position = new Point(0, 0);

var item = SpawnItemAndAddToInventory(ItemType.MagnesiumPipe);

var commandFactory = Container.Resolve<ICommandFactory>();

var applyItemCommand = commandFactory.CreateApplyItemCommand(_gameWorld);
applyItemCommand.Initialise(_gameWorld.Player, item);

// Act
var result = applyItemCommand.Execute();

// Assert
Assert.AreEqual(CommandResultEnum.NoMove, result.Result);
Assert.AreEqual("A Magnesium Pipe cannot be applied. Did you mean to equip it?", result.Messages[0]);
}

[TestMethod]
public void ApplyItemCommand_Should_Apply_Gadget_And_Put_Gadget_On_Cooldown()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();
_gameWorld.Player.Position = new Point(0, 0);

var item = SpawnItemAndAddToInventory(ItemType.ShieldGenerator);
_gameWorld.Inventory.ItemTypeDiscoveries[ItemType.ShieldGenerator].IsItemTypeDiscovered = true;

var commandFactory = Container.Resolve<ICommandFactory>();

var applyItemCommand = commandFactory.CreateApplyItemCommand(_gameWorld);
applyItemCommand.Initialise(_gameWorld.Player, item);

// Act
var result = applyItemCommand.Execute();

// Assert
Assert.AreEqual(CommandResultEnum.Success, result.Result);
Assert.IsNotNull(_gameWorld.Inventory.Items.SingleOrDefault(i => i.Equals(item)));
Assert.IsTrue(item.CurrentRechargeDelay > 0);
Assert.AreEqual("You apply a Shield Generator Gadget", result.Messages[0]);

var subsequentCommand = result.SubsequentCommands[0] as ApplyShieldCommand;
Assert.IsNotNull(subsequentCommand);
}

[TestMethod]
public void ApplyItemCommand_Should_Identify_Gadget_If_Used_When_Unidentified()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();
_gameWorld.Player.Position = new Point(0, 0);

var item = SpawnItemAndAddToInventory(ItemType.ShieldGenerator);

var commandFactory = Container.Resolve<ICommandFactory>();

var applyItemCommand = commandFactory.CreateApplyItemCommand(_gameWorld);
applyItemCommand.Initialise(_gameWorld.Player, item);

// Act
var result = applyItemCommand.Execute();

// Assert
Assert.AreEqual(CommandResultEnum.Success, result.Result);
Assert.IsNotNull(_gameWorld.Inventory.Items.SingleOrDefault(i => i.Equals(item)));
Assert.IsTrue(item.CurrentRechargeDelay > 0);
Assert.AreEqual($"The {_gameWorld.Inventory.ItemTypeDiscoveries.GetUndiscoveredDescription(item)} is a Shield Generator Gadget!", result.Messages[0]);

var subsequentCommand = result.SubsequentCommands[0] as ApplyShieldCommand;
Assert.IsNotNull(subsequentCommand);
}

[TestMethod]
public void ApplyItemCommand_Should_Not_Apply_Gadget_If_It_Is_Still_On_Cooldown()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();
_gameWorld.Player.Position = new Point(0, 0);

var item = SpawnItemAndAddToInventory(ItemType.ShieldGenerator);
item.CurrentRechargeDelay = 1;
_gameWorld.Inventory.ItemTypeDiscoveries[ItemType.ShieldGenerator].IsItemTypeDiscovered = true;

var commandFactory = Container.Resolve<ICommandFactory>();

var applyItemCommand = commandFactory.CreateApplyItemCommand(_gameWorld);
applyItemCommand.Initialise(_gameWorld.Player, item);

// Act
var result = applyItemCommand.Execute();

// Assert
Assert.AreEqual(CommandResultEnum.NoMove, result.Result);
Assert.IsNotNull(_gameWorld.Inventory.Items.SingleOrDefault(i => i.Equals(item)));
Assert.AreEqual(1, item.CurrentRechargeDelay);
Assert.AreEqual("Cannot apply the Shield Generator Gadget (99% charged). It is still recharging.", result.Messages[0]);
}

[TestMethod]
public void ApplyItemCommand_Should_Apply_NanoFlask_And_Consume_NanoFlask()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();
_gameWorld.Player.Position = new Point(0, 0);

var item = SpawnItemAndAddToInventory(ItemType.HealingBots);
_gameWorld.Inventory.ItemTypeDiscoveries[ItemType.HealingBots].IsItemTypeDiscovered = true;

var commandFactory = Container.Resolve<ICommandFactory>();

var applyItemCommand = commandFactory.CreateApplyItemCommand(_gameWorld);
applyItemCommand.Initialise(_gameWorld.Player, item);

// Act
var result = applyItemCommand.Execute();

// Assert
Assert.AreEqual(CommandResultEnum.Success, result.Result);
Assert.IsNull(_gameWorld.Inventory.Items.SingleOrDefault(i => i.Equals(item)));
Assert.IsTrue(_gameWorld.CurrentMap.GetObjectsAt<Item>(_gameWorld.Player.Position).IsEmpty());
Assert.AreEqual("You apply a NanoFlask of Healing Bots", result.Messages[0]);

var subsequentCommand = result.SubsequentCommands[0] as ApplyHealingBotsCommand;
Assert.IsNotNull(subsequentCommand);
}

[TestMethod]
public void ApplyItemCommand_Should_Identify_NanoFlask_If_Consumed_When_Unidentified()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();
_gameWorld.Player.Position = new Point(0, 0);

var item = SpawnItemAndAddToInventory(ItemType.HealingBots);

var commandFactory = Container.Resolve<ICommandFactory>();

var applyItemCommand = commandFactory.CreateApplyItemCommand(_gameWorld);
applyItemCommand.Initialise(_gameWorld.Player, item);

// Act
var result = applyItemCommand.Execute();

// Assert
Assert.AreEqual(CommandResultEnum.Success, result.Result);
Assert.IsNull(_gameWorld.Inventory.Items.SingleOrDefault(i => i.Equals(item)));
Assert.IsTrue(_gameWorld.CurrentMap.GetObjectsAt<Item>(_gameWorld.Player.Position).IsEmpty());
Assert.IsTrue(_gameWorld.Inventory.ItemTypeDiscoveries[ItemType.HealingBots].IsItemTypeDiscovered);
Assert.AreEqual($"The {_gameWorld.Inventory.ItemTypeDiscoveries.GetUndiscoveredDescription(item)} is a NanoFlask of Healing Bots!", result.Messages[0]);

var subsequentCommand = result.SubsequentCommands[0] as ApplyHealingBotsCommand;
Assert.IsNotNull(subsequentCommand);
}
}
}
42 changes: 42 additions & 0 deletions MarsUndiscovered.Tests/Commands/ApplyShieldCommandTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using FrigidRogue.MonoGame.Core.Components;

using MarsUndiscovered.Components;
using MarsUndiscovered.Components.Factories;
using MarsUndiscovered.Tests.Components;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using SadRogue.Primitives;

namespace MarsUndiscovered.Tests.Commands
{
[TestClass]
public class ApplyShieldCommandTests : BaseGameWorldIntegrationTests
{
[TestMethod]
public void ApplyShieldCommand_Should_Add_A_Shield_To_Player_Overwriting_Existing_Shield()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();
_gameWorld.Player.Position = new Point(0, 0);
_gameWorld.SpawnItem(new SpawnItemParams().WithItemType(ItemType.ShieldGenerator).InInventory(_gameWorld.Inventory));
var item = _gameWorld.Items.First().Value;

_gameWorld.Player.Shield = 1;

var commandFactory = Container.Resolve<ICommandFactory>();

var applyShieldCommand = commandFactory.CreateApplyShieldCommand(_gameWorld);
applyShieldCommand.Initialise(item, _gameWorld.Player);

// Act
var result = applyShieldCommand.Execute();

// Assert
Assert.AreEqual(CommandResultEnum.Success, result.Result);

Assert.AreEqual((item.DamageShieldPercentage * _gameWorld.Player.MaxHealth) / 100, _gameWorld.Player.Shield);
Assert.AreEqual("A soft glow and rhythmic hum surrounds you", result.Messages[0]);
}
}
}
70 changes: 69 additions & 1 deletion MarsUndiscovered.Tests/Commands/AttackCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,75 @@ public void AttackCommand_Should_Deduct_Health_Of_Target()
Assert.AreEqual("You hit the roach", attackCommand.CommandResult.Messages[0]);
Assert.AreSame(_gameWorld.Player, attackCommand.Source);
Assert.AreSame(monster, attackCommand.Target);
}
}

[TestMethod]
public void AttackCommand_Should_Deduct_Health_After_Shield()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();

_gameWorld.Player.Position = new Point(0, 0);
_gameWorld.SpawnMonster(new SpawnMonsterParams().WithBreed("Roach").AtPosition(new Point(0, 1)));
var monster = _gameWorld.Monsters.Values.First();
_gameWorld.Player.MeleeAttack.DamageRange = new Range<int>(5, 5);
var healthBefore = monster.Health;
monster.Shield = 1;

// Act
_gameWorld.MoveRequest(Direction.Down);

// Assert
var walkCommand = _gameWorld.HistoricalCommands.WalkCommands[0];

Assert.AreEqual(CommandResultEnum.Success, walkCommand.CommandResult.Result);
Assert.AreEqual(1, _gameWorld.HistoricalCommands.WalkCommands[0].CommandResult.SubsequentCommands.Count);

var attackCommand =
_gameWorld.HistoricalCommands.WalkCommands[0].CommandResult.SubsequentCommands.First() as MeleeAttackCommand;
Assert.IsNotNull(attackCommand);
Assert.AreEqual(CommandResultEnum.Success, attackCommand.CommandResult.Result);

Assert.AreEqual(healthBefore - 4, monster.Health);
Assert.AreEqual(0, monster.Shield);
Assert.AreEqual("You hit the roach", attackCommand.CommandResult.Messages[0]);
Assert.AreSame(_gameWorld.Player, attackCommand.Source);
Assert.AreSame(monster, attackCommand.Target);
}

[TestMethod]
public void AttackCommand_Should_Deduct_Shield_Before_Health()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();

_gameWorld.Player.Position = new Point(0, 0);
_gameWorld.SpawnMonster(new SpawnMonsterParams().WithBreed("Roach").AtPosition(new Point(0, 1)));
var monster = _gameWorld.Monsters.Values.First();
_gameWorld.Player.MeleeAttack.DamageRange = new Range<int>(5, 5);
var healthBefore = monster.Health;
monster.Shield = 6;

// Act
_gameWorld.MoveRequest(Direction.Down);

// Assert
var walkCommand = _gameWorld.HistoricalCommands.WalkCommands[0];

Assert.AreEqual(CommandResultEnum.Success, walkCommand.CommandResult.Result);
Assert.AreEqual(1, _gameWorld.HistoricalCommands.WalkCommands[0].CommandResult.SubsequentCommands.Count);

var attackCommand =
_gameWorld.HistoricalCommands.WalkCommands[0].CommandResult.SubsequentCommands.First() as MeleeAttackCommand;
Assert.IsNotNull(attackCommand);
Assert.AreEqual(CommandResultEnum.Success, attackCommand.CommandResult.Result);

Assert.AreEqual(healthBefore, monster.Health);
Assert.AreEqual(1, monster.Shield);
Assert.AreEqual("You hit the roach", attackCommand.CommandResult.Messages[0]);
Assert.AreSame(_gameWorld.Player, attackCommand.Source);
Assert.AreSame(monster, attackCommand.Target);
}

[TestMethod]
public void AttackCommand_Should_Kill_Monster_If_Health_Drops_Below_Zero()
Expand Down
32 changes: 30 additions & 2 deletions MarsUndiscovered.Tests/Commands/WalkCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public void Should_Walk_To_Position()
}

[TestMethod]
public void Should_Walk_To_Position_And_Stop_At_Wall()
public void Should_Walk_To_Position_And_Stop_At_Wall_With_NoMove()
{
// Arrange
NewGameWithCustomMapNoMonstersNoItems();
Expand All @@ -252,7 +252,9 @@ public void Should_Walk_To_Position_And_Stop_At_Wall()
Assert.IsTrue(result1[0].Command is WalkCommand);
Assert.IsTrue(result1[1].Command is MoveCommand);

Assert.AreEqual(0, result2.Count);
Assert.AreEqual(1, result2.Count);
Assert.IsTrue(result2[0].Command is WalkCommand);
Assert.IsTrue(((WalkCommand)result2[0].Command).CommandResult.Result == CommandResultEnum.NoMove);
}

[TestMethod]
Expand Down Expand Up @@ -371,6 +373,32 @@ public void WalkCommand_Into_Ship_With_Repair_Parts_Should_Set_Victory_Flag()
Assert.AreEqual("You board your ship, make hasty repairs to critical parts and fire the engines! You have escaped!", walkCommand.CommandResult.Messages.First());
Assert.AreEqual(0, _gameWorld.HistoricalCommands.WalkCommands[0].CommandResult.SubsequentCommands.Count);
}

[TestMethod]
public void WalkCommand_Into_Ship_With_Repair_Parts_Using_Path_Should_Set_Victory_Flag()
{
// Arrange
_gameWorld.NewGame();

var shipRepairParts = _gameWorld.Items.Values.First(i => i.ItemType is ShipRepairParts);

_gameWorld.Inventory.Add(shipRepairParts);

// Act - player always starts directly underneath ship, moving up will enter the ship
var path = _gameWorld.GetPathToPlayer(new Point(_gameWorld.Player.Position.X, _gameWorld.Player.Position.Y - 1));
var result = _gameWorld.MoveRequest(path);

// Assert
Assert.IsTrue(_gameWorld.Player.IsVictorious);
Assert.AreEqual(1, _gameWorld.HistoricalCommands.Count());
Assert.AreEqual(1, _gameWorld.HistoricalCommands.WalkCommands.Count);

var walkCommand = _gameWorld.HistoricalCommands.WalkCommands[0];

Assert.AreEqual(CommandResultEnum.NoMove, walkCommand.CommandResult.Result);
Assert.AreEqual("You board your ship, make hasty repairs to critical parts and fire the engines! You have escaped!", walkCommand.CommandResult.Messages.First());
Assert.AreEqual(0, _gameWorld.HistoricalCommands.WalkCommands[0].CommandResult.SubsequentCommands.Count);
}

[TestMethod]
public void WalkCommand_Into_Ship_Without_Repair_Parts_Should_Not_Set_Victory_Flag()
Expand Down
Loading

0 comments on commit e108d28

Please sign in to comment.